美文网首页
gRPC实践--Client and Server

gRPC实践--Client and Server

作者: 枕溪研书 | 来源:发表于2021-04-26 15:39 被阅读0次

    gRPC实践:Server&Client

    前言

    在gRPC中,客户端应用程序可以直接在其他计算机上的服务器应用程序上调用方法,就好像它是本地对象一样,从而使您更轻松地创建分布式应用程序和服务。

    与许多RPC系统一样,gRPC围绕定义服务的思想,指定可通过其参数和返回类型远程调用的方法。 在服务器端,服务器实现此接口并运行gRPC服务器以处理客户端调用。 在客户端,客户端具有一个存根(在某些语言中仅称为客户端),提供与服务器相同的方法。

    例如,你可以使用Go,Python或Ruby的客户端轻松地用 Java 创建 gRPC 服务器。

    前提知识点:

    • protocol buffers
    • RPC

    更多详情,可以参考官网:https://www.grpc.io/docs/

    安装

    假设当前已经按照了protocol buffers 的编译器,如果没有安装,请参照:

    gRPC

    go get -u google.golang.org/grpc
    

    安装包拉去成功一般会这样:

    go: downloading google.golang.org/grpc v1.33.2
    go: downloading github.com/google/go-cmp v0.5.0
    go: downloading google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
    go: downloading golang.org/x/net v0.0.0-20190311183353-d8887717615a
    go: downloading golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
    go: downloading golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
    

    这里可能会下载失败,可以被长城了,可以换国内的 go mod 加速源:

    https://mirrors.aliyun.com/goproxy/
    ## 即,set GOPROXY=https://mirrors.aliyun.com/goproxy/
    

    或者参考:

    protoc-gen-go

    本文以 go 语言为示例,所以 proto 编译器还需要安装 protoc-gen-go

    go get -u github.com/golang/protobuf/protoc-gen-go
    

    gRPC实践

    目录结构

    λ tree /F
    D:.
    │  go.mod
    │  go.sum
    ├─client
    │      client.go
    ├─proto
    │      helloworld.pb.go
    │      helloworld.proto
    └─server
            server.go
    

    IDL存根

    定义服务

    定义服务的方法,如下所示:

    image

    下面展示一个写好的.proto 文件:

    image

    编译

    编译成.pb.go 文件

    protoc --go_out=plugins=grpc:. helloworld.proto
    
    • --go_out,输出 golang 代码
    • plugins=grpc,proto默认是不会生成 RPC 代码的,需要指定插件,不指定则不会生成Service的接口
    • :,分隔符
    • .,表示文件输出路径,我指定输出到当前目录,当然你也可以指定目录
    • helloworld.proto,表示需要转换的proto 文件路径,我指定的是当前目录下的helloworld.proto文件

    编译后

    让我们查看生成的文件,其实,是一般RPC代码的一种自动化封装。

    client
    • 结构体greeterClient
    • 返回对象NewGreeterClient
    • 实现接口里的方法SayHello
    // greeterClient结构体
    type greeterClient struct {...}
    // 返回一个新的客户端对象
    func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {...}
    // 实现接口
    func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {...}
    
    server
    • 结构体UnimplementedGreeterServer
    • 绑定的方法SayHello
    • 服务注册RegisterGreeterServer
    // server API 接口
    type GreeterServer interface {...}
    // UnimplementedGreeterServer结构体,可扩展
    type UnimplementedGreeterServer struct {...}
    // 绑定到“类”的方法
    func (*UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {...}
    // 注册服务
    func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {...}
    

    更详细的说明,可以参考:https://segmentfault.com/a/1190000016496165

    服务端

    参考官方示例:https://github.com/grpc/grpc-go/tree/master/examples

    可知,实现 gRPC 服务,一般是通过一下几步实现:

    • 初始化一个gRPC的结构体对象
    • 实现服务方法,并注册服务
    • 设置监听,指定IPport
    • 启动服务
    package main
    
    import (
        "context"
        pb "gRPC_example/proto"  //引入生成的代码
        "google.golang.org/grpc"
        "log"
        "net"
    )
    
    const (
        port = ":50051"
    )
    
    // server 用于实现 helloworld.GreeterServer接口
    type server struct{}
    
    // SayHello 实现 helloworld.GreeterServer 接口里的方法
    func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
        log.Printf("Received: %v", in.GetName())
        return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
    }
    
    func main() {
        // 初始化一个gRPC的结构体对象
        s := grpc.NewServer()
        // 注册服务
        pb.RegisterGreeterServer(s, &server{})
        // 设置监听,指定IP 和 port
        lis, err := net.Listen("tcp", port)
        if err != nil {
            log.Fatalf("failed to listen: %v", err)
        }
        // 启动服务
        if err := s.Serve(lis); err != nil {
            log.Fatalf("failed to serve: %v", err)
        }
    }
    

    客户端

    gRPC 客户端的一般操作流程是:

    • 连接 gRPC 服务器
    • 初始化gRPC 客户端
    • 输入参数,通过服务名称调用gRPC服务
    • 获得响应结果
    package main
    
    import (
        "context"
        "log"
        "os"
        "time"
        pb "gRPC_example/proto"
        "google.golang.org/grpc"
    )
    
    const (
        address     = "localhost:50051"
        defaultName = "world"
    )
    
    func main() {
        // 连接 gRPC 服务器
        conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
        if err != nil {
            log.Fatalf("did not connect: %v", err)
        }
        defer conn.Close()
        //初始化gRPC 客户端
        c := pb.NewGreeterClient(conn)
        // 输入参数
        name := defaultName
        if len(os.Args) > 1 { //如果外部没有接收到参数,则使用默认参数
            name = os.Args[1]
        }
        // 设置超时
        ctx, cancel := context.WithTimeout(context.Background(), time.Second)
        defer cancel()
        // 调用gRPC服务
        r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
        if err != nil {
            log.Fatalf("could not greet: %v", err)
        }
        log.Printf("Greeting: %s", r.GetMessage())
    }
    

    结果验证

    启动服务端:

    go run server.go
    

    启动客户端:

    go run client.go [参数,可选]
    

    结果如下:

    image

    总结

    这篇博客中,介绍了gRPC的基本实现步骤,演示了如何使用golang的demo,实现一个简单的gRPC远程过程调用服务。总的来说,一般的步骤为:

    1. 根据需求,定义服务,写成.proto文件,作为IDL存根;
    2. 编译proto文件,成对应的语言;
    3. 根据第二步,写服务。

    相关文章

      网友评论

          本文标题:gRPC实践--Client and Server

          本文链接:https://www.haomeiwen.com/subject/pijerltx.html