Go语言与gRpc

1. gRpc 概述

gRpc是什么 ?

gRPC是Google开源的可以在任何环境中运行的现代开源高性能RPC框架。它可以通过可插拔的支持来有效地连接数据中心内和跨数据中心的服务,以实现负载平衡,跟踪,健康检查和身份验证。它也适用于分布式计算的最后一英里,以将设备,移动应用程序和浏览器连接到后端服务。

主要使用场景

  1. 在微服务风格架构中有效连接多语种服务
  2. 将移动设备,浏览器客户端连接到后端服务
  3. 生成高效的客户端库

gRpc官方地址

https://grpc.io/

gRpc源码托管地址

https://github.com/grpc/grpc

gRpc支持我们常见的编程语言(C++ java Python Go Ruby C# Node.js PHP Dart Objective-C ) ,这些编程语言基本都有对gRpc的实现

详情可以参看 https://grpc.io/docs/

2. gRpc执行概述

在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。

grpc.png

通过任意编程语言创建的gRpc服务端和客户端可以运行在多种环境中,创建的gRpc服务端,可以通过任意编程语言编写的客户端调用

gRpc默认使用protocol buffers 对数据进行序列化

关于Protobuf的详情,请参看 https://developers.google.com/protocol-buffers

3. gRpc-go 安装

gRpc 有多种语言的实现

  • C++: follow the instructions under the src/cpp directory
  • C#: NuGet package Grpc
  • Dart: pub package grpc
  • Go: go get google.golang.org/grpc
  • Java: Use JARs from Maven Central Repository
  • Node: npm install grpc
  • Objective-C: Add gRPC-ProtoRPC dependency to podspec
  • PHP: pecl install grpc
  • Python: pip install grpcio
  • Ruby: gem install grpc
  • WebJS: follow the grpc-web instructions
Language Source
Shared C [core library] src/core
C++ src/cpp
Ruby src/ruby
Python src/python
PHP src/php
C# (core library based) src/csharp
Objective-C src/objective-c
Java grpc-java
Go grpc-go
NodeJS grpc-node
WebJS grpc-web
Dart grpc-dart
.NET (pure C# impl.) grpc-dotnet

grpc- go 是gRpc库的Golang 实现版本,也是我们需要安装的版本(根据自己的开发语言选择安装)

安装细节

进入GOPATH目录下执行如下命令

go get -u google.golang.org/grpc

不出意外的话会安装失败,报错信息如下:

package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)

这怎么办呢?来来换个姿势继续操作

下载 grpc-go 的源码包和依赖包

git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

下载完成之后 ,执行安装命令

cd $GOPATH/src
go install google.golang.org/grpc

因为grpc默认使用Protocol buffers

所以必须安装 Protocol Buffers 编译器,安装步骤省略

$ protoc --version
libprotoc 3.11.0

我们开发语言是Golang,所以也需要安装protobuf文件的Go语言代码生成插件

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

4. gRpc使用

上面的基础环境具备之后,我们就可以快乐的使用gRpc开发服务了

我们梳理一下gRpc框架使用的最基础的流程:

  1. 编写Protobuf 文件,在Protobuf文件中定义服务和接口

  2. 自动生成Go语言代码.

    这里主要是指安装的protoc-gen-go 插件然后指定参数生成兼容gRpc框架的Go语言代码

  3. 服务接口的实现

  4. gRpc服务端实现

  5. gRpc客户端实现

在gRpc安装完成之后会有如下的一个目录存在

$GOPATH/src/google.golang.org/grpc/examples

gRpc的很多功能的使用示例都在其中,我们学习gRpc可以多看看这个目录中的文件

step1 : 编写一个.proto 文件,文件名自己拟定

例如: demo5.proto

syntax = "proto3";
package example;
// 添加服务
service Demo5 {
    // 定义服务接口
    rpc GetInfo (Demo5Request) returns (Demo5Response) {
    }
    rpc SetName (Demo5Request) returns (Demo5Response) {
    }
}
message Demo5Request {
    string name = 1;
    int32 age = 2;
    enum Gender {
        MALE = 0;
        FEMALE = 1;
    }
    message Other {
        string addr = 1;
        string hobby = 2;
        Gender g = 3;
    }
    Other info = 3;
}
message Demo5Response {
    string info = 1;
}

step2 : 自动生成Go语言代码

执行完下面的命令,将生成代码文件 demo5.pb.go

 protoc --go_out=plugins=grpc:. example/demo5.proto

demo5.pb.go 具体内容如下(在开发中不太会去关注其中的内容)

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: example/demo5.proto

package example

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

type Demo5Request_Gender int32

const (
    Demo5Request_MALE   Demo5Request_Gender = 0
    Demo5Request_FEMALE Demo5Request_Gender = 1
)

var Demo5Request_Gender_name = map[int32]string{
    0: "MALE",
    1: "FEMALE",
}

var Demo5Request_Gender_value = map[string]int32{
    "MALE":   0,
    "FEMALE": 1,
}

func (x Demo5Request_Gender) String() string {
    return proto.EnumName(Demo5Request_Gender_name, int32(x))
}

func (Demo5Request_Gender) EnumDescriptor() ([]byte, []int) {
    return fileDescriptor_dc43dfb84d83bd6d, []int{0, 0}
}

type Demo5Request struct {
    Name                 string              `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
    Age                  int32               `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
    Info                 *Demo5Request_Other `protobuf:"bytes,3,opt,name=info,proto3" json:"info,omitempty"`
    XXX_NoUnkeyedLiteral struct{}            `json:"-"`
    XXX_unrecognized     []byte              `json:"-"`
    XXX_sizecache        int32               `json:"-"`
}

func (m *Demo5Request) Reset()         { *m = Demo5Request{} }
func (m *Demo5Request) String() string { return proto.CompactTextString(m) }
func (*Demo5Request) ProtoMessage()    {}
func (*Demo5Request) Descriptor() ([]byte, []int) {
    return fileDescriptor_dc43dfb84d83bd6d, []int{0}
}

func (m *Demo5Request) XXX_Unmarshal(b []byte) error {
    return xxx_messageInfo_Demo5Request.Unmarshal(m, b)
}
func (m *Demo5Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    return xxx_messageInfo_Demo5Request.Marshal(b, m, deterministic)
}
func (m *Demo5Request) XXX_Merge(src proto.Message) {
    xxx_messageInfo_Demo5Request.Merge(m, src)
}
func (m *Demo5Request) XXX_Size() int {
    return xxx_messageInfo_Demo5Request.Size(m)
}
func (m *Demo5Request) XXX_DiscardUnknown() {
    xxx_messageInfo_Demo5Request.DiscardUnknown(m)
}

var xxx_messageInfo_Demo5Request proto.InternalMessageInfo

func (m *Demo5Request) GetName() string {
    if m != nil {
        return m.Name
    }
    return ""
}

func (m *Demo5Request) GetAge() int32 {
    if m != nil {
        return m.Age
    }
    return 0
}

func (m *Demo5Request) GetInfo() *Demo5Request_Other {
    if m != nil {
        return m.Info
    }
    return nil
}

type Demo5Request_Other struct {
    Addr                 string              `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"`
    Hobby                string              `protobuf:"bytes,2,opt,name=hobby,proto3" json:"hobby,omitempty"`
    G                    Demo5Request_Gender `protobuf:"varint,3,opt,name=g,proto3,enum=example.Demo5Request_Gender" json:"g,omitempty"`
    XXX_NoUnkeyedLiteral struct{}            `json:"-"`
    XXX_unrecognized     []byte              `json:"-"`
    XXX_sizecache        int32               `json:"-"`
}

func (m *Demo5Request_Other) Reset()         { *m = Demo5Request_Other{} }
func (m *Demo5Request_Other) String() string { return proto.CompactTextString(m) }
func (*Demo5Request_Other) ProtoMessage()    {}
func (*Demo5Request_Other) Descriptor() ([]byte, []int) {
    return fileDescriptor_dc43dfb84d83bd6d, []int{0, 0}
}

func (m *Demo5Request_Other) XXX_Unmarshal(b []byte) error {
    return xxx_messageInfo_Demo5Request_Other.Unmarshal(m, b)
}
func (m *Demo5Request_Other) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    return xxx_messageInfo_Demo5Request_Other.Marshal(b, m, deterministic)
}
func (m *Demo5Request_Other) XXX_Merge(src proto.Message) {
    xxx_messageInfo_Demo5Request_Other.Merge(m, src)
}
func (m *Demo5Request_Other) XXX_Size() int {
    return xxx_messageInfo_Demo5Request_Other.Size(m)
}
func (m *Demo5Request_Other) XXX_DiscardUnknown() {
    xxx_messageInfo_Demo5Request_Other.DiscardUnknown(m)
}

