关于golang:GOgrpc

16次阅读

共计 7313 个字符,预计需要花费 19 分钟才能阅读完成。

参考资料

https://www.jianshu.com/p/9ea…
https://www.cnblogs.com/baosh…
http://doc.oschina.net/grpc?t…
https://pkg.go.dev/google.gol…
https://www.jianshu.com/p/b72…
https://grpc.io/docs/language…
https://www.cnblogs.com/ExMan…
https://blog.csdn.net/u011518…
https://grpc.io/docs/language…
https://www.cnblogs.com/aweso…
https://blog.csdn.net/zhangmi…
https://blog.csdn.net/xp17817…

1. 概念

RPC(remote procedure call近程过程调用),实际上是提供了一套框架机制,使得位于网络中的不同机器上的应用程序之间能够进行通信互相调用,而且也听从 server/client 模型。应用的时候客户端调用 server 端提供的接口就像是调用本地的函数一样。通常 RPC 都是通过反射机制来实现的,本文不做深入分析,待后续文章再深入分析 RPC 的实现原理。

与其余的 RPC 框架相似,gRPC在服务端提供一个gRPC Server,客户端的库是gRPC Stub。典型的场景是客户端发送申请,调用服务端的接口,客户端和服务端之间的通信协议是基于 HTTP2 的,反对双工的流式保序音讯,性能比拟好,同时也很轻量级。

2. 长处

既然是 vserver/client 模型,那么咱们间接用 restful api 不是也能够的吗,为什么还须要 RPC(或者 gRPC)呢?上面咱们就来看看 gRPC 绝对于 Restful API 到底有哪些劣势?gRPC 和 restful API 都提供了一套通信机制,用于 server/client 模型通信,而且它们都应用 http 作为底层的传输协定。不过 gRPC 还是有些特有的劣势的,如下:

  • gRPC 能够通过 protobuf 来定义接口,从而能够有更加严格的接口约束条件;
  • 另外,通过 protobuf 能够将数据序列化为二进制编码,这会缩小须要传输的数据量,从而进步性能;
  • gRPC 能够不便地反对流式通信 (实践上通过http2.0 就能够应用 streaming 模式);
    简略易学,疾速开始,可能反对多种语言和平台,双向流式通信、集成认证模块。

3. 应用场景

须要对接口进行严格束缚的状况,咱们不心愿客户端给咱们传递任意的数据,尤其是思考到安全性的因素,咱们通常须要对接口进行更加严格的束缚。这时 gRPC 就能够通过 protobuf 来提供严格的接口束缚;

对于性能有更高的要求时。有时咱们的服务须要传递大量的数据,而又心愿不影响咱们的性能,这个时候也能够思考 gRPC 服务,因为通过 protobuf 咱们能够将数据压缩编码转化为二进制格局,通常传递的数据量要小得多,而且通过 http2 咱们能够实现异步的申请,从而大大提高了通信效率;

