admin管理员组文章数量:1029912
搞懂gRPC支持HTTP进行双协议通信
gRPC 是一种高性能、跨语言的 RPC 框架,其核心优势包括:
1)基于 HTTP/2 协议实现多路复用和低延迟通信,显著提升传输效率;
2)通过 Protocol Buffers 提供强类型接口定义和高效的二进制序列化,减少数据体积;
3)支持 双向流式通信(如客户端/服务端流),灵活应对复杂交互场景;
4)自动生成多语言客户端和服务端代码,简化开发并保障一致性;
5)内置 认证、负载均衡、重试和超时机制,天然适配微服务架构,是构建高并发、分布式系统的理想选择。
为什么需要将 gRPC 以 HTTP 形式提供接口?
在微服务架构中,gRPC 凭借其高性能、强类型接口和双向流式通信等特性,成为服务间内部通信的首选协议。然而,直接对外暴露 gRPC 接口往往面临挑战,尤其是在需要与浏览器、移动端或第三方系统交互时。此时,同时支持 HTTP 协议(如 RESTful API)成为关键需求,将 gRPC 服务通过 HTTP(如 RESTful API)对外提供,主要有以下便利性:
1)跨平台兼容性:HTTP/1.1 + JSON 是 Web、移动端、IoT 设备的通用标准,浏览器原生支持,无需引入 gRPC 客户端库。
2)简化客户端调用:前端开发者可直接用 fetch 或 axios 调用接口,无需生成 gRPC 客户端代码。
3)生态集成:兼容现有工具链(如 API 网关、监控、日志、Postman 调试)。
4)渐进式迁移:允许旧系统逐步迁移到 gRPC,无需一次性重构。
如何实现双协议支持?
将 gRPC 服务同时暴露为 HTTP 接口,本质是通过协议转换层或代码生成工具实现两种协议之间的映射。常见的有以下几种典型实现方式:
方式一:协议转换层(反向代理)
通过中间代理将 HTTP 请求转换为 gRPC 调用,常见工具有 gRPC-Gateway 和 Envoy Proxy。核心流程:
1)在 Protobuf 文件中通过注解定义 HTTP 路由(如 RESTful 路径、方法)。
2)生成反向代理代码,监听 HTTP 请求并转发至 gRPC 服务。
3)代理层处理协议差异(如 JSON ↔ Protobuf 的编解码)。
示例(gRPC-Gateway):
代码语言:javascript代码运行次数:0运行复制// 定义 gRPC 服务时添加 HTTP 注解
service UserService {
rpc GetUser(GetUserRequest) returns (User) {
option (google.api.http) = {
get: "/v1/users/{user_id}"
};
}
}
message GetUserRequest { string user_id = 1; }
message User { string name = 1; uint32 age = 2; }
生成代理代码后,HTTP 请求 GET /v1/users/123
会被转换为 GetUser(user_id="123")
的 gRPC 调用。
方式二:双协议服务端
部分框架(如 go-zero、.NET Core gRPC-HTTP API)允许服务端同时监听 gRPC 和 HTTP 端口,并自动处理协议转换。核心流程:
1)使用同一套接口定义(Protobuf 或代码优先)。
2)框架生成两种协议的处理逻辑,共享业务实现。
3)服务端并行处理 gRPC 和 HTTP 请求。
示例(go-zero):
代码语言:javascript代码运行次数:0运行复制// 定义 REST 路由和 gRPC 服务
// greet.api 文件
service greet {
@handler GreetHandler
get /greet (Request) returns (Response)
}
// 自动生成 gRPC 和 HTTP 服务代码
goctl api go -api greet.api -dir .
方式三:基础设施层转换
通过 API 网关(如 Kong、Envoy)动态转换协议,无需修改服务代码。核心流程:
1)网关接收 HTTP 请求,根据配置路由到 gRPC 服务。
2)网关处理协议转换(如 JSON → Protobuf)和负载均衡。
示例(Envoy gRPC-JSON Transcoding):
代码语言:javascript代码运行次数:0运行复制# Envoy 配置
http_filters:
- name: envoy.filters.http.grpc_json_transcoder
config:
proto_descriptor: "path/to/descriptor.pb"
services: ["user.UserService"]
gRPC支持HTTP协议实战
这篇文章我们就来分享一下使用gRPC API Gateway插件,通过反向代理实现双协议的支持,大致会分为以下几个步骤:
1)定义RPC接口:引入gRPC API Gateway模块定义RPC和HTTP接口
2)编译中间文件:使用buf工具编译protobuf文件
3)编码实现接口:编写Go代码实现RPC接口,并同步支持HTTP
定义RPC接口
首先我们按照buf工具的约定定义配置文件,名为buf.yaml:
代码语言:javascript代码运行次数:0运行复制version: v1
deps:
- "buf.build/meshapi/grpc-api-gateway"
接下来定义protobuf文件,与常规gRPC接口定义不同的地方主要有两处:
第一是引入了三方的protobuf文件:
代码语言:javascript代码运行次数:0运行复制import "meshapi/gateway/annotations.proto";
第二是利用引入的protobuf文件定义HTTP接口,具体如下:
代码语言:javascript代码运行次数:0运行复制syntax = "proto3";
package main;
import "meshapi/gateway/annotations.proto";
option go_package = "/main";
service HelloService {
rpc SayHello(HelloRequest) returns (HelloResponse) {
option (meshapi.gateway.http) = {
post: "/hello",
body: "*"
};
}
}
message HelloRequest {
string msg = 1;
}
message HelloResponse {
string msg = 1;
}
使用buf工具编译protobuf文件
buf工具 是一个专为 Protocol Buffers(Protobuf) 设计的现代化开发工具链,旨在优化 Protobuf 生态系统的开发体验。它通过提供代码生成、依赖管理、代码质量检查(Linting)、格式化、版本控制等功能,简化了 Protobuf 文件的开发、维护和协作流程。
下载和安装方式参考:/docs/cli/installation/
优势对比传统 Protobuf 工具:
传统 Protobuf 流程 | Buf 工具 |
---|---|
手动管理 protoc 版本和插件 | 统一 CLI 工具,简化安装和版本管理 |
繁琐的命令行参数生成代码 | 声明式配置,一键生成多语言代码 |
依赖需手动下载或拷贝 .proto 文件 | 声明式依赖,自动从远程仓库拉取 |
缺乏代码规范检查 | 内置 Linting 和格式化,提升代码质量 |
无版本化模块管理 | 支持模块发布、版本控制和安全扫描 |
编译命令:
更新模块依赖:
代码语言:javascript代码运行次数:0运行复制buf mod update
编译protobuf文件:
代码语言:javascript代码运行次数:0运行复制buf generate
编译后我们会看见项目目录会增加很多相关文件。
编写Go代码实现RPC接口
在Go代码中我们定义HelloService结构体,实现SayHello方法,用于处理gRPC请求,SayHello方法接收一个HelloRequest请求,返回包含问候信息的HelloResponse。
主函数启动gRPC服务器,在40000端口监听TCP连接,创建gRPC服务器实例,注册HelloService服务,在goroutine中启动gRPC服务器,创建新的HTTP ServeMux,注册HTTP到gRPC的转换处理器,在4000端口启动HTTP网关服务器,具体代码如下:
代码语言:javascript代码运行次数:0运行复制package main
import (
"context"
"fmt"
"log"
"net"
"net/http"
"github/meshapi/grpc-api-gateway/gateway"
"google.golang/grpc"
"google.golang/grpc/credentials/insecure"
)
type HelloService struct {
UnimplementedHelloServiceServer
}
func (u *HelloService) SayHello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) {
log.Printf("Received request: %+v", req)
return &HelloResponse{Msg: fmt.Sprintf("Hi %s", req.GetMsg())}, nil
}
func main() {
// Start the gRPC server
listener, err := net.Listen("tcp", ":40000")
if err != nil {
log.Fatalf("Failed to bind: %s", err)
}
server := grpc.NewServer()
RegisterHelloServiceServer(server, &HelloService{})
gofunc() {
log.Printf("Starting gRPC server on port 40000...")
if err := server.Serve(listener); err != nil {
log.Fatalf("Failed to start gRPC server: %s", err)
}
}()
// Set up the HTTP gateway
connection, err := grpc.NewClient(":40000", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("Failed to dial gRPC server: %s", err)
}
restGateway := gateway.NewServeMux()
RegisterHelloServiceHandlerClient(context.Background(), restGateway, NewHelloServiceClient(connection))
log.Printf("Starting HTTP gateway on port 4000...")
if err := http.ListenAndServe(":4000", restGateway); err != nil {
log.Fatalf("Failed to start HTTP gateway: %s", err)
}
}
调用接口
代码语言:javascript代码运行次数:0运行复制package main
import (
"context"
"google.golang/grpc"
"google.golang/grpc/credentials/insecure"
"io"
"log"
"net/http"
"strings"
"testing"
)
func TestCallRpcApi(t *testing.T) {
connection, err := grpc.NewClient(":40000", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("Failed to dial gRPC server: %s", err)
}
client := NewHelloServiceClient(connection)
resp, err := client.SayHello(context.TODO(), &HelloRequest{Msg: "YanTongXue"})
if err != nil {
log.Fatalf("Failed to call SayHello: %s", err)
}
log.Printf("Response: %s", resp.GetMsg())
}
func TestCallHttpApi(t *testing.T) {
// 创建HTTP客户端
client := &http.Client{}
// 创建请求体
requestBody := strings.NewReader(`{"msg":"YanTongXue"}`)
// 创建HTTP请求
req, err := http.NewRequest("POST", "http://localhost:4000/hello", requestBody)
if err != nil {
log.Fatalf("Failed to create HTTP request: %s", err)
}
req.Header.Set("Content-Type", "application/json")
// 发送请求
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Failed to send HTTP request: %s", err)
}
defer resp.Body.Close()
// 读取响应
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed to read response body: %s", err)
}
// 打印响应
log.Printf("HTTP Response: %s", string(body))
}
启动服务后运行两个Test函数就能成功调用RPC接口和HTTP接口了。
小总结
为 gRPC 接口同时支持 HTTP 协议,本质上是在高性能与广泛兼容性之间寻求平衡。通过协议转换层、双协议框架或基础设施网关,开发者可以在保留 gRPC 内部通信优势的同时,对外提供易用的 HTTP 接口。这一方案尤其适合需要兼顾微服务效率与开放生态的场景,例如:
- 对外提供开放 API 的 SaaS 平台;
- 需要与浏览器、移动端深度交互的应用;
- 渐进式迁移至云原生架构的传统系统。
未来,随着 HTTP/3 和 gRPC-Web 的普及,跨协议支持将更加高效,但“双协议适配”仍是微服务设计中的重要模式。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-16,如有侵权请联系 cloudcommunity@tencent 删除httpgrpc接口通信协议搞懂gRPC支持HTTP进行双协议通信
gRPC 是一种高性能、跨语言的 RPC 框架,其核心优势包括:
1)基于 HTTP/2 协议实现多路复用和低延迟通信,显著提升传输效率;
2)通过 Protocol Buffers 提供强类型接口定义和高效的二进制序列化,减少数据体积;
3)支持 双向流式通信(如客户端/服务端流),灵活应对复杂交互场景;
4)自动生成多语言客户端和服务端代码,简化开发并保障一致性;
5)内置 认证、负载均衡、重试和超时机制,天然适配微服务架构,是构建高并发、分布式系统的理想选择。
为什么需要将 gRPC 以 HTTP 形式提供接口?
在微服务架构中,gRPC 凭借其高性能、强类型接口和双向流式通信等特性,成为服务间内部通信的首选协议。然而,直接对外暴露 gRPC 接口往往面临挑战,尤其是在需要与浏览器、移动端或第三方系统交互时。此时,同时支持 HTTP 协议(如 RESTful API)成为关键需求,将 gRPC 服务通过 HTTP(如 RESTful API)对外提供,主要有以下便利性:
1)跨平台兼容性:HTTP/1.1 + JSON 是 Web、移动端、IoT 设备的通用标准,浏览器原生支持,无需引入 gRPC 客户端库。
2)简化客户端调用:前端开发者可直接用 fetch 或 axios 调用接口,无需生成 gRPC 客户端代码。
3)生态集成:兼容现有工具链(如 API 网关、监控、日志、Postman 调试)。
4)渐进式迁移:允许旧系统逐步迁移到 gRPC,无需一次性重构。
如何实现双协议支持?
将 gRPC 服务同时暴露为 HTTP 接口,本质是通过协议转换层或代码生成工具实现两种协议之间的映射。常见的有以下几种典型实现方式:
方式一:协议转换层(反向代理)
通过中间代理将 HTTP 请求转换为 gRPC 调用,常见工具有 gRPC-Gateway 和 Envoy Proxy。核心流程:
1)在 Protobuf 文件中通过注解定义 HTTP 路由(如 RESTful 路径、方法)。
2)生成反向代理代码,监听 HTTP 请求并转发至 gRPC 服务。
3)代理层处理协议差异(如 JSON ↔ Protobuf 的编解码)。
示例(gRPC-Gateway):
代码语言:javascript代码运行次数:0运行复制// 定义 gRPC 服务时添加 HTTP 注解
service UserService {
rpc GetUser(GetUserRequest) returns (User) {
option (google.api.http) = {
get: "/v1/users/{user_id}"
};
}
}
message GetUserRequest { string user_id = 1; }
message User { string name = 1; uint32 age = 2; }
生成代理代码后,HTTP 请求 GET /v1/users/123
会被转换为 GetUser(user_id="123")
的 gRPC 调用。
方式二:双协议服务端
部分框架(如 go-zero、.NET Core gRPC-HTTP API)允许服务端同时监听 gRPC 和 HTTP 端口,并自动处理协议转换。核心流程:
1)使用同一套接口定义(Protobuf 或代码优先)。
2)框架生成两种协议的处理逻辑,共享业务实现。
3)服务端并行处理 gRPC 和 HTTP 请求。
示例(go-zero):
代码语言:javascript代码运行次数:0运行复制// 定义 REST 路由和 gRPC 服务
// greet.api 文件
service greet {
@handler GreetHandler
get /greet (Request) returns (Response)
}
// 自动生成 gRPC 和 HTTP 服务代码
goctl api go -api greet.api -dir .
方式三:基础设施层转换
通过 API 网关(如 Kong、Envoy)动态转换协议,无需修改服务代码。核心流程:
1)网关接收 HTTP 请求,根据配置路由到 gRPC 服务。
2)网关处理协议转换(如 JSON → Protobuf)和负载均衡。
示例(Envoy gRPC-JSON Transcoding):
代码语言:javascript代码运行次数:0运行复制# Envoy 配置
http_filters:
- name: envoy.filters.http.grpc_json_transcoder
config:
proto_descriptor: "path/to/descriptor.pb"
services: ["user.UserService"]
gRPC支持HTTP协议实战
这篇文章我们就来分享一下使用gRPC API Gateway插件,通过反向代理实现双协议的支持,大致会分为以下几个步骤:
1)定义RPC接口:引入gRPC API Gateway模块定义RPC和HTTP接口
2)编译中间文件:使用buf工具编译protobuf文件
3)编码实现接口:编写Go代码实现RPC接口,并同步支持HTTP
定义RPC接口
首先我们按照buf工具的约定定义配置文件,名为buf.yaml:
代码语言:javascript代码运行次数:0运行复制version: v1
deps:
- "buf.build/meshapi/grpc-api-gateway"
接下来定义protobuf文件,与常规gRPC接口定义不同的地方主要有两处:
第一是引入了三方的protobuf文件:
代码语言:javascript代码运行次数:0运行复制import "meshapi/gateway/annotations.proto";
第二是利用引入的protobuf文件定义HTTP接口,具体如下:
代码语言:javascript代码运行次数:0运行复制syntax = "proto3";
package main;
import "meshapi/gateway/annotations.proto";
option go_package = "/main";
service HelloService {
rpc SayHello(HelloRequest) returns (HelloResponse) {
option (meshapi.gateway.http) = {
post: "/hello",
body: "*"
};
}
}
message HelloRequest {
string msg = 1;
}
message HelloResponse {
string msg = 1;
}
使用buf工具编译protobuf文件
buf工具 是一个专为 Protocol Buffers(Protobuf) 设计的现代化开发工具链,旨在优化 Protobuf 生态系统的开发体验。它通过提供代码生成、依赖管理、代码质量检查(Linting)、格式化、版本控制等功能,简化了 Protobuf 文件的开发、维护和协作流程。
下载和安装方式参考:/docs/cli/installation/
优势对比传统 Protobuf 工具:
传统 Protobuf 流程 | Buf 工具 |
---|---|
手动管理 protoc 版本和插件 | 统一 CLI 工具,简化安装和版本管理 |
繁琐的命令行参数生成代码 | 声明式配置,一键生成多语言代码 |
依赖需手动下载或拷贝 .proto 文件 | 声明式依赖,自动从远程仓库拉取 |
缺乏代码规范检查 | 内置 Linting 和格式化,提升代码质量 |
无版本化模块管理 | 支持模块发布、版本控制和安全扫描 |
编译命令:
更新模块依赖:
代码语言:javascript代码运行次数:0运行复制buf mod update
编译protobuf文件:
代码语言:javascript代码运行次数:0运行复制buf generate
编译后我们会看见项目目录会增加很多相关文件。
编写Go代码实现RPC接口
在Go代码中我们定义HelloService结构体,实现SayHello方法,用于处理gRPC请求,SayHello方法接收一个HelloRequest请求,返回包含问候信息的HelloResponse。
主函数启动gRPC服务器,在40000端口监听TCP连接,创建gRPC服务器实例,注册HelloService服务,在goroutine中启动gRPC服务器,创建新的HTTP ServeMux,注册HTTP到gRPC的转换处理器,在4000端口启动HTTP网关服务器,具体代码如下:
代码语言:javascript代码运行次数:0运行复制package main
import (
"context"
"fmt"
"log"
"net"
"net/http"
"github/meshapi/grpc-api-gateway/gateway"
"google.golang/grpc"
"google.golang/grpc/credentials/insecure"
)
type HelloService struct {
UnimplementedHelloServiceServer
}
func (u *HelloService) SayHello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) {
log.Printf("Received request: %+v", req)
return &HelloResponse{Msg: fmt.Sprintf("Hi %s", req.GetMsg())}, nil
}
func main() {
// Start the gRPC server
listener, err := net.Listen("tcp", ":40000")
if err != nil {
log.Fatalf("Failed to bind: %s", err)
}
server := grpc.NewServer()
RegisterHelloServiceServer(server, &HelloService{})
gofunc() {
log.Printf("Starting gRPC server on port 40000...")
if err := server.Serve(listener); err != nil {
log.Fatalf("Failed to start gRPC server: %s", err)
}
}()
// Set up the HTTP gateway
connection, err := grpc.NewClient(":40000", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("Failed to dial gRPC server: %s", err)
}
restGateway := gateway.NewServeMux()
RegisterHelloServiceHandlerClient(context.Background(), restGateway, NewHelloServiceClient(connection))
log.Printf("Starting HTTP gateway on port 4000...")
if err := http.ListenAndServe(":4000", restGateway); err != nil {
log.Fatalf("Failed to start HTTP gateway: %s", err)
}
}
调用接口
代码语言:javascript代码运行次数:0运行复制package main
import (
"context"
"google.golang/grpc"
"google.golang/grpc/credentials/insecure"
"io"
"log"
"net/http"
"strings"
"testing"
)
func TestCallRpcApi(t *testing.T) {
connection, err := grpc.NewClient(":40000", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("Failed to dial gRPC server: %s", err)
}
client := NewHelloServiceClient(connection)
resp, err := client.SayHello(context.TODO(), &HelloRequest{Msg: "YanTongXue"})
if err != nil {
log.Fatalf("Failed to call SayHello: %s", err)
}
log.Printf("Response: %s", resp.GetMsg())
}
func TestCallHttpApi(t *testing.T) {
// 创建HTTP客户端
client := &http.Client{}
// 创建请求体
requestBody := strings.NewReader(`{"msg":"YanTongXue"}`)
// 创建HTTP请求
req, err := http.NewRequest("POST", "http://localhost:4000/hello", requestBody)
if err != nil {
log.Fatalf("Failed to create HTTP request: %s", err)
}
req.Header.Set("Content-Type", "application/json")
// 发送请求
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Failed to send HTTP request: %s", err)
}
defer resp.Body.Close()
// 读取响应
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed to read response body: %s", err)
}
// 打印响应
log.Printf("HTTP Response: %s", string(body))
}
启动服务后运行两个Test函数就能成功调用RPC接口和HTTP接口了。
小总结
为 gRPC 接口同时支持 HTTP 协议,本质上是在高性能与广泛兼容性之间寻求平衡。通过协议转换层、双协议框架或基础设施网关,开发者可以在保留 gRPC 内部通信优势的同时,对外提供易用的 HTTP 接口。这一方案尤其适合需要兼顾微服务效率与开放生态的场景,例如:
- 对外提供开放 API 的 SaaS 平台;
- 需要与浏览器、移动端深度交互的应用;
- 渐进式迁移至云原生架构的传统系统。
未来,随着 HTTP/3 和 gRPC-Web 的普及,跨协议支持将更加高效,但“双协议适配”仍是微服务设计中的重要模式。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-16,如有侵权请联系 cloudcommunity@tencent 删除httpgrpc接口通信协议本文标签: 搞懂gRPC支持HTTP进行双协议通信
版权声明:本文标题:搞懂gRPC支持HTTP进行双协议通信 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/jiaocheng/1747623761a2194884.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论