前言

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

# 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