var xxx_messageInfo_Demo5Request_Other proto.InternalMessageInfo

func (m *Demo5Request_Other) GetAddr() string {
    if m != nil {
        return m.Addr
    }
    return ""
}

func (m *Demo5Request_Other) GetHobby() string {
    if m != nil {
        return m.Hobby
    }
    return ""
}

func (m *Demo5Request_Other) GetG() Demo5Request_Gender {
    if m != nil {
        return m.G
    }
    return Demo5Request_MALE
}

type Demo5Response struct {
    Info                 string   `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"`
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
}

func (m *Demo5Response) Reset()         { *m = Demo5Response{} }
func (m *Demo5Response) String() string { return proto.CompactTextString(m) }
func (*Demo5Response) ProtoMessage()    {}
func (*Demo5Response) Descriptor() ([]byte, []int) {
    return fileDescriptor_dc43dfb84d83bd6d, []int{1}
}

func (m *Demo5Response) XXX_Unmarshal(b []byte) error {
    return xxx_messageInfo_Demo5Response.Unmarshal(m, b)
}
func (m *Demo5Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    return xxx_messageInfo_Demo5Response.Marshal(b, m, deterministic)
}
func (m *Demo5Response) XXX_Merge(src proto.Message) {
    xxx_messageInfo_Demo5Response.Merge(m, src)
}
func (m *Demo5Response) XXX_Size() int {
    return xxx_messageInfo_Demo5Response.Size(m)
}
func (m *Demo5Response) XXX_DiscardUnknown() {
    xxx_messageInfo_Demo5Response.DiscardUnknown(m)
}

