前言
RPC(Remote Procedure Call) 远程过程调用。RPC是指计算机A上的进程,调用另外一台计算机B上的进程,其中A上的调用进程被挂起,而B上的被调用进程开始执行,当值返回给A时,A进程继续执行。调用方可以通过使用参数将信息传送给被调用方,而后可以通过传回的结果得到信息。而这一过程,对于开发人员来说是透明的。
场景
大中型互联网公司分布式内部互相调用不同的服务
主要流程
- 客户端调用以本地方式调用服务
- client stub(存根)接收调用后负责将方法、参数等组装成能够进行网络传输的消息体
- client stub找到服务地址,发送给服务端
- server stub收到消息进行解码
- server stub根据解码结果调用本地服务
- 本地服务执行并将结果返回server stub
- server stub将返回结果打包成消息发送给客户端
- 客户端接收到消息解码,得到最终结果
RPC和REST区别
REST要求把应用定义为资源,对资源对象操作,采用http,有get等动词,其主体是资源,是个名词。 RPC是把本地函数映射到api,其主体是动作,是个动词。RCP接口调用通常包含两个部分,序列化,比如json,xml,protobuf,thrift,bytes等,通信协议,比如tcp等。 一般内部采用RPC,外部采用REST。
gRPC
gRPC是Google开源的高性能、通用的RPC框架,面向移动和http/2设计。 其主要使用流程是:
- 定义protobuf,protobuf是Google定义的数据传输格式,有proto2和proto3版本,参考
- 使用protoc(protobuf编译器),将proto文件编译成不容语言的实现,生成访问类
- 在项目中继承
grpc有4种通信方式 数据源:json格式,存储很多地点,每个地点有经纬度和地名组成
- 客户端请求一次,服务器应答一次,请求一个地点是否在数据源中
- 请求一次,多次应答(流式),客户端指定矩形,服务器返回这个范围内的地点
- 流式,一次,客户端发送多个地点,服务器返回汇总信息
- 流式,流式,客户端和服务器使用地点聊天
定义proto
# protos/helloword.proto
syntax = "proto3";
message HelloRequest { # 定义消息体
string name = 1; # 1是分配标识符
}
message HelloReply {
string message = 1;
}
service Greeter { # 定义服务
rpc SayHello(HelloRequest) returns (HelloReply) {}
}
python使用
# pip install grpcio && pip install grpcio-tools 编译器
# 编译proto文件
# python -m grpc_tools.protoc
# --python_out=. 编译生成protobuf相关代码的路径,这里是当前目录
# --grpc_python_out=. 编译生成grpc相关的代码的路径,这里是当前目录
# -I../protos helloworld.proto proto文件的路径,然后指定proto文件
# 编译会生成helloworld_pb2.py helloworld_pb2_grpc.py
# 然后根据生成的grpc类,实现项目集成
# 服务器端
class Greeter(helloworld_pb2_grpc.GreeterServicer):
# 实现 proto 文件中定义的 rpc 调用
def SayHello(self, request, context):
return helloworld_pb2.HelloReply(message = 'hello {msg}'.format(msg = request.name))
def serve():
# 启动 rpc 服务
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port('[::]:50051')
server.start()
try:
while True:
time.sleep(60*60*24)
except KeyboardInterrupt:
server.stop(0)
# 客户端
def run():
# 连接 rpc 服务器
channel = grpc.insecure_channel('localhost:50051')
# 调用 rpc 服务
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='wcg'))
print("Greeter client received: " + response.message)
go使用
// 安装grpc https://blog.csdn.net/cjj198561/article/details/78133193
// protoc -I../protos helloworld.proto --go_out=plugins=grpc:.
// 服务端
package main
import (
"log"
"net"
pb "gRPC-demo/go"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
const (
port = ":50051"
)
// server is used to implement helloworld.GreeterServer.
type server struct{}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
// Register reflection service on gRPC server.
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
// 客户端
package main
import (
"log"
"os"
pb "gRPC-demo/go"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
address = "localhost:50051"
defaultName = "wcg"
)
func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
}
Demo地址
https://github.com/itswcg/gRPC-demo
参考
https://grpc.io/docs/guides/
https://waylau.com/remote-procedure-calls/
https://www.cnblogs.com/LBSer/p/4853234.html
https://www.jianshu.com/p/43fdfeb105ff