该项目演示了如何使用 Envoy 代理在 Web 客户端和服务器之间建立简单的 gRPC 通信。
我正在使用 gRPC-Web 库来实现此目的。 gRPC-Web 是一个 JavaScript 客户端库,允许 Web 应用程序与 gRPC 服务交互。由于浏览器不支持 HTTP/2 或标准 gRPC 使用的二进制协议,因此 gRPC-Web 提供了一种弥合差距的方法,即使用 HTTP/1.1 或 HTTP/2 并以浏览器可以处理的方式对 gRPC 消息进行编码。 gRPC-Web 的工作原理如下:
- 客户端使用 gRPC-Web 向服务器发送请求,通常通过 HTTP/1.1 或 HTTP/2 进行通信。元数据(如标头)可以附加到请求中,例如用于身份验证(例如 JWT 令牌)。
请求以 gRPC-Web 格式编码,通常对二进制 gRPC 负载使用 base64 编码。客户端通过 HTTP/1.1 或 HTTP/2 发送此请求。
- Envoy(或其他反向代理,如 Nginx)充当 gRPC-Web 客户端和 gRPC 服务器之间的中介。 Envoy 接收 gRPC-Web 请求,解码 gRPC-Web 负载,并使用 HTTP/2 将其作为标准 gRPC 请求转发到 gRPC 服务器。
gRPC 服务器处理请求,就好像它是本机 gRPC 请求一样,使用 HTTP/2 进行通信。
gRPC 服务器处理传入的 gRPC 请求,执行必要的业务逻辑,并生成响应(在本示例中是 Go 编写的应用程序)。响应以标准 gRPC 格式编码并发回 Envoy。
Envoy 接收 gRPC 响应,将其编码为 gRPC-Web 格式(通常使用 base64),然后通过 HTTP/1.1 或 HTTP/2 将其发送回 gRPC-Web 客户端。 gRPC 响应中包含的任何元数据(例如状态代码)都会被适当翻译。
gRPC-Web 客户端对响应进行解码并将其转换为 Web 应用程序中可用的格式。 Web 应用程序处理响应,根据需要更新 UI 或处理错误。

注意:当前不支持客户端和双向流式传输(请参阅流式传输路线图)
gRPC-Web 的优点
- 浏览器兼容性:允许现代 Web 应用程序与 gRPC 服务交互,而不需要对 HTTP/2 和二进制协议的本机支持。
- 效率:利用 gRPC 的性能和效率,同时适应网络。
这里有一个 GitHub 项目:
https://github.com/ehsaniara/gRPC-web-example
原始文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | syntax = "proto3" ;
package helloworld;
option go_package = "./proto" ;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
|
登录后复制
服务器端(去)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | package main
import (
"context"
"google.golang.org/grpc/reflection"
"log"
"net"
pb "github.com/ehsaniara/gRPC-web-example/proto"
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
lis, err := net.Listen( "tcp" , ":50051" )
if err != nil {
log.Fatalf( "failed to listen: %v" , err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf( "failed to serve: %v" , err)
}
log.Println( "Server is running on port 50051" )
if err := s.Serve(lis); err != nil {
log.Fatalf( "failed to serve: %v" , err)
}
}
|
登录后复制
特使配置
1 2 3 4 5 6 7 8 9 10 11 12 | ...
http_filters:
- name: envoy.filters.http.grpc_web
typed_config:
"@type" : type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
- name: envoy.filters.http.cors
typed_config:
"@type" : type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
- name: envoy.filters.http.router
typed_config:
"@type" : type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
...
|
登录后复制
JS Web 客户端(webpack)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import {GreeterClient} from './generated/helloworld_grpc_web_pb' ;
import {HelloRequest} from './generated/helloworld_pb' ;
const client = new GreeterClient( 'http://localhost:8080' );
function sayHello(name) {
const request = new HelloRequest();
request.setName(name);
client.sayHello(request, {}, (err, response) => {
if (err) {
console.error( 'Error:' , err.message);
document.getElementById( 'output' ).textContent = 'Error: ' + err.message;
} else {
console.log( 'Greeting:' , response.getMessage());
document.getElementById( 'output' ).textContent = 'Greeting: ' + response.getMessage();
}
});
}
document.addEventListener( 'DOMContentLoaded' , () => {
const name = 'World' ;
sayHello(name);
});
|
登录后复制
这是与此项目相关的 GitHub 项目
https://github.com/ehsaniara/gRPC-web-example
以上是Web 和服务器之间的 gRPC。的详细内容。更多信息请关注PHP中文网其他相关文章!