var xxx_messageInfo_Demo5Response proto.InternalMessageInfo

func (m *Demo5Response) GetInfo() string {
    if m != nil {
        return m.Info
    }
    return ""
}

func init() {
    proto.RegisterEnum("example.Demo5Request_Gender", Demo5Request_Gender_name, Demo5Request_Gender_value)
    proto.RegisterType((*Demo5Request)(nil), "example.Demo5Request")
    proto.RegisterType((*Demo5Request_Other)(nil), "example.Demo5Request.Other")
    proto.RegisterType((*Demo5Response)(nil), "example.Demo5Response")
}

func init() { proto.RegisterFile("example/demo5.proto", fileDescriptor_dc43dfb84d83bd6d) }

var fileDescriptor_dc43dfb84d83bd6d = []byte{
    // 271 bytes of a gzipped FileDescriptorProto
    0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0xad, 0x48, 0xcc,
    0x2d, 0xc8, 0x49, 0xd5, 0x4f, 0x49, 0xcd, 0xcd, 0x37, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
    0x62, 0x87, 0x0a, 0x2a, 0x3d, 0x61, 0xe4, 0xe2, 0x71, 0x01, 0x49, 0x04, 0xa5, 0x16, 0x96, 0xa6,
    0x16, 0x97, 0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70,
    0x06, 0x81, 0xd9, 0x42, 0x02, 0x5c, 0xcc, 0x89, 0xe9, 0xa9, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xac,
    0x41, 0x20, 0xa6, 0x90, 0x3e, 0x17, 0x4b, 0x66, 0x5e, 0x5a, 0xbe, 0x04, 0xb3, 0x02, 0xa3, 0x06,
    0xb7, 0x91, 0xb4, 0x1e, 0xd4, 0x38, 0x3d, 0x64, 0xa3, 0xf4, 0xfc, 0x4b, 0x32, 0x52, 0x8b, 0x82,
    0xc0, 0x0a, 0xa5, 0x62, 0xb9, 0x58, 0xc1, 0x5c, 0x90, 0xf9, 0x89, 0x29, 0x29, 0x45, 0x30, 0xf3,
    0x41, 0x6c, 0x21, 0x11, 0x2e, 0xd6, 0x8c, 0xfc, 0xa4, 0xa4, 0x4a, 0xb0, 0x0d, 0x9c, 0x41, 0x10,
    0x8e, 0x90, 0x16, 0x17, 0x63, 0x3a, 0xd8, 0x02, 0x3e, 0x23, 0x19, 0xec, 0x16, 0xb8, 0xa7, 0xe6,
    0xa5, 0xa4, 0x16, 0x05, 0x31, 0xa6, 0x2b, 0xc9, 0x71, 0xb1, 0x41, 0x38, 0x42, 0x1c, 0x5c, 0x2c,
    0xbe, 0x8e, 0x3e, 0xae, 0x02, 0x0c, 0x42, 0x5c, 0x5c, 0x6c, 0x6e, 0xae, 0x60, 0x36, 0xa3, 0x92,
    0x32, 0x17, 0x2f, 0x54, 0x67, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0x2a, 0xc8, 0x19, 0x60, 0x0f, 0x40,
    0x9d, 0x01, 0x62, 0x1b, 0xd5, 0x73, 0xb1, 0x82, 0x15, 0x09, 0x59, 0x71, 0xb1, 0xbb, 0xa7, 0x96,
    0x78, 0xe6, 0xa5, 0xe5, 0x0b, 0x89, 0x62, 0xb5, 0x59, 0x4a, 0x0c, 0x5d, 0x18, 0x62, 0xac, 0x12,
    0x03, 0x48, 0x6f, 0x70, 0x6a, 0x89, 0x1f, 0x28, 0xd8, 0x48, 0xd5, 0x9b, 0xc4, 0x06, 0x8e, 0x1c,
    0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x16, 0x42, 0x94, 0xe0, 0xb3, 0x01, 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

// Demo5Client is the client API for Demo5 service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type Demo5Client interface {
    GetInfo(ctx context.Context, in *Demo5Request, opts ...grpc.CallOption) (*Demo5Response, error)
    SetName(ctx context.Context, in *Demo5Request, opts ...grpc.CallOption) (*Demo5Response, error)
}

