各位观众老爷,大家好!我是今天的主讲人,很高兴和大家聊聊 gRPC-Web 协议转换与 Proxy 层设计,也就是让你的浏览器能愉快地和 gRPC 服务“眉来眼去”。
我们今天的主题是:浏览器到 gRPC 服务:JS gRPC-Web
协议转换与 Proxy
层设计。
好,废话不多说,咱们直接上干货!
一、 为什么我们需要 gRPC-Web?
首先,我们要搞清楚一个问题:为啥浏览器不能直接和 gRPC 服务通信?
答案很简单:浏览器不支持 gRPC 的 HTTP/2 binary 协议。
传统的 gRPC 使用 HTTP/2 作为传输协议,并且数据采用 Protocol Buffers (protobuf) 进行序列化。这对于后端服务之间的高效通信简直是完美CP,但是浏览器表示:“我看不懂,我不玩!”
浏览器主要使用 HTTP/1.1 和 XMLHttpRequest (XHR) 或 Fetch API 进行网络通信,并且更喜欢 JSON 这种“人类友好型”的数据格式。
因此,我们需要一个“翻译”,一个“中间人”,来让浏览器和 gRPC 服务能够互相理解。 这就是 gRPC-Web 的用武之地。
二、 gRPC-Web 是什么?
gRPC-Web 是一种协议和库,它允许 Web 浏览器应用像调用原生 gRPC 服务一样,直接调用 gRPC 服务。
它通过以下方式实现:
- 协议转换: gRPC-Web 客户端会将浏览器发送的 HTTP 请求转换为 gRPC 兼容的请求,并发送到 gRPC-Web Proxy。
- Proxy: gRPC-Web Proxy 接收到请求后,将其转换为标准的 gRPC 请求,并转发到后端的 gRPC 服务。
- 反向转换: gRPC 服务返回的响应,Proxy 会将其转换为 gRPC-Web 客户端可以理解的格式,然后发送回浏览器。
简单来说,gRPC-Web 就像一个“翻译官”,在浏览器和 gRPC 服务之间架起一座桥梁。
三、 核心组件
gRPC-Web 方案中,主要有以下几个核心组件:
- gRPC 服务: 这是你的后端服务,使用 gRPC 协议。
- gRPC-Web 客户端库: 这是一个 JavaScript 库,用于在浏览器中调用 gRPC 服务。它负责将 JavaScript 对象序列化为 protobuf 格式,并处理与 Proxy 的通信。
- gRPC-Web Proxy: 这是一个服务器端组件,负责接收 gRPC-Web 客户端的请求,将其转换为 gRPC 请求,并转发到后端的 gRPC 服务。常见的 Proxy 实现有 Envoy、grpc-web 官方提供的
grpcwebproxy
。 - Protocol Buffers (protobuf): 用于定义 gRPC 服务接口和消息格式。
可以用表格来总结:
组件 | 作用 | 技术栈 |
---|---|---|
gRPC 服务 | 提供后端服务,使用 gRPC 协议 | Golang, Java, Python, C++ 等 |
gRPC-Web 客户端 | 在浏览器中调用 gRPC 服务,负责协议转换和通信 | JavaScript |
gRPC-Web Proxy | 接收 gRPC-Web 请求,转换为 gRPC 请求,转发到后端服务,并进行反向转换 | Envoy, grpcwebproxy (Go) |
protobuf | 定义 gRPC 服务接口和消息格式,用于代码生成 | Protobuf IDL |
四、 gRPC-Web Proxy 的选择和设计
gRPC-Web Proxy 是整个方案的核心。选择合适的 Proxy 并进行合理的设计,对于系统的性能、可扩展性和安全性至关重要。
1. 常见的 Proxy 实现
- Envoy: 一个高性能的开源边缘和服务代理,功能非常强大,支持各种协议和配置,可以作为 gRPC-Web Proxy 使用。优点是性能好,可扩展性强,缺点是配置相对复杂。
grpcwebproxy
: grpc-web 官方提供的 Proxy,用 Go 语言编写,配置简单,易于上手。优点是简单易用,缺点是功能相对有限。
2. Proxy 的设计要点
- 性能: Proxy 位于浏览器和 gRPC 服务之间,任何性能瓶颈都会影响用户体验。因此,Proxy 必须具有高性能,能够快速处理请求和响应。
- 可扩展性: 随着业务的发展,请求量会不断增加。Proxy 应该具有良好的可扩展性,能够轻松应对高并发的场景。
- 安全性: Proxy 应该提供必要的安全措施,例如 TLS 加密、身份验证和授权,以保护数据安全。
- 监控和日志: Proxy 应该提供完善的监控和日志功能,方便我们了解系统的运行状态,及时发现和解决问题。
- 配置管理: Proxy 的配置应该易于管理,方便我们进行修改和更新。
3. Proxy 的部署方式
Proxy 可以部署在以下位置:
- 反向代理服务器后面: 例如 Nginx 或 Apache。这种方式可以利用反向代理服务器的负载均衡、缓存和安全功能。
- Kubernetes 集群中: 可以使用 Kubernetes 的 Ingress 或 Service Mesh 来管理 Proxy。这种方式可以提高系统的可靠性和可扩展性。
- 云平台: 可以使用云平台的 API Gateway 或 Load Balancer 来管理 Proxy。这种方式可以简化部署和运维。
五、 代码示例:使用 grpcwebproxy
为了让大家更直观地了解 gRPC-Web 的使用,我们以 grpcwebproxy
为例,演示一个简单的例子。
1. 定义 protobuf 文件
首先,我们需要定义一个 protobuf 文件,描述 gRPC 服务的接口和消息格式。
syntax = "proto3";
package example;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
这个 protobuf 文件定义了一个 Greeter
服务,包含一个 SayHello
方法,接收 HelloRequest
消息,返回 HelloReply
消息。
2. 生成 gRPC 代码
使用 protoc
命令生成 gRPC 代码,包括服务端和客户端代码。
protoc --go_out=. --go_opt=paths=source_relative
--go-grpc_out=. --go-grpc_opt=paths=source_relative
example.proto
这个命令会生成 example.pb.go
和 example_grpc.pb.go
两个文件,包含 gRPC 服务的接口和消息类型的定义。
3. 实现 gRPC 服务
使用生成的 gRPC 代码,实现 gRPC 服务。
package main
import (
"context"
"fmt"
"log"
"net"
"google.golang.org/grpc"
pb "path/to/your/generated/code" // 替换成你生成的代码路径
)
const (
port = ":50051"
)
type server struct {
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
fmt.Printf("Server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
这个代码实现了一个简单的 gRPC 服务,接收 HelloRequest
消息,返回 HelloReply
消息,其中包含了 "Hello " + name
的问候语。
4. 启动 gRPC 服务
编译并运行 gRPC 服务。
go run main.go
5. 配置 grpcwebproxy
下载并配置 grpcwebproxy
。
# 下载 grpcwebproxy
go install github.com/grpc-ecosystem/grpc-web/grpcwebproxy@latest
# 运行 grpcwebproxy
grpcwebproxy --backend_addr=localhost:50051 --backend_tls=false --allow_all_origins
--backend_addr
指定后端的 gRPC 服务地址。--backend_tls=false
表示后端服务不使用 TLS 加密。--allow_all_origins
允许所有来源的跨域请求。生产环境请务必配置安全的 CORS 策略。
6. 创建 gRPC-Web 客户端
使用 gRPC-Web 客户端库,创建一个 JavaScript 客户端,用于调用 gRPC 服务。
首先,安装 gRPC-Web 客户端库。
npm install google-protobuf grpc-web
然后,创建 JavaScript 代码。
// Import the grpc-web library
const {GreeterClient} = require('./example_grpc_web_pb'); // 替换成你生成的代码路径
const {HelloRequest} = require('./example_pb'); // 替换成你生成的代码路径
// Create a new gRPC-Web client
const client = new GreeterClient('http://localhost:8080'); // grpcwebproxy 默认端口是 8080
// Create a new request
const request = new HelloRequest();
request.setName('World');
// Call the SayHello method
client.sayHello(request, {}, (err, response) => {
if (err) {
console.error(err);
} else {
console.log(response.getMessage()); // 输出 "Hello World"
}
});
这个代码创建了一个 GreeterClient
实例,并调用 sayHello
方法,向 gRPC 服务发送 HelloRequest
消息,并打印返回的 HelloReply
消息。
7. 运行 gRPC-Web 客户端
在浏览器中运行 JavaScript 代码,或者使用 Node.js 运行。
node index.js
你应该能在控制台中看到 "Hello World" 的输出。
六、 生产环境的注意事项
在生产环境中部署 gRPC-Web 应用时,需要考虑以下几个方面:
- CORS: gRPC-Web 应用通常需要跨域访问 gRPC 服务。因此,需要在 Proxy 上配置 CORS 策略,允许来自特定域的请求。
- TLS: 为了保证数据安全,建议使用 TLS 加密传输数据。需要在 Proxy 上配置 TLS 证书,并启用 HTTPS。
- 身份验证和授权: 为了保护 gRPC 服务,需要进行身份验证和授权。可以使用 JWT 或 OAuth 2.0 等协议进行身份验证,并使用 RBAC 或 ABAC 等模型进行授权。
- 监控和日志: 为了了解系统的运行状态,需要收集 Proxy 和 gRPC 服务的监控指标和日志。可以使用 Prometheus、Grafana 和 ELK Stack 等工具进行监控和日志分析。
- 错误处理: 需要对 gRPC 服务的错误进行处理,并向客户端返回友好的错误信息。
七、 总结
gRPC-Web 是一种让 Web 浏览器应用直接调用 gRPC 服务的有效方案。通过 gRPC-Web Proxy 进行协议转换,我们可以充分利用 gRPC 的高性能和强类型特性,构建高效可靠的 Web 应用。
选择合适的 Proxy 并进行合理的设计,对于系统的性能、可扩展性和安全性至关重要。在生产环境中部署 gRPC-Web 应用时,需要考虑 CORS、TLS、身份验证和授权、监控和日志以及错误处理等问题。
希望今天的讲座能帮助大家更好地理解 gRPC-Web 协议转换与 Proxy 层设计。谢谢大家!
各位还有什么问题吗?欢迎提问!