RPC基础

前言

RPC(Remote Procedure Call) 远程过程调用。RPC是指计算机A上的进程,调用另外一台计算机B上的进程,其中A上的调用进程被挂起,而B上的被调用进程开始执行,当值返回给A时,A进程继续执行。调用方可以通过使用参数将信息传送给被调用方,而后可以通过传回的结果得到信息。而这一过程,对于开发人员来说是透明的。

场景

大中型互联网公司分布式内部互相调用不同的服务

主要流程

  1. 客户端调用以本地方式调用服务
  2. client stub(存根)接收调用后负责将方法、参数等组装成能够进行网络传输的消息体
  3. client stub找到服务地址,发送给服务端
  4. server stub收到消息进行解码
  5. server stub根据解码结果调用本地服务
  6. 本地服务执行并将结果返回server stub
  7. server stub将返回结果打包成消息发送给客户端
  8. 客户端接收到消息解码,得到最终结果

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# protos/helloword.proto

syntax = "proto3";

message HelloRequest { # 定义消息体
string name = 1; # 1是分配标识符
}

message HelloReply {
string message = 1;
}

service Greeter { # 定义服务
rpc SayHello(HelloRequest) returns (HelloReply) {}
}

python使用

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
# 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使用

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 安装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

----------本文完,感谢您的阅读----------