gRPC极速入门

  1. gRPC是什么?
  2. 简单示例
  • Proto定义
    1. 生成 gRPC代码
    2. 服务端代码
    3. 客户端代码
    4. 执行效果
  • gRPC是什么?

    gRPC是什么可以用官网的一句话来概括:

    A high-performance, open-source universal RPC framework

    所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型。使用的时候客户端调用server端提供的接口就像是调用本地的函数一样。如下图引用自官方网站:

    gRPC

    gRPC使用protocol buffers作为接口描述语言(IDL)以及底层的信息交换格式,一般情况下推荐使用 proto3因为其能够支持更多的语言,并减少一些兼容性的问题。

    简单示例

    // 定义了一个服务 Greeter
    service Greeter {
      // 定义方法 SayHello
      rpc SayHello (HelloRequest) returns (HelloReply) {}
    }
    
    // request
    message HelloRequest {
      string name = 1;
    }
    
    // response
    message HelloReply {
      string message = 1;
    }
    

    gRPC使用 protoc 和一个插件来将proto定义的内容生成客户端/服务端的代码。

    Proto定义

    syntax = "proto3";
    
    package Service;
    
    option go_package = "./Service";
    
    // The greeting service definition.
    service Greeter {
      // Sends a greeting
      rpc SayHello (HelloRequest) returns (HelloReply) {}
    }
    
    // The request message containing the user's name.
    message HelloRequest {
      string name = 1;
    }
    
    // The response message containing the greetings
    message HelloReply {
      string message = 1;
    }
    
    
    
    

    生成 gRPC代码

    protoc -I. --go_out=plugins=grpc:. demo.proto
    

    生成如下go代码:

    // Code generated by protoc-gen-go. DO NOT EDIT.
    // source: demo.proto
    
    package Service
    
    import (
        context "context"
        fmt "fmt"
        proto "github.com/golang/protobuf/proto"
        grpc "google.golang.org/grpc"
        codes "google.golang.org/grpc/codes"
        status "google.golang.org/grpc/status"
        math "math"
    )
    
    // Reference imports to suppress errors if they are not otherwise used.
    var _ = proto.Marshal
    var _ = fmt.Errorf
    var _ = math.Inf
    
    // This is a compile-time assertion to ensure that this generated file
    // is compatible with the proto package it is being compiled against.
    // A compilation error at this line likely means your copy of the
    // proto package needs to be updated.
    const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
    
    // The request message containing the user's name.
    type HelloRequest struct {
        Name                 string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
        XXX_NoUnkeyedLiteral struct{} `json:"-"`
        XXX_unrecognized     []byte   `json:"-"`
        XXX_sizecache        int32    `json:"-"`
    }
    
    func (m *HelloRequest) Reset()         { *m = HelloRequest{} }
    func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
    func (*HelloRequest) ProtoMessage()    {}
    func (*HelloRequest) Descriptor() ([]byte, []int) {
        return fileDescriptor_ca53982754088a9d, []int{0}
    }
    
    func (m *HelloRequest) XXX_Unmarshal(b []byte) error {
        return xxx_messageInfo_HelloRequest.Unmarshal(m, b)
    }
    func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
        return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic)
    }
    func (m *HelloRequest) XXX_Merge(src proto.Message) {
        xxx_messageInfo_HelloRequest.Merge(m, src)
    }
    func (m *HelloRequest) XXX_Size() int {
        return xxx_messageInfo_HelloRequest.Size(m)
    }
    func (m *HelloRequest) XXX_DiscardUnknown() {
        xxx_messageInfo_HelloRequest.DiscardUnknown(m)
    }
    
    var xxx_messageInfo_HelloRequest proto.InternalMessageInfo
    
    func (m *HelloRequest) GetName() string {
        if m != nil {
            return m.Name
        }
        return ""
    }
    
    // The response message containing the greetings
    type HelloReply struct {
        Message              string   `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
        XXX_NoUnkeyedLiteral struct{} `json:"-"`
        XXX_unrecognized     []byte   `json:"-"`
        XXX_sizecache        int32    `json:"-"`
    }
    
    func (m *HelloReply) Reset()         { *m = HelloReply{} }
    func (m *HelloReply) String() string { return proto.CompactTextString(m) }
    func (*HelloReply) ProtoMessage()    {}
    func (*HelloReply) Descriptor() ([]byte, []int) {
        return fileDescriptor_ca53982754088a9d, []int{1}
    }
    
    func (m *HelloReply) XXX_Unmarshal(b []byte) error {
        return xxx_messageInfo_HelloReply.Unmarshal(m, b)
    }
    func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
        return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic)
    }
    func (m *HelloReply) XXX_Merge(src proto.Message) {
        xxx_messageInfo_HelloReply.Merge(m, src)
    }
    func (m *HelloReply) XXX_Size() int {
        return xxx_messageInfo_HelloReply.Size(m)
    }
    func (m *HelloReply) XXX_DiscardUnknown() {
        xxx_messageInfo_HelloReply.DiscardUnknown(m)
    }
    
    var xxx_messageInfo_HelloReply proto.InternalMessageInfo
    
    func (m *HelloReply) GetMessage() string {
        if m != nil {
            return m.Message
        }
        return ""
    }
    
    func init() {
        proto.RegisterType((*HelloRequest)(nil), "Service.HelloRequest")
        proto.RegisterType((*HelloReply)(nil), "Service.HelloReply")
    }
    
    func init() { proto.RegisterFile("demo.proto", fileDescriptor_ca53982754088a9d) }
    
    var fileDescriptor_ca53982754088a9d = []byte{
        // 151 bytes of a gzipped FileDescriptorProto
        0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x49, 0xcd, 0xcd,
        0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x0f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x55,
        0x52, 0xe2, 0xe2, 0xf1, 0x48, 0xcd, 0xc9, 0xc9, 0x0f, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11,
        0x12, 0xe2, 0x62, 0xc9, 0x4b, 0xcc, 0x4d, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0xb3,
        0x95, 0xd4, 0xb8, 0xb8, 0xa0, 0x6a, 0x0a, 0x72, 0x2a, 0x85, 0x24, 0xb8, 0xd8, 0x73, 0x53, 0x8b,
        0x8b, 0x13, 0xd3, 0x61, 0x8a, 0x60, 0x5c, 0x23, 0x67, 0x2e, 0x76, 0xf7, 0xa2, 0xd4, 0xd4, 0x92,
        0xd4, 0x22, 0x21, 0x0b, 0x2e, 0x8e, 0xe0, 0xc4, 0x4a, 0xb0, 0x2e, 0x21, 0x51, 0x3d, 0xa8, 0x65,
        0x7a, 0xc8, 0x36, 0x49, 0x09, 0xa3, 0x0b, 0x17, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0x71, 0x47, 0x71,
        0xea, 0xe9, 0x43, 0x65, 0x92, 0xd8, 0xc0, 0xae, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xa1,
        0x4c, 0x5f, 0x91, 0xbb, 0x00, 0x00, 0x00,
    }
    
    // Reference imports to suppress errors if they are not otherwise used.
    var _ context.Context
    var _ grpc.ClientConn
    
    // This is a compile-time assertion to ensure that this generated file
    // is compatible with the grpc package it is being compiled against.
    const _ = grpc.SupportPackageIsVersion4
    
    // GreeterClient is the client API for Greeter service.
    //
    // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
    type GreeterClient interface {
        // Sends a greeting
        SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
    }
    
    type greeterClient struct {
        cc *grpc.ClientConn
    }
    
    func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
        return &greeterClient{cc}
    }
    
    func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
        out := new(HelloReply)
        err := c.cc.Invoke(ctx, "/Service.Greeter/SayHello", in, out, opts...)
        if err != nil {
            return nil, err
        }
        return out, nil
    }
    
    // GreeterServer is the server API for Greeter service.
    type GreeterServer interface {
        // Sends a greeting
        SayHello(context.Context, *HelloRequest) (*HelloReply, error)
    }
    
    // UnimplementedGreeterServer can be embedded to have forward compatible implementations.
    type UnimplementedGreeterServer struct {
    }
    
    func (*UnimplementedGreeterServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) {
        return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
    }
    
    func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
        s.RegisterService(&_Greeter_serviceDesc, srv)
    }
    
    func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
        in := new(HelloRequest)
        if err := dec(in); err != nil {
            return nil, err
        }
        if interceptor == nil {
            return srv.(GreeterServer).SayHello(ctx, in)
        }
        info := &grpc.UnaryServerInfo{
            Server:     srv,
            FullMethod: "/Service.Greeter/SayHello",
        }
        handler := func(ctx context.Context, req interface{}) (interface{}, error) {
            return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
        }
        return interceptor(ctx, in, info, handler)
    }
    
    var _Greeter_serviceDesc = grpc.ServiceDesc{
        ServiceName: "Service.Greeter",
        HandlerType: (*GreeterServer)(nil),
        Methods: []grpc.MethodDesc{
            {
                MethodName: "SayHello",
                Handler:    _Greeter_SayHello_Handler,
            },
        },
        Streams:  []grpc.StreamDesc{},
        Metadata: "demo.proto",
    }
    

    服务端代码

    package main
    
    import (
        "log"
        "net"
    
        "golang.org/x/net/context"
        "google.golang.org/grpc"
        "google.golang.org/grpc/reflection"
        pb "grpcDemo/Service"
    )
    
    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) {
        log.Printf("get client msg: %s \n",in.Name)
        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"
        "time"
    
        "golang.org/x/net/context"
        "google.golang.org/grpc"
        pb "grpcDemo/Service"
    )
    
    const (
        address     = "localhost:50051"
        defaultName = "world"
    )
    
    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]
        }
        ctx, cancel := context.WithTimeout(context.Background(), time.Second)
        defer cancel()
        r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
        if err != nil {
            log.Fatalf("could not greet: %v", err)
        }
        log.Printf("Greeting: %s", r.Message)
    }
    

    执行效果

    gRPC


    转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 irvin.em@live.com。

    文章标题:gRPC极速入门

    文章字数:1.5k

    本文作者:dino

    发布时间:2019-11-18, 11:02:41

    最后更新:2019-11-18, 14:47:55

    原始链接:https://blog.walkbc.com/2019/11/18/gRPC/

    版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

    QQ交流群:273078549

    目录