然而,通常咱们不会去独自应用 gRPC,而是将gRPC 作为一个部件进行应用,这是因为在生产环境,咱们面对大并发的状况下,须要应用分布式系统来去解决,而 `gRPCv 并没有提供分布式系统相干的一些必要组件。而且,真正的线上服务还须要提供包含负载平衡,限流熔断,监控报警,服务注册和发现等必要的组件;

接下来还得简略介绍一下 Protobuf,因为 gRPC 应用vprotobuf 来定义接口。Protobuf是什么?Protobuf理论是一套相似于 Json 或者 XML 的数据传输格局和标准,用于不同利用或过程之间进行通信时应用。通信时所传递的信息是通过 Protobuf 定义的 message 数据结构进行打包,而后编译成二进制的码流再进行传输或者存储。

4.Protobuf 概念

Protobuf 理论是一套相似于 Json 或者 XML 的数据传输格局和标准,用于不同利用或过程之间进行通信时应用。通信时所传递的信息是通过 Protobuf 定义的 message 数据结构进行打包,而后编译成二进制的码流再进行传输或者存储。

5.Protobuf 长处

  • 足够简略;
  • 序列化后体积很小,音讯大小只须要 XML1/10 ~ 1/3
  • 解析速度快,解析速度比 XML 快 20 ~ 100 倍;
  • 多语言反对;
  • 更好的兼容性,Protobuf设计的一个准则就是要可能很好的反对向下或向上兼容;

6. 应用 Protobuf 步骤

  • 定义音讯;
  • 初始化音讯以及存储传输音讯;
  • 读取音讯并解析;

Protobuf的音讯构造是通过一种叫做 Protocol Buffer Language 的语言进行定义和形容的,实际上 Protocol Buffer Language 分为两个版本,版本 2 和版本 3,默认不申明的状况下应用的是版本 2,目前举荐应用的是版本 3。

采纳 ProtoBuf 作为 IDLInterface Definition Language 接口定义语言),须要定义 servicemessage,生成客户端和服务端代码。用户本人实现服务端代码中的调用接口,并且利用客户端代码来发动申请到服务端。service代表 RPC 接口,message代表数据结构(外面能够包含不同类型的成员变量,包含字符串、数字、数组、字典等)。message中成员变量前面的数字代表进行二进制编码时候的提示信息,1~15示意热变量,会用较少的字节来编码。默认所有变量都是可选的(optional),repeated则示意数组。service rpc接口只能承受单个message 参数,返回单个 message。

7.golang 装置 gRpc

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
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto
 
cd $GOPATH/src/
go install google.golang.org/grpc

windows 装置:

$ export GO111MODULE=on  # Enable module mode
$ go get google.golang.org/protobuf/cmd/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc
$ export PATH="$PATH:$(go env GOPATH)/bin"

问题:
【1】go get github.com/golang/protobuf/protoc-gen-go 的问题
解决链接:https://blog.csdn.net/wwqcher…

【2】git 呈现 fatal: The remote end hung up unexpectedly
计划:https://blog.csdn.net/qq_3539…
办法:git clone –depth 1 https://github.com/grpc/grpc-…

8. 例子

https://www.sohu.com/a/426454…

对于开发者而言:
【1】须要应用 protobuf 定义接口,即 .proto 文件

【2】而后应用 compile 工具生成特定语言的执行代码,比方 JAVAC/C++Python 等。相似于thrift,为了解决跨语言问题。

【3】启动一个 Server 端,server端通过侦听指定的 port,来期待 Client 链接申请,通常应用Netty 来构建,GRPC内置了 Netty 的反对。

【4】启动一个或者多个 Client 端,Client也是基于 Netty,Client 通过与 Server 建设 TCP 长链接,并发送申请;RequestResponse 均被封装成 HTTP2 的stream Frame,通过 Netty Channel 进行交互。

8.1 定义服务
咱们想要实现的是通过 gRPC 框架进行近程服务调用,首先第一步应该是要有服务。利用之前所把握的内容,gRPC 框架反对对服务的定义和生成。gRPC 框架默认应用 protocol buffers 作为接口定义语言,用于形容网络传输音讯构造。除此之外,还能够应用 protobuf 定义服务接口。

syntax = "proto3";
package message;

// 订单申请参数
message OrderRequest {
    string orderId = 1;
    int64 timeStamp = 2;
}


// 订单信息
message OrderInfo {
    string OrderId = 1;
    string OrderName = 2;
    string OrderStatus = 3;
}
// 订单服务 service 定义
service OrderService{rpc GetOrderInfo(OrderRequest) returns (OrderInfo);
}

通过 proto 文件定义了数据结构的同时,还定义了要实现的服务接口,GetOrderInfo即是具体服务接口的定义,在 GetOrderInfo 接口定义中,OrderRequest示意是申请传递的参数,OrderInfo示意处理结果返回数据参数。

8.2 环境筹备
定义的 proto 文件须要通过编译,生成 go 语言代码文件,供客户端程序和服务端程序应用。能够装置 go 语言环境中的对于 proto 的插件。

go get -a github.com/golang/protobuf/protoc-gen-go #-a 参数标示下载好后间接做 go install

能够通过根本编译命令实现对.proto 文件的编译. 根底编译命令如下:

protoc --go_out=. *.proto

gRPC 编译反对

如果定义的 .proto 文件,如本案例中所示,定义中蕴含了服务接口的定义,而咱们想要应用 gRPC 框架实现 RPC 调用。开发者能够采纳 protocol-gen-go 库提供的插件编译性能,生成兼容 gRPC 框架的 golang 语言代码。只须要在根本编译命令的根底上,指定插件的参数,告知 protoc 编译器即可。具体的编译生成兼容 gRPC 框架的服务代码的命令如下:

protoc --go_out=plugins=grpc:. *.proto

8.3 gRPC 实现 RPC 编程

8.3.1 服务接口实现
.proto定义好服务接口并生成对应的 go 语言文件后,须要对服务接口做具体的实现。定义服务接口具体由 OrderServiceImpl 进行实现,并实现 GetOrderInfo 具体内容,服务实现逻辑与前文所述内容雷同。不同点是服务接口参数的变动。具体代码实现如下:

type OrderServiceImpl struct {
}

// 具体的办法实现
func (os *OrderServiceImpl) GetOrderInfo(ctx context.Context, request *message.OrderRequest) (*message.OrderInfo, error) {orderMap := map[string]message.OrderInfo{"201907300001": message.OrderInfo{OrderId: "201907300001", OrderName: "衣服", OrderStatus: "已付款"},
        "201907310001": message.OrderInfo{OrderId: "201907310001", OrderName: "零食", OrderStatus: "已付款"},
        "201907310002": message.OrderInfo{OrderId: "201907310002", OrderName: "食品", OrderStatus: "未付款"},
    }
    var response *message.OrderInfo
    current := time.Now().Unix()
    if (request.TimeStamp > current) {*response = message.OrderInfo{OrderId: "0", OrderName: "", OrderStatus:" 订单信息异样 "}
    } else {result := orderMap[request.OrderId]
        if result.OrderId != "" {fmt.Println(result)
            return &result, nil
        } else {return nil, errors.New("server error")
        }
    }
    return response, nil
}

8.3.2 gRPC 实现服务端
应用 gRPC 框架,首先实现服务端的程序。既然应用 gRPC 框架来实现,就须要调用 gRPC 进行服务办法的注册以及监听的解决。服务注册和监听解决实现如下:

func main() {server := grpc.NewServer()
    message.RegisterOrderServiceServer(server, new(OrderServiceImpl))
    lis, err := net.Listen("tcp", ":8090")
    if err != nil {panic(err.Error())
    }
    server.Serve(lis)
}

8.3.3 gRPC 实现客户端
实现完服务端当前,实现客户端程序。和服务端程序关系对应,调用 gRPC 框架的办法获取相应的客户端程序,并实现服务的调用,具体编程实现如下:

func main() {
    //1、Dail 连贯
    conn, err := grpc.Dial("localhost:8090", grpc.WithInsecure())
    if err != nil {panic(err.Error())
    }
    defer conn.Close()
    orderServiceClient := message.NewOrderServiceClient(conn)
    orderRequest := &message.OrderRequest{OrderId: "201907300001", TimeStamp: time.Now().Unix()}
    orderInfo, err := orderServiceClient.GetOrderInfo(context.Background(), orderRequest)
    if orderInfo != nil {fmt.Println(orderInfo.GetOrderId())
        fmt.Println(orderInfo.GetOrderName())
        fmt.Println(orderInfo.GetOrderStatus())
    }
}

运行程序

通过上述步骤后,程序及逻辑全副开发实现。程序运行,打印如下后果:

201907300001
衣服
已付款

9. 生成.go 文件

$ protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helloworld/helloworld.proto

go_out:生成 *.pb.go 代码的门路
go-grpc_out:生产*.grpc.pb.go 的门路
helloworld/helloworld.proto*.proto 的门路

10. 遇到的谬误

【1】Missing ‘go_package’ option Go 生成 grpc 文件告警
https://www.jianshu.com/p/e05…

【2】No syntax specified for the proto file : xxx.proto
https://blog.csdn.net/weixin_…

【3】proto:2:1: Interpreting non ascii codepoint 226.
https://blog.csdn.net/qq_3845…
https://www.jianshu.com/p/6f1…

【4】rpc error: code = Unimplemented desc = RPC method not implemented
https://www.cnblogs.com/lavin…

【5】_grpc.pb.go:14:11: undefined: grpc.SupportPackageIsVersion7
https://blog.csdn.net/yzf2795…

【6】*.pb.go:222:7: undefined: grpc.ClientConnInterface
https://blog.csdn.net/qq_1607…
go mod 中批改版本

syntax = "proto3";// 协定为 proto3
option go_package = ".;protoTest";

// 定义发送申请信息
message SimpleRequest{
    // 定义发送的参数
    // 参数类型 参数名 标识号(不可反复)
    string data = 1;
}

// 定义响应信息
message SimpleResponse{
    // 定义接管的参数
    // 参数类型 参数名 标识号(不可反复)
    int32 code = 1;
    string value = 2;
}

// 定义咱们的服务(可定义多个服务, 每个服务可定义多个接口)service Simple{rpc Route (SimpleRequest) returns (SimpleResponse){};}
正文完
 0