type demo5Client struct {
    cc *grpc.ClientConn
}

func NewDemo5Client(cc *grpc.ClientConn) Demo5Client {
    return &demo5Client{cc}
}

func (c *demo5Client) GetInfo(ctx context.Context, in *Demo5Request, opts ...grpc.CallOption) (*Demo5Response, error) {
    out := new(Demo5Response)
    err := c.cc.Invoke(ctx, "/example.Demo5/GetInfo", in, out, opts...)
    if err != nil {
        return nil, err
    }
    return out, nil
}

func (c *demo5Client) SetName(ctx context.Context, in *Demo5Request, opts ...grpc.CallOption) (*Demo5Response, error) {
    out := new(Demo5Response)
    err := c.cc.Invoke(ctx, "/example.Demo5/SetName", in, out, opts...)
    if err != nil {
        return nil, err
    }
    return out, nil
}

// Demo5Server is the server API for Demo5 service.
type Demo5Server interface {
    GetInfo(context.Context, *Demo5Request) (*Demo5Response, error)
    SetName(context.Context, *Demo5Request) (*Demo5Response, error)
}

// UnimplementedDemo5Server can be embedded to have forward compatible implementations.
type UnimplementedDemo5Server struct {
}

func (*UnimplementedDemo5Server) GetInfo(ctx context.Context, req *Demo5Request) (*Demo5Response, error) {
    return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented")
}
func (*UnimplementedDemo5Server) SetName(ctx context.Context, req *Demo5Request) (*Demo5Response, error) {
    return nil, status.Errorf(codes.Unimplemented, "method SetName not implemented")
}

func RegisterDemo5Server(s *grpc.Server, srv Demo5Server) {
    s.RegisterService(&_Demo5_serviceDesc, srv)
}

