gRPC-Gateway介绍
gRPC简单实现
定义proto文件
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
30syntax = "proto3";
package auth.v1;
option go_package = "coolcar/auth/api/gen/v1;authpb";
// import "google/api/annotations.proto";
service AuthService {
// 注解方式
// rpc Login (LoginRequest) returns (LoginResponse) {
// option(google.api.http) = {
// post: "/v1/auth/login",
// body: "*"
// };
// };
// 也可以通过auth.yaml配置文件形式
rpc Login (LoginRequest) returns (LoginResponse);
}
message LoginRequest {
string code = 1;
}
message LoginResponse {
string access_token = 1;
int32 expires_in = 2;
}根据文件生成相应代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16go程序代码
protoc -I . --go_out gen/v1 --go_opt paths=source_relative auth.proto
grpc服务代码
protoc -I . --go-grpc_out gen/v1 --go-grpc_opt paths=source_relative auth.proto
grpc-gateway代码
protoc -I . --grpc-gateway_out gen/v1 --grpc-gateway_opt paths=source_relative,grpc_api_configuration=auth.yaml auth.proto
三个命令可合为一个
protoc -I . \
--go_out gen/v1 \
--go_opt paths=source_relative \
--go-grpc_out gen/v1 \
--go-grpc_opt paths=source_relative \
--grpc-gateway_out gen/v1 \
--grpc-gateway_opt paths=source_relative,grpc_api_configuration=auth.yaml \
auth.proto生成代码文件
1
2
3
4
5
6
7├── auth.proto # proto
├── auth.yaml # gateway 配置文件
└── gen
└── v1
├── auth_grpc.pb.go # grpc服务方法
├── auth.pb.go # 消息体文件
└── auth.pb.gw.go # grpc-gateway代理文件实现grpc服务接口方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package auth
import (
"context"
authpb "coolcar/auth/api/gen/v1"
)
type Service struct {
// 不要问,继承这个空服务就行了。看稳定是为了兼容后续
*authpb.UnimplementedAuthServiceServer
}
func (s *Service) Login(ctx context.Context, req *authpb.LoginRequest) (*authpb.LoginResponse, error) {
return &authpb.LoginResponse{
AccessToken: "access_token for " + req.Code,
ExpiresIn: 7200,
}, nil
}拉起grpc服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package main
import (
authpb "coolcar/auth/api/gen/v1"
"coolcar/auth/auth"
"log"
"net"
"google.golang.org/grpc"
)
func main() {
// 监听TCP端口号
lis, err := net.Listen("tcp", ":8081")
if err != nil {
log.Fatal(err)
}
// 创建一个grpc服务
s := grpc.NewServer()
// 注册grpc服务
authpb.RegisterAuthServiceServer(s, &auth.Service{})
// 拉起服务
log.Fatal(s.Serve(lis))
}调用grpc服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17func startClient() {
conn, err := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalln("连接服务失败:", err)
}
defer conn.Close()
client := authpb.NewAuthServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
rsp, err := client.Login(ctx, &authpb.LoginRequest{Code: "123456"})
if err != nil {
log.Fatalln("调用方法失败:", err)
}
fmt.Printf("调用结果为: access_token = %v, expire_in = %v", rsp.AccessToken, rsp.ExpiresIn)
}
为gRPC服务添加Gateway
前端程序不能直接调用grpc服务,所以使用grpc-gateway网关反向代理
- 方式一
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
36func registerHandler() {
// 创建一个客户端连接到grpc服务器,然后用grpc-gateway代理请求反向代理到这个客户端
conn, err := grpc.DialContext(
context.Background(),
"localhost:8081",
grpc.WithBlock(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalln("连接grpc服务器失败", err)
}
// 创建路由处理器
gwmux := runtime.NewServeMux(runtime.WithMarshalerOption(
runtime.MIMEWildcard,
&runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
Multiline: false, // 多行显示,Indent
Indent: "", // 缩进
AllowPartial: false,
UseProtoNames: false, // 使用原型proto里定义的字段名
UseEnumNumbers: false, // 使用枚举enum对应的数字
EmitUnpopulated: false, // 是否返回空(未填充)的字段
Resolver: nil,
},
},
))
err = authpb.RegisterAuthServiceHandler(context.Background(), gwmux, conn)
if err != nil {
log.Fatal("注册grpc-gateway代理失败:", err)
}
// 监听http服务
log.Fatal("启动http服务失败:", http.ListenAndServe(":8080", gwmux))
} - 方式二
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
37func registerHandlerFromEndPoint() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 创建路由处理器
gwmux := runtime.NewServeMux(runtime.WithMarshalerOption(
runtime.MIMEWildcard,
// JSONPb对JSON进行编组/解组。与JSONBuiltin不同,它支持protobuf的全部功能。
&runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
// Multiline: false, // 多行显示,Indent
// Indent: "", // 缩进
// AllowPartial: false,
UseProtoNames: true, // 使用原型proto里定义的字段名
UseEnumNumbers: true, // 使用枚举enum对应的数字
EmitUnpopulated: true, // 是否返回空(未填充)的字段
// Resolver: nil,
},
},
))
// 注册服务
err := authpb.RegisterAuthServiceHandlerFromEndpoint(ctx, gwmux, "localhost:8081", []grpc.DialOption{
grpc.WithBlock(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
})
if err != nil {
log.Fatalln("注册服务失败", err)
}
// 监听http服务
err = http.ListenAndServe(":8080", gwmux)
if err != nil {
log.Fatal("启动http服务失败:", err)
}
} - 发起http请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14curl -X POST \
'http://127.0.0.1:8080/v1/auth/login' \
--header 'Accept: */*' \
--header 'User-Agent: Thunder Client (https://www.thunderclient.com)' \
--header 'Content-Type: application/json' \
--data-raw '{
"code": "123"
}'
返回
{
"accessToken": "access_token for 123",
"expiresIn": 7200
}
gRPC-Gateway代理配置
- 注解方式(google.api.http)
需要加入依赖文件googleapisprotobuf 文件修改加入注解1
2
3
4
5
6
7proto
├── google
│ └── api
│ ├── annotations.proto
│ └── http.proto
└── helloworld
└── hello_world.proto1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26syntax = "proto3";
package helloworld;
import "google/api/annotations.proto";
// Here is the overall greeting service definition where we define all our endpoints
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
};
}
}
// The request message containing the user's name
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
} - yaml配置文件
文档地址:https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/grpc_api_configuration/1
2
3
4
5
6
7
8type: google.api.Service
config_version: 3
http:
rules:
- selector: your.service.v1.YourService.Echo
post: /v1/example/echo
body: "*"