学习笔记:初学grpc,在node中使用grpc(静态方式)

1,379 阅读6分钟

1.grpc是什么?

首先,要了解RPC(Remote Procedure Call),即远程过程调用。使得我们可以像调用本地方法一样地调用远程机器上的方法。就是说,A机器发起命令调用一个方法,但是这个服务是在B机器上,所以叫远程过程调用。它为了解决不同服务之间的调用问题。

grpc就是RPC的一种具体实现。官方是这么介绍的:高性能、开源和通用的RPC框架,面向移动和 HTTP/2 设计。

完整的RPC实现一般会包含有传输协议和序列化协议这两个。grpc对应使用的HTTP2和Protocol Buffers 数据序列化协议。这就是高性能的体现,因为它们都是二进制协议。

往grpc深入的去讲的话,就涉及到很多方面了,如Protocol Buffers是什么,HTTP2的原理啥的。以后有时间再详细去了解吧。本文就不拓展了。

2.在node中使用(静态)

这篇笔记,主要是对官方示例静态使用方式的一个补充,假设你已经熟悉proto文件的定义,知道客户端和服务端的交互等基础知识,不知道的话也没关系,去官方地址,把代码跑一遍就ok了。github.com/grpc/grpc/t…

官方有的部分是,基础类型以及嵌套对象的set和get的使用。

它缺少的是,map的使用,数组元素是对象时,转化json结构。当然,可能缺的不止这些,但是我在项目中需要有这些,还好最后有大神的协助,才解决了这几个问题。可能也是因为对原理不熟悉吧。

node提供了两种方式,一个是动态方式(dynamic_codegen目录),一个是静态方式(static_codegen目录)。这两种方式,个人觉得是在实现上的区别吧,我问过官方人员,他是这么回答我的

“In this context, "static" code generation refers to generating files ahead of time using grpc-tools and using them with the google-protobuf library. "Dynamic" code generation refers to loading .proto files and generating objects at runtime using the @grpc/proto-loader library. Those two libraries provide different interfaces for the same services.”

谷歌翻译成中文就是

在这种情况下,“静态”代码生成是指使用grpc-tools并与google-protobuf库一起使用来提前生成文件。 “动态”代码生成是指使用@grpc / proto-loader库在运行时加载.proto文件并生成对象。 这两个库为相同的服务提供了不同的接口。

怎么选的话,得看具体项目。但最好就是保持一致性。如果和你对接的人员使用了静态,还是统一使用静态方式吧。

动态方式就不多说了,官方例子写的很清楚了。主要讲静态方式,注意:代码都是简化版,只有核心部分,所以一定要学一下官方例子,有一个基础才行。

1.map的使用

假设我们有一个这样的proto文件。

syntax = "proto3";

package request; 

service Demo {
    rpc GetDemo(GetDemoRequest) returns (GetDemoResponse) {}
}

message GetDemoRequest{	
}

message GetDemoResponse{    map<string, string> map = 1;
}

生成两个静态文件后,那么我们可以这样去使用

const res = new messages.GetDemoResponse();

res.getMapMap().set('1', 'a').set('2', 'b');

由于之前一直是使用基础类型,都是先set,然后get,结果map是只提供了get方法(getMapMap)所以瞬间不知道怎么使用了。

为什么map类型是只提供了get方法,让你先get回来再set呢?我领导和我解释过,依稀记忆是这样描述的:如果是自己直接写set的话,系统创建内存分配内存的时候都是随机的,不利于管理,但是通过grpc,先get回来的话,这块工作grpc就会帮你做好,它会开辟一大块内存,对这块内存进行管理。

2.数组元素是对象时

上面的proto文件改下

syntax = "proto3";

package request; 

service Demo {
    rpc GetDemo(GetDemoRequest) returns (GetDemoResponse) {}
}

message GetDemoRequest{
	
}

message GetDemoResponse{
    repeated Arr obj= 1;}

message Arr { 
    string name = 1;
}

数组会提供三个方法setObjList,getObjList,addObj,老套路,我们一般都是直接用set和get,简直不要太简单。但结果打印出来懵逼了。怎么元素不是对象呢?因为上面的定义,元素应该都是Arr对象来的。

最后,求助某一大神,大神也折腾了一番,发现应该是使用addObj,为什么我一开始没有用这个方法,可能是因为被官方误导了,官方的是基本类型,而不是一个对象。然后我脑海里就一直固化了。。。

使用起来也很简单:

const res = new messages.GetDemoResponse();
const arr = new messages.Arr();
arr.setName('abc')  
res.addObj(arr)
// res.add(new messages.Arr().setName('abc')) 这样写也可以

3.转化为json结构

使用文件提供的toObject方法即可。

3.静态文件的生成

使用方式是不是很简单,然而我这个菜鸡摸索了很久,除了看官方,网上都没啥资料,要不都是官方的搬运。希望新手们都能快速上手吧。

当时还有一个问题也让我摸索了很久。就是关于静态文件是怎么生成的。

  1. npm下载grpc-tools工具(这个工具之前有个optional的bug,还以为是我的问题,搞了很久,还好最后直接问官方,官方已解决)

  2. 使用命令,我是把命令写成一个bat文件。

    protoc --js_out=import_style=commonjs,binary:./ --plugin=protoc-gen-grpc=./grpc_node_plugin.exe --grpc_out=./ helloworld.proto
    

--js_out=import_style=commonjs,binary: 这个参数是指输出的pb文件是js文件,后面可以指定路径输出到哪里。例子是输出到当前目录下

 --plugin=protoc-gen-grpc=./grpc_node_plugin.exe 这个是指使用插件生成grpc_pb文件,具体插件就是grpc_node_plugin.exe,这个是node的插件。 

 --grpc_out 指的是grpc_out 的文件输出位置,例子是输出到当前目录下

helloworld.proto 具体的proto,前面可以加路径,例子当前路径下。

4.为什么使用grpc

依稀记得我领导是这么跟我说的:常用的协议有HTTP,TCP和UDP,存在的一定的问题,UDP传输快,但不保证指令一定到达,并且丢包了也不会通知。TCP可以保证,但是TCP也存在自己的问题,丢包,错包,安全问题,时序错乱等。这些问题是需要自己解决的,但如果使用grpc的话,它已经做了这部分的内容,就减轻了工作量。http发送的数据有很多冗余,但是grpc是使用protobuffer协议的,会压缩数据,并且把数据序列化。

本人也是刚刚接触grpc,会点简单的使用,在原理这块,还需要好好补充才行。