gRPC背景介绍
gRPC
是由 google
开发的一个高性能、通用的开源RPC
框架,主要面向移动应用开发且基于HTTP/2
协议标准而设计,同时支持大多数流行的编程语言。
gRPC
基于 HTTP/2
协议传输,而HTTP/2
相比HTTP1.x
,有以下一些优势:
但同时,gRPC也有自身的局限性:
浏览器支持有限:当下,不可能直接从浏览器调用gRPC服务。gRPC大量使用HTTP/2功能,没有浏览器提供支持gRPC客户机的Web请求所需的控制级别。例如,浏览器不允许调用者要求使用的HTTP/2,或者提供对底层HTTP/2框架的访问。
不是人类可读的:HTTP API请求以文本形式发送,可以由人读取和创建。默认情况下,gRPC消息使用protobuf编码。虽然protobuf的发送和接收效率很高,但它的二进制格式是不可读的。protobuf需要在*.proto文件中指定的消息接口描述才能正确反序列化。需要额外的工具来分析线路上的Protobuf有效负载,并手工编写请求。
若需要将内部gRPC作为接口开放给外部用户或浏览器调用,则需要有第三方代理解决HTTP协议转换成gRPC协议的问题。
为此,我们在Apinto多协议支持的基础上,发布了 HTTP转gRPC插件(eolinker.com:apinto:http_to_grpc)
Apinto HTTP转gRPC插件
Apinto 通过插件的方式支持 HTTP协议转换成gRPC协议请求,在http_to_grpc 插件中完成了 HTTP 客户端 与 gRPC Server 通讯时的协议转换及数据编解码工作,其通讯的过程如下:
接下来通过一个完整的示例向大家演示怎样构建一个 HTTP 请求,并通过 Apinto 进行 HTTP协议 转换成 gRPC 协议请求。在以下的示例中,我们会将 Go 作为 gRPC Server 服务端处理程序,使用 Eolink 作为 HTTP 客户端,发起 HTTP 请求。
以下示例可以在 Apinto 仓库中获取。
配置 Protocol Buffer
1、创建示例文件msg.proto
syntax = "proto3";
option go_package = "github.com/eolinker/apinto/example/grpc/demo_service";
package Service;
// HelloRequest 请求
message HelloRequest {
string name = 1;
}
// HelloResponse 响应,成功消息时,msg为json数据
message HelloResponse {
string msg = 1;
string err = 2;
}
该文件定义了示例消息类型,我们在这里定义了一个HelloRequest
和HelloResponse
的message。
2、创建示例文件service.proto
syntax = "proto3";
option go_package = "github.com/eolinker/apinto/example/grpc/demo_service";
import "msg.proto";
package Service;
service Hello {
rpc Hello(HelloRequest) returns (HelloResponse){};
rpc StreamRequest(stream HelloRequest) returns (HelloResponse){};
rpc StreamResponse(HelloRequest) returns (stream HelloResponse) {};
rpc AllStream(stream HelloRequest)returns (stream HelloResponse) {};
}
该文件定义了服务Hello
,引入了第一步创建的文件msg.proto
,定义了四个方法,包含了一元 RPC、客户端流、服务端流、双向流四种gRPC通信模式。
配置服务端程序
1、创建自动生成gRPC文件脚本(grpc.sh)
#!/bin/bash
OUT_DIR=demo_service
set -e
mkdir -p $OUT_DIR
protoc --grpc-gateway_opt logtostderr=true \
--grpc-gateway_opt paths=source_relative \
--grpc-gateway_opt generate_unbound_methods=true \
--grpc-gateway_out=$OUT_DIR \
--go_out=$OUT_DIR --go_opt=paths=source_relative \
--go-grpc_out=$OUT_DIR --go-grpc_opt=paths=source_relative *.proto
该脚本将生成gRPC客户端/服务端调用相关代码,并将其存储到demo_service目录下。
执行grpc.sh
,生成服务端 Go 原始消息和服务/客户端存根。
./grpc.sh
2、实现服务端处理程序接口
package main
import (
"fmt"
service "github.com/eolinker/apinto/example/grpc/demo_service"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc"
"golang.org/x/net/context"
)
var _ service.HelloServer = (*Server)(nil)
type Server struct {
service.UnimplementedHelloServer
}
func NewServer() *Server {
return &Server{}
}
func (s *Server) Hello(ctx context.Context, request *service.HelloRequest) (*service.HelloResponse, error) {
trailingMD, ok := metadata.FromIncomingContext(ctx)
if ok {
grpc.SetTrailer(ctx, trailingMD)
}
return &service.HelloResponse{
Msg: fmt.Sprintf("hello,%s", request.Name),
}, nil
}
上述代码重新定义了Hello方法:
3、定义gRPC服务端入口文件
package main
import (
"fmt"
"net"
"google.golang.org/grpc/reflection"
service "github.com/eolinker/apinto/example/grpc/demo_service"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
)
func main() {
Parse()
err := listen()
if err != nil {
fmt.Println(err)
return
}
}
func listen() error {
address := fmt.Sprintf("%s:%d", BindIP, ListenPort)
l, err := net.Listen("tcp", address)
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
var opts []grpc.ServerOption
if TlsKey != "" && TlsPem != "" {
creds, err := credentials.NewServerTLSFromFile(TlsPem, TlsKey)
if err != nil {
grpclog.Fatalf("Failed to generate credentials %v", err)
}
opts = append(opts, grpc.Creds(creds))
}
// 实例化grpc Server
s := grpc.NewServer(opts...)
ser := NewServer()
// 注册HelloService
service.RegisterHelloServer(s, ser)
// 开启grpc反射
reflection.Register(s)
fmt.Println("Listen on " + address)
return s.Serve(l)
}
在此处,gRPC服务端开启了gRPC反射,配置Apinto网关时,可选择绑定具体的Protobuf资源,也可以直接启用反射,动态获取gRPC服务端的 Protobuf 信息。
4、编译gRPC服务器程序
go build -o grpcServer server/.
配置Apinto
Apinto提供了可视化界面工具Apinto-Dashboard,降低初学者的使用成本,以下操作均在Apinto-Dashboard中进行配置。
1、在全局插件中新建 http_to_grpc 插件
2、创建gRPC服务
在这里,我们配置gRPC服务的相关信息,我们可以配置多个静态负载地址,这里我们填写了 127.0.0.1:9001。
3、创建http路由,绑定grpc_demo
上游服务
4、在路由中绑定 http_to_grpc 插件
由于gRPC服务端示例中,我们开启了gRPC反射,因此,在配置插件时,开启反射按钮即可
注:
当服务名称不填时,则默认使用 HTTP请求路径的第一个/
和第二个/
之间的值作为服务名
当方法名称不填时,则默认使用 HTTP请求路径的第二个/
和第三个/
之间的值作为服务名
即,若HTTP请求路径上/Service.Hello/Hello
,则此时服务名称为Service.Hello
,方法名称为Hello
关于Protobuf编码器
若gRPC未开启反射,我们需要先新建一个Protobuf编码器,绑定 http_to_grpc 插件时,绑定对应的编码器ID即可,详细步骤如下:
1、创建Protobuf编码器
2、在路由中绑定 http_to_grpc 插件
填写完后提交即可。
验证协议转换请求
1、启动gRPC服务器
./grpcServer
2、请求Service.Hello服务的Hello方法
在上文中,我们定义了Hello方法的功能:
调用结果如下:
写在最后
目前Apinto 及其周边项目已经开源,我们希望通过Apinto强大的插件拓展能力,用户可像乐高积木一样根据需要自行拓展Apinto的插件,以满足不同的业务市场需求。 Apinto 目前属于萌芽阶段,我们希望集合广大开源爱好者的力量,与大家一起讨论方案,接受大家的批评指正,一起将产品打磨完善,做下一个端与端间的Traffic Middleware。 这是一个开放和积极的项目,我们诚挚地邀请您一起参与到我们的项目开源工作中。每一个贡献都是有意义的,包括但不限于:
查找bugs,取得性能上的提升
帮助完善文档,提供用户操作体验
提交你们的issue,让我们知道您的奇思妙想
参与自定义插件的开发,丰富apinto的能力
…
欢迎各位开源爱好者参与到Apinto 项目中,和我们一起为开源事业贡献自己的力量。
联系我们