我正在参加「掘金·启航计划」
前言
最近在看有关微服务的内容,今天想总结一下有关gRPC的知识.下面的图很好的解释了gRPC的特点,服务端负责实现接口来处理客户端的请求,客户端直接调用需要的服务就行.最突出的特点就是gRPC的服务端和客户端可以支持不同语言实现,也就是说不再有语言的障碍会啥用啥就好了(像其它一些RPC框架比如Dubbo就只支持Java而不能跨语言通信)
1. gRPC与ProtoBuf
如果对gRPC不熟悉可以先去看看gRPC官网的介绍
默认情况下,gRPC是使用Protocol Buffers来传递数据的,比起xml或者json更加轻量(二进制格式而不是文本格式).在这里关于ProtoBuf和gRPC的安装配置我就不多赘述,可以参考其他博客.
常规的步骤也很简单,根据官网的教程首先使用protobuf定义好服务,然后使用protoc
生成各自语言的proto文件进行调用实现不同语言的服务端与客户端.
2. gRPC的适用场景
- 分布式: gRPC的低延迟和高吞吐非常适合分布式微服务
- 多语言开发: gRPC支持多种语言,因此适合不同语言的工程师们混合开发
- 轻量化消息: gRPC发送的消息是ProtoBuf二进制,比普通的二进制格式消息有天然优势
3. Demo
我平时常用的几种语言就是C++ Go Python
,不过如果做服务用C++
还是比较少的,所以今天尝试一下用Go
和Python
分别写服务端和客户端进行通信.
3.1 Proto定义及转换
syntax="proto3";
option go_package="./;hello";
package hello;
service Greeter{
rpc SayHello(HelloRequest) returns (HelloReply){}
}
message HelloRequest{
string name = 1;
}
message HelloReply{
string message = 1;
}
这里定义了Greeter
这个服务以及服务中传递的消息,接下来需要转换为两种语言的代码
- Go
protoc --proto_path=../protos --go_out=plugins=grpc:. hello.proto
protoc --proto_path=../protos --go_out=. --go-grpc_out=. hello.proto
- Python
pip install grpcio grpcio-tools
python -m grpc_tools.protoc -I ./protos --python_out=. --pyi_out=. --grpc_python_out=. ./protos/hello.proto
3.2 服务端部分Go实现
package main
import (
pb "PRC_learning/hello"
"context"
"flag"
"fmt"
"google.golang.org/grpc"
"log"
"net"
)
type server struct{ pb.UnimplementedGreeterServer }
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
var port = flag.Int("port", 50001, "The Server Port")
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
if err != nil {
log.Fatal("fail to listen :%v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("fail to serve:%v", err)
}
}
flag可以通过命令行解析设置服务的端口号,这里ip我设置的是本地.这里服务主要是实现hello_grpc.pb.go
中的接口
3.3 客户端部分Python实现
import grpc
import hello_pb2
import hello_pb2_grpc
def run():
with grpc.insecure_channel(target='localhost:50001',
options=[
('grpc.lb_policy_name','pick_first'),
('grpc.enable_retries',0),
('grpc.keepalive_timeout_ms',1000)
]) as channel:
stub=hello_pb2_grpc.GreeterStub(channel)
response=stub.SayHello(hello_pb2.HelloRequest(name='xxx'),timeout=10)
print(f"Greeter client received:{response.message}")
if __name__ == '__main__':
run()
关注于run函数部分,除了为了通信设置相同的端口,更应该注意到SayHello
这个方法.这个Demo是一元的消息(还有流式的消息等形式)所以对应unary_unary
这个方法
需要传入某个方法,请求的编码序列化以及回应的解序列化.上面那张图就实现了这三个参数
最后返回响应,输出响应值中的信息就完成了一次通信
3.4 运行
首先运行服务端 go run server.go
然后运行客户端 python client.py