了解gRPC

RPC是一种远程调用的方法,可以在不同的机器上调用函数,但是调用者不需要知道函数在哪个机器上,只需要知道函数名和参数即可。

gRPC是RPC框架,接口描述语言是Protobuf(一种IDL)

1.简略一元RPC方式

  • 首先写Proto文件,定义函数名和参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    syntax = "proto3";

    package helloWorld;

    option go_package = "./proto";

    service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
    }

    message HelloRequest {
    string name = 1;
    }

    message HelloReply {
    string message = 1;
    }

    本Proto文件首先声明了使用proto3语法,定义了Greeter的RPC服务,
    其中RPC的方法为SayHello,入参为消息体HelloRequest,出参为HelloReply,
    还定义了这两个消息体。

  • Server部分

    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
    var port string

    func init() {
    flag.StringVar(&port, "port", "8080", "port to listen on")
    flag.Parse()
    }

    type server struct {
    pb.UnimplementedGreeterServer
    }

    func (s *server) SayHello(ctx context.Context, r *pb.HelloRequest) (*pb.HelloReply, error) {
    fmt.Println(r.Name)
    return &pb.HelloReply{Message: "Hello " + r.Name}, nil
    }

    func main() {
    //生成一个grpc服务器
    s := grpc.NewServer()
    //注入实例
    pb.RegisterGreeterServer(s, &server{})

    lis, _ := net.Listen("tcp", ":"+port)
    err := s.Serve(lis)
    if err != nil {
    panic(err)
    }
    }
  • 创建了grpc Server端的抽象对象
  • 然后将server(包含服务端接口)注入到s中
  • 创建Listen监听TCP
  • s.Serve()开启服务
  • Client部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    var port string

    func init() {
    flag.StringVar(&port, "port", "8080", "port to listen on")
    flag.Parse()
    }

    func SayHello(client pb.GreeterClient) error {
    resq, _ := client.SayHello(context.TODO(), &pb.HelloRequest{Name: "world"})
    fmt.Println(resq.Message)
    return nil
    }

    func main() {
    //创建与服务端的连接句柄
    conn, _ := grpc.Dial("127.0.0.1:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
    defer conn.Close()
    //创建客户端对象
    client := pb.NewGreeterClient(conn)
    //发出请求
    _ = SayHello(client)
    }
  • 创建与服务端的连接句柄
  • 创建客户端对象
  • 发出请求

这样一元RPC就完成啦,分别配置好server(创建服务器,注入接口,启动监听开启服务器),client(连接句柄,创建客户端对象,发出请求)

2.流式RPC方式

由于一元rpc可能会出现数据包过大造成瞬时压力,需要等待所有数据报接受成功才响应的问题,出现流式rpc

流式RPC使用stream关键字,分为客户端流式和服务端流式,还有双向流式

直接说双向流式

  • 首先写Proto文件,定义函数名和参数

    1
    rpc SayRoute (stream HelloRequest) returns (stream HelloReply) {}
  • Server部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    func (s *server) SayRoute(stream pb.Greeter_SayRouteServer) error {
    n := 0
    for {
    _ = stream.Send(&pb.HelloReply{Message: "hi Route"})
    r, err := stream.Recv()
    if err == io.EOF {
    return nil
    }
    if err != nil {
    return err
    }
    n++
    println(r.Name, n)
    }
    }
  • Client部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    func SayRoute(client pb.GreeterClient, r *pb.HelloRequest) error {
    stream, _ := client.SayRoute(context.Background())
    for i := 0; i < 10; i++ {
    _ = stream.Send(r)

    fmt.Println("1")
    resp, err := stream.Recv()

    if err == io.EOF {
    break
    }
    if err != nil {
    return err
    }
    fmt.Println(resp.Message)
    }
    return nil
    }

    stream.Send()和stream.Recv()分别是发送和接受数据,这里的消息体是HelloRequest和HelloReply。
    stream.Send()和stream.Recv()是阻塞的,所以需要在另一个goroutine中执行

服务端流式类似,服务器使用stream.Send()发送数据,客户端使用stream.Recv()接受数据

客户端流式,客户端不断使用stream.Send()最后使用stream.CloseAndRecv(),服务器使用Recv()和SendAndRecv()

gRPC暂时先写到这啦,现在对rpc没有什么需求,大致了解一下