func _Demo5_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    in := new(Demo5Request)
    if err := dec(in); err != nil {
        return nil, err
    }
    if interceptor == nil {
        return srv.(Demo5Server).GetInfo(ctx, in)
    }
    info := &grpc.UnaryServerInfo{
        Server:     srv,
        FullMethod: "/example.Demo5/GetInfo",
    }
    handler := func(ctx context.Context, req interface{}) (interface{}, error) {
        return srv.(Demo5Server).GetInfo(ctx, req.(*Demo5Request))
    }
    return interceptor(ctx, in, info, handler)
}

func _Demo5_SetName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    in := new(Demo5Request)
    if err := dec(in); err != nil {
        return nil, err
    }
    if interceptor == nil {
        return srv.(Demo5Server).SetName(ctx, in)
    }
    info := &grpc.UnaryServerInfo{
        Server:     srv,
        FullMethod: "/example.Demo5/SetName",
    }
    handler := func(ctx context.Context, req interface{}) (interface{}, error) {
        return srv.(Demo5Server).SetName(ctx, req.(*Demo5Request))
    }
    return interceptor(ctx, in, info, handler)
}

var _Demo5_serviceDesc = grpc.ServiceDesc{
    ServiceName: "example.Demo5",
    HandlerType: (*Demo5Server)(nil),
    Methods: []grpc.MethodDesc{
        {
            MethodName: "GetInfo",
            Handler:    _Demo5_GetInfo_Handler,
        },
        {
            MethodName: "SetName",
            Handler:    _Demo5_SetName_Handler,
        },
    },
    Streams:  []grpc.StreamDesc{},
    Metadata: "example/demo5.proto",
}

step3 : gRpc服务端实现

具体服务的实现代码可以单独放在一个文件中,此处我们都放在了gRpc服务端代码文件中

文件名: grpc_demo5_server.go

package main

import (
    pb "GoNote/chapter10/demo9/example"
    "fmt"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    "log"
    "net"
)

// 实现服务
type Demo5Server struct {
    pb.UnimplementedDemo5Server
}
// 实现服务定义的接口
func (d *Demo5Server) GetInfo(ctx context.Context, in *pb.Demo5Request) (*pb.Demo5Response, error) {
    InfoS := fmt.Sprintf("my name is %s, i am %d, i live in %s", in.Name, in.Age, in.Info.Addr)
    return &pb.Demo5Response{Info: InfoS}, nil
}
func (d *Demo5Server) SetName(ctx context.Context, in *pb.Demo5Request) (*pb.Demo5Response, error) {
    return &pb.Demo5Response{Info: "getName"}, nil
}
func main() {
    // 监听8080端口
    listen, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal("failed to listen : ", err.Error())
    }
    // 创建一个没有注册服务的新的gRpc服务器
    s := grpc.NewServer()
    // 注册服务
    pb.RegisterDemo5Server(s, &Demo5Server{})
    fmt.Println("gRpc 服务端开启")
    // 接收gRpc请求
    if err := s.Serve(listen); err != nil {
        log.Fatal("this is error : ", err.Error())
    }
}

step4 : gRpc客户端实现

文件名: grpc_demo5_client.go

package main

import (
    pb "GoNote/chapter10/demo9/example"
    "fmt"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    "log"
    "time"
)

func main() {
    // 创建一个客户端连接
    conn, err := grpc.Dial(":8080", grpc.WithInsecure(), grpc.WithBlock())
    if err != nil {
        log.Fatal("connect failed : ", err.Error())
    }
    // 关闭客户端连接
    defer conn.Close()
    // 客户端服务api
    client := pb.NewDemo5Client(conn)
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*1)
    defer cancel()
    // 客户端请求数据
    req := pb.Demo5Request{
        Name: "Tom",
        Age:  99,
        Info: &pb.Demo5Request_Other{
            Addr:  "Beijing",
            Hobby: "climbing",
            G:     0,
        },
    }
    // 调用服务接口
    r, err := client.GetInfo(ctx, &req)
    if err != nil {
        log.Fatal("called failed : ", err.Error())
    }
    // 打印结果
    fmt.Println(r.Info)
}

测试

$ go run grpc_demo5_server.go
gRpc 服务端开启
$ go run grpc_demo5_client.go
my name is Tom, i am 99, i live in Beijing

你可能感兴趣的:(Go语言与gRpc)