ProtoBuffer 的介绍
google 有一款非常高效的数据传输格式框架 ProtoBuffer 在 java 中使用 protobuffer 作为序列化效率比 jdk 自身的 serializable 接口效率高的多 (github 上有个对于序列号性能的研究 github.com/eishay/jvm-), 这在缓存的时候效率非常高当然, 如此优秀的数据格式框架并不是仅仅使用在缓存上的, 既然压缩(姑且将其简单理解为压缩算法吧) 如此高效, 那么使用在网络 IO 传输中比 JSON 或许 XML 而言效率也为提升很多吧
gRPC 的介绍
gRPC 是 google 开发的一款 RPC 框架, RPC Server 与 RPC Clinet 之间的数据传输就是刚刚提到的 ProtoBuffer, 并且该 RPC 框架还是基于 HTTP2 的因此, HTTP2 的多路复用, 基于流的传输在 gRPC 上也有相应的实现
ProtoBuffer 格式的定义
PrototBuffer 的官方文档: developers.google.com/protocol-bu
笔者贴出在实际使用中的格式定义文件. proto:
- syntax = "proto3";
- package vsig;
- service VSIGProto {
- rpc setAcl (ACLRequest) returns (Reply) {}
- rpc openNtp (NTPConfig) returns (Reply) {}
- }
- message ACLRequest {
- string extranetIp = 1;
- int32 devType = 2;
- string intranetIp = 3;
- }
- message InterfaceInfoRequest {
- string name =1;
- string ip = 2;
- string mask=3;
- string gateway=4;
- }
- message NTPInfoRequest{
- bool state = 1;
- string ntpServerIp =2;
- int32 ntpServerPort =3;
- }
- message NTPConfig{
- NTPInfoRequest ntpInfo = 1;
- InterfaceInfoRequest br0Info = 2;
- }
- message Reply {
- string message = 1;
- }
.proto 文件中主要定义了三部分东西:
RPC 的方法名以及接受的参数和返回的参数
RPC 方法接受参数的格式
RPC 方法返回的格式
一个方法仅能接受一个参数, 因为笔者定义 NTPConfig 里面又包含了两个对象, 这样保证了 openNtp 方法仅接收了一个对象
对于定义的 message, 每个值都有一个唯一的 number 类型的数字, 根据官方文档的解释: 它是用于以消息二进制格式标识字段, 并且在使用过程中不能随便更改, 否则会导致数据无法还原同时, 如果数字定义为 1~15 则使用一个字节来存储, 而 16~2047 需要使用两个字节来存储
gRPC Server 的实现
定义好. proto 之后就可以使用该文件来使用 grpc 客户端与服务器端了, gRPC 的客户端与服务器端必须使用同一个. proto 文件
gRPC 支持众多常见的编程语言, 笔者使用 java 与 node 两种语言实现 gRPC
NodeJS gRPC Server 实现
- package.json:
- {
- "name": "grpc-examples",
- "version": "0.1.0",
- "dependencies": {
- "async": "^1.5.2",
- "google-protobuf": "^3.0.0",
- "grpc": "^1.0.0",
- "lodash": "^4.6.1",
- "minimist": "^1.2.0"
- }
- }
gRPC 服务器的编码实现:
- // 上述定义的. proto 的路径
- const PROTO_PATH = '../../vsig.proto';
- const grpc = require( 'grpc' );
- // 最后 vsig 是. proto 中的 package
- const proto = grpc.load( PROTO_PATH ).vsig;
- // 定义 rpc Server 的 ip 与端口
- const rpcHost = '127.0.0.1';
- const rpcPort = 50051;
- // 定义方法的映射, 因为方法最终是在该类中实现的, 因此定义改类与. proto 中的方法的映射左边为. proto 中的方法名, 右边为实现
- const methodCover = {
- setAcl: setAcl,
- openNtp: openNTP
- };
- function setAcl( call, callback ) {
- //call.request 即为该方法在. proto 中定义的参数接收的 message 对象
- console.log( call.request);
- // 该回调即为对客户端的方法, 参数 1 是 error, 参数二与. proto 中方法的返回值对应
- callback( null, {
- message: "rpc call setAcl method success"
- } )
- }
- function openNTP( call, callback ) {
- const ntpInfo = call.request;
- console.log(ntpInfo);
- callback(null,{
- message:"rpc call openNTP call success"
- })
- }
- function main() {
- var server = new grpc.Server();
- //VSIGProto 即为. proto 中的 server 的名称, 参数二为方法映射
- server.addProtoService( proto.VSIGProto.service, methodCover );
- const grpcIn = grpc.ServerCredentials.createInsecure();
- // 绑定端口
- server.bind( rpcHost + ":" + rpcPort, grpcIn );
- // 启动
- server.start();
- }
- main();
Java gRPC Server 的实现
gRPC Client 的实现
如上定义好服务端之后, 将监听指定的端口, 客户端只需要对改端口发送请求即可
Node gRPC Client 的实现
- const PROTO_PATH = '../vsig.proto';
- const grpc = require('grpc');
- // 最后 vsig 是. proto 中的 package
- const proto = grpc.load(PROTO_PATH).vsig;
- const rpcHost = '127.0.0.1';
- const rpcPort = 50051;
- //VSIGProto 是. proto 中 service 中的 VSIGProto
- const client = new proto.VSIGProto(rpcHost + ":" + rpcPort, grpc.credentials.createInsecure());
- class RpcClient {
- setAcl(acl, cb) {
- // 执行 rpc 调用
- client.setAcl(acl,
- function(err, response) {
- cb(err, response)
- });
- }
- // 封装 RPC 的方法
- openNTP(ntpconfig, cb) {
- // 执行 rpc 调用
- client.openNtp(ntpconfig,
- function(err, response) {
- cb(err, response);
- });
- }
- }
- const rpcClient = new RpcClient();
- module.exports = rpcClient;
Java gRPC Client 的实现
来源: https://juejin.im/entry/5ab1c328f265da23961241eb