gRPC
1. gRPC讲解
我们回顾一下微服务,
微服务架构将应用构建为一系列小型,松散耦合且可独立部署的服务
,每项服务均旨在执行特定业务功能,可通过定义明确的API与其他服务进行通信
。
- 微服务之间通信的方式为
RPC
- 在Golang的体系中,
gRPC
是使用最广的RPC框架。 gRPC
基于HTTP/2.0,采用Protobuf
作为数据序列化协议。gRPC
具有语言中立的特点go-zero
框架对GRPC已经支持,可以轻松的开发gRPC应用
1.1 proto文件
在使用Protobuf之前,我们需要编写proto格式的文件
//指定正在使用proto3语法
syntax = "proto3";
//消息类型 用于传输的数据格式的定义
//每个字段必须指定字段编号: 1到536870911之间的一个数字,需要唯一,19000到19999是保留编号不能使用
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
}
message SearchResponse{}
//可以导入其他proto文件 使用其他proto文件定义的消息
import "myproject/other_protos.proto";
//包定义 防止消息类型之间的名称冲突
package foo.bar
//定义RPC服务接口
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2. gRPC案例
rpc
分为server端
和client端
:
- server端提供
rpc服务
,为服务提供方
- client端为
服务消费方
- RPC框架的目的是让远程调用变得和本地调用一样方便
2.1 server端
先生成proto文件
goctl rpc -o greet.proto
1//语法版本 syntax = "proto3"; //包定义 对go无效 package greet; //生成go文件的包名 option go_package="./greet"; //消息体 需要传输的数据结构 message Request { string ping = 1; } //消息体 需要传输的数据结构 message Response { string pong = 1; } //服务名称以及服务定义 service Greet { rpc Ping(Request) returns(Response); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20生成gRPC服务端
$ goctl rpc protoc greet.proto --go_out=./grpc-server --go-grpc_out=./grpc-server --zrpc_out=./grpc-server
1go_out:
proto生成的go代码所在的目录,proto本身的命令参数go-grpc_out:
proto生成的grpc代码所在的目录,proto本身的命令参数,和go_out
必须同一个目录zrpc_out:
goctl rpc
自带的命令,go-zero生成的代码所在的目录
拉取依赖
# cd grpc-server # go mod tidy
1
2编写代码逻辑
func (l *PingLogic) Ping(in *greet.Request) (*greet.Response, error) { // todo: add your logic here and delete this line return &greet.Response{ Pong: "pong", }, nil }
1
2
3
4
5
6
7修改配置文件,去掉etcd配置,采用直连模式
Name: greet.rpc ListenOn: 0.0.0.0:8080 #Etcd: # Hosts: # - 127.0.0.1:2379 # Key: greet.rpc
1
2
3
4
5
6启动服务
观察生成的代码:
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
// 向grpc注册服务
greet.RegisterGreetServer(grpcServer, server.NewGreetServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
//注册反射服务,在gRPC中,反射是一种机制,允许客户端在不知道服务定义(即.proto文件)的情况下查询服务端上的gRPC服务信息。
reflection.Register(grpcServer)
}
})
defer s.Stop()
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
2.2 grpcurl
上述我们注册了
反射服务
,可以使用grpcurl
来进行测试
2.2.1 安装
去https://github.com/fullstorydev/grpcurl/releases
下载。
下载完成后,配置环境变量或者直接放在GOPATH的bin
目录下即可。
2.2.2 基本使用
Name: greet.rpc
ListenOn: 0.0.0.0:8080
Mode: dev #开发模式 用于反射服务可用
1
2
3
2
3
# 获取grpc服务列表 -plaintext选项代表使用http连接
# grpcurl -plaintext localhost:8080 list
greet.Greet
grpc.health.v1.Health
grpc.reflection.v1.ServerReflection
grpc.reflection.v1alpha.ServerReflection
1
2
3
4
5
6
2
3
4
5
6
# 获取grpc服务的方法
# grpcurl -plaintext localhost:8080 list greet.Greet
greet.Greet.Ping
1
2
3
2
3
# 获取服务细节 或者 方法细节
# grpcurl -plaintext localhost:8080 describe
greet.Greet is a service:
service Greet {
rpc Ping ( .greet.Request ) returns ( .greet.Response );
}
# grpcurl -plaintext localhost:8080 describe greet.Greet
greet.Greet is a service:
service Greet {
rpc Ping ( .greet.Request ) returns ( .greet.Response );
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 获取类型信息
# grpcurl -plaintext localhost:8080 describe greet.Request
greet.Request is a message:
message Request {
string ping = 1;
}
1
2
3
4
5
6
2
3
4
5
6
# 调用方法
# grpcurl -d {\"ping\":\"ping\"} -plaintext localhost:8080 greet.Greet/Ping
# 或者使用 -d @ 这种是流式输入,windows用先回车 crtl+z结束输入 其他一般用 先回车 crtl+d结束输入
# grpcurl -d @ -plaintext localhost:8080 greet.Greet/Ping
{"ping":"ping"}
1
2
3
4
5
2
3
4
5
经过测试,我们发现grpc服务可以正常使用
2.4 client端
创建
grpc-client
目录,在目录下,新建etc
目录和client.go
client.go
内容package main import ( "context" "github.com/zeromicro/go-zero/core/conf" "github.com/zeromicro/go-zero/zrpc" "gozero-learn/grpc-server/greet" "log" ) func main() { var clientConf zrpc.RpcClientConf conf.MustLoad("grpc-client/etc/client.yaml", &clientConf) conn := zrpc.MustNewClient(clientConf) client := greet.NewGreetClient(conn.Conn()) resp, err := client.Ping(context.Background(), &greet.Request{Ping: "ping"}) if err != nil { log.Fatal(err) return } log.Println(resp) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24client.yaml
配置文件内容:Target: 127.0.0.1:8080
1运行进行测试
2.4.1 原生支持
func main() {
var clientConf zrpc.RpcClientConf
conf.MustLoad("grpc-client/etc/client.yaml", &clientConf)
//conn := zrpc.MustNewClient(clientConf)
conn, err := grpc.NewClient("127.0.0.1:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
client := greet.NewGreetClient(conn)
resp, err := client.Ping(context.Background(), &greet.Request{Ping: "ping"})
if err != nil {
log.Fatal(err)
return
}
log.Println(resp)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3. 配置文件说明
在使用
gRPC
服务时,我们会用到go-zero
内置的RpcClientConf
配置和RpcServerConf
配置
RpcSeverConf
:- ServiceConf: 基础服务配置
- ListenOn: 监听地址
- Etcd: etcd 配置项
- Auth: 是否开启 Auth
- Redis: rpc 认证,仅当 Auth 为 true 生效
- Timeout: 超时时间
- Middlewares: 启用中间件
- Health: 是否开启健康检查
RpcClientConf
:- Etcd: 服务发现配置,当需要使用 etcd 做服务发现时配置
- Endpoints: RPC Server 地址列表,用于直连,当需要直连 rpc server 集群时配置
- Target: 域名解析地址,名称规则请参考
- App: rpc 认证的 app 名称,仅当 rpc server 开启认证时配置
- Token: rpc 认证的 token,仅当 rpc server 开启认证时配置
- NonBlock: 是否阻塞模式,当值为 true 时,不会阻塞 rpc 链接
- Timeout: 超时时间
- KeepaliveTime: 保活时间
- Middlewares: 是否启用中间件
4. 使用etcd
4.1 服务发现
注册中心是服务发现的核心,是包含服务实例数据(例如ip地址,端口等)的数据库。所以注册中心需要一个高可用的分布式键/值存储,例如Etcd,Zookeeper,Consul等。
- Etcd更加稳定可靠
- 在服务发现的实现上,Etcd使用的是节点租约(Lease)
- Etcd支持稳定的watch
- Etcd支持mvcc
- Etcd支持更大的数据规模,支持存储百万到千万级别的key
- Etcd性能更好
4.2 docker-compose配置
version: "3.5"
services:
Etcd:
container_name: etcd3-go-zero
image: bitnami/etcd:3.5.6
deploy:
replicas: 1
restart_policy:
condition: on-failure
environment:
- ALLOW_NONE_AUTHENTICATION=yes
- ETCD_SNAPSHOT_COUNT=10000
- ETCD_QUOTA_BACKEND_BYTES=6442450944
privileged: true
volumes:
- ./volumes/etcd/data:/bitnami/etcd/data
ports:
- 2379:2379
- 2380:2380
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
4.3 使用etcd做为注册中心
server端:
Etcd: Hosts: - 127.0.0.1:2379 Key: greet.rpc
1
2
3
4client端:
#Target: 127.0.0.1:8080 Etcd: Hosts: - 127.0.0.1:2379 Key: greet.rpc
1
2
3
4
5
如果使用grpc
原生支持:
func main() {
var clientConf zrpc.RpcClientConf
conf.MustLoad("grpc-client/etc/client.yaml", &clientConf)
//conn := zrpc.MustNewClient(clientConf)
conn, err := grpc.NewClient("etcd://127.0.0.1:2379/greet.rpc", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
client := greet.NewGreetClient(conn)
resp, err := client.Ping(context.Background(), &greet.Request{Ping: "ping"})
if err != nil {
log.Fatal(err)
return
}
log.Println(resp)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16