工作中要把原来Java服务端基于SpringMVC的服务改为使用gRPC直接调用。由于原Service的返回值为动态的Map类型,key值不确定,且value的类型不唯一,因此使用了protobuf 3中的map和Any类型。在这个过程中遇到了一些困难,查阅资料时发现这一块的资料不是很多,尤其是在NodeJS的gRPC-Client处理google.protobuf.Any类型,完全找不到相关的资料。好在自己摸索和调试后解决了问题,因此记录下来以供他人之需。

testservice.proto:

1 syntax = "proto3";

2

3 import "google/protobuf/any.proto";

4 option java_package = "com.zfp.demo.grpc";

5

6 service TestService {

7 rpc getMapData (Param) returns (GenericMap);

8 }

9

10 message GenericMap {

11 map value = 1;

12 }

13

14 message Param{

15 string value = 1;

16 }

17

18 message ListParam{

19 repeated string value = 1;

20 }

其中,Any类型的作用是在protobuf中不需要明确定义值的结构和类型,而是在gRPC的Server端通过pack()将任何message打包成Any类型(不可以直接打包Java Object),在client可以通过unPack()将message从Any中取出,实现了protobuf对泛型的支持。

Java Server:

1 import com.google.protobuf.Any;

2 @Overried

3 public void getMapData(Testservice.Param request, StreamObserver responseObserver) {

4

5    Testservice.Param stringValue = Testservice.Param

6              .newBuilder()

7           .setValue("this is String type")

8              .build();

9

10    List tempList = Lists.newArrayList("this is", "List type");

11    Testservice.ListParam listValue = Testservice.ListParam

12           .newBuilder()

13                 .addAllValue()

14               .build();

15

16    Map reMap = Maps.newHashMap();

17    reMap.put("k1", Any.pack(stringValue));

18    reMap.put("k2", Any.pack(listValue));

19

20    Testservice.GenericMap genericMap = Testservice.GenericMap

21                            .newBuilder()

22                           .putAllValue(reMap)

23                        .build();

24

25    responseObserver.onNext(genericMap);

26    responseObserver.onCompleted();

27 }

Java Client:

1 @Test

2 public void getMapDataTest() throws ExecutionException, InterruptedException {

3

4     ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 6565)

5                           .usePlaintext(true)

6                         .build();

7

8     TestServiceGrpc.RoomServiceBlockingStub bkStub = TestServiceGrpc.newBlockingStub(channel);

9

10

11     Testservice.Param param = Testservice.Param.newBuilder().setValue("test param").build();

12

13     Map reMap = bkStub.getMapData(param).get().getValueMap();

14

15     Map dataMap = Maps.newHashMap();

16

17     reMap.forEach((k, v) -> {

18 if (k.equals("k1")) {

19 try {

20 dataMap.put(k, v.unpack(Testservice.Param.class).getValue());

21 } catch (InvalidProtocolBufferException e) {

22 e.printStackTrace();

23 }

24 } else {

25 try {

26 dataMap.put(k, v.unpack(Testservice.ListParam.class).getValueList());

27 } catch (InvalidProtocolBufferException e) {

28 e.printStackTrace();

29 }

30 }

31 });

32

33 logger.info(JSON.toJSONString(dataMap, true));

34 }

NodeJS Client:

var messages = require(‘./testservice_pb‘);

var services = require(‘./testservice_grpc_pb‘); // 这两个文件是利用protoc命令根据 testservice.proto 自动生成的

var grpc = require(‘grpc‘);

var prob = require(‘./node_modules/google-protobuf/google/protobuf/any_pb‘);  // 使用了Any类型必须引入这个文件

var jspb = require(‘google-protobuf‘);

main = function () {

var client = new services.TestServiceClient(‘localhost:6565‘,

grpc.credentials.createInsecure());

var request = new messages.Param();

request.setValue("test param");

client.getMapData(request, function (err, res) {

var reMap = unPackGpMap(res);

console.log(reMap);

});

};

/**

* 解包 google.protobuf.Any 对象,并返回结果

* @param {!google.protobuf.Any} gpAny

*/

unPackAny = function (gpAny) {

var typeName = gpAny.getTypeName();  // 获取Any包装的message对象的类型名称

var deserialize;

switch (typeName) {

case "ListParam":

deserialize = messages.ListParam.deserializeBinary;  // 从Uint8Array反序列化ListParam的function

return unPackAny_List(gpAny, deserialize, typeName);

case "Param":

deserialize = messages.Param.deserializeBinary;

return unPackAny_OneField(gpAny, deserialize, typeName);

case "ObjParam":

deserialize = messages.ObjParam.deserializeBinary;

return unPackAny_ComplexObject(gpAny, deserialize, typeName);

default:

return "the Message type \‘" + typeName +

"\‘ is not defiend in .proto file";

}

};

/** * Any包装的message只含有一个名为value的字段时使用 */

unPackAny_OneField = function (gpAny, deserialize, typeName) {

return gpAny.unpack(deserialize, typeName).toObject()["value"];

};

/** * Any包装的message含有一个名为value的repeated字段时使用 */

unPackAny_List = function (gpAny, deserialize, typeName) {

return gpAny.unpack(deserialize, typeName).toObject()["valueList"];

};

/** * Any包装的message含有多个field时使用(message嵌套也同样适用) */

unPackAny_ComplexObject = function (gpAny, deserialize, typeName) {

return gpAny.unpack(deserialize, typeName).toObject();

};

/**

* 将 GenericMap 中需要的Map数据取出,并解包Any型的value, 组装成可读的reMap并返回

* @param gpMap

* @returns {{}}

*/

unPackGpMap = function (gpMap) {

var dataMap = gpMap[‘wrappers_‘][‘1‘][‘map_‘];

var fieldList = Object.keys(dataMap);

var reMap = {};

for (var i = 0; i < fieldList.length; i++) {

reMap[fieldList[i]] = unPackAny(dataMap[fieldList[i]]["valueWrapper"]);

}

return reMap;

};

main();

本文只展示了google.protobuf.Any的使用,Java和NodeJS中gRpc项目的具体构建可以参考以下项目:

https://github.com/LogNet/grpc-spring-boot-starter

https://github.com/grpc/grpc/tree/master/examples

时间: 02-12

grpc java 泛型_gRPC中Any类型的使用(Java和NodeJs端)相关推荐

  1. java泛型之自限定类型和参数协变

    java泛型之自限定类型和参数协变 本博文参考<thinking in java>第四版第15张"泛型"中的相关内容和网络上的各种博客,本文也是几个月前的一篇博文&qu ...

  2. java引用类型和值类型_[Java教程]JavaScript中值类型和引用类型的区别

    [Java教程]JavaScript中值类型和引用类型的区别 0 2017-02-24 00:00:35 JavaScript的数据类型分为两类:原始类型和对象类型.其中,原始类型包括:数字.字符串和 ...

  3. Java泛型(11):潜在类型机制

    泛型的目标之一就是能够编写尽可能广泛应用的代码. 为了实现这一点,我们需要各种途径来放松对我们的代码将要作用的类型所做的限制,同时不丢失静态类型检查的好处.即写出更加泛化的代码. Java泛型看起来是 ...

  4. Java 泛型,你了解类型擦除吗?

    泛型,一个孤独的守门者. 大家可能会有疑问,我为什么叫做泛型是一个守门者.这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘与神奇.泛型是 Java 中一个很小巧的概念,但 ...

  5. oracle的clob类型对应java,java对oracle9i中CLOB类型的操作

    这是一个完整的使用java对oracle9i的操作,其实,使用oracle10g后,操作将非常方便,但现在由于仍在使用oracle9i,所以不得不忍受这种痛苦.现将对oracle9i中CLOB类型的操 ...

  6. java map clone_Java中HashMap的clone()方法: java.util.HashMap.clone() - Break易站

    Java中的HashMap java.util.HashMap.clone()方法用于返回所提到的哈希映射HashMap的浅表副本.它只是创建了map的副本. HashMap.clone句法: Has ...

  7. java向数组中增加新元素_用Java中的新元素替换List的给定索引处的元素

    java向数组中增加新元素 Given a list of the integers and we have to replace it an element from specified index ...

  8. Android中long类型对应Java/Jni/C++中的类型

    1.Android的Java中 long 数据类型是 64 位.有符号的以二进制补码表示的整数: 最小值是 -9,223,372,036,854,775,808(-2^63): 最大值是 9,223, ...

  9. java处理 mysql中json类型

    当数据库中存在json类型的数据,如图 json类型的值在数据库中保存的时候,会先字母排序并加空格后保存 场景:业务上需要校验,此json字段是否跟库里的json完全匹配(验重) 原理:利用mysql ...

最新文章

  1. 调整html css表格位置,调整表格中的列宽(CSS / HTML)
  2. Asp.net设计模式笔记之一:理解设计模式
  3. LINQPad学习与验证工具
  4. 解决WinCE自动挂起的问题
  5. css样式之 direction
  6. JavaScript实现degreeToRadian度到弧度算法(附完整源码)
  7. ASP.NET Core之跨平台的实时性能监控
  8. java 判断是否整数倍_java怎样实现判断一个最高达1000位数字的整数是不是3的倍数。...
  9. String.GetEnumerator 方法的C#例子
  10. 易语言取CPU序列号特征字
  11. mc用什么版本的java_我的世界网易 Java 版哪个版本比较好?
  12. 用友rep文件改为html形式,如何利用ufo简版工具修改用友凭证模板
  13. 测度论与概率论基础学习笔记1——1.1 集合及其运算
  14. OpenContrail架构浅析
  15. HCIP 安全资料下载 肖哥视频
  16. 使用pyinstaller将python脚本转成EXE可执行文件遇到的问题和总结
  17. Android---App Widget(官方文档翻译)
  18. 佳能打印机手机显示未连接服务器失败,佳能打印机连不上手机
  19. HC05蓝牙模块与手机APP连接
  20. Qt Quick实现的涂鸦程序

热门文章

  1. java调用cmd_Java调用CMD命令
  2. php yaf 教程,干货:PHP YAF框架实践教程——配置与多模块
  3. go java websocket_java的连接wss的websocket服务器代码如何转成go代码
  4. git pull git add git commit git branch git更新代码git提交git分支管理
  5. OpenShift 4 - 向OpenShift添加新的SSH Key
  6. Windows Terminal Preview 1.3 发布
  7. 电子邮件地址验证:详细解释,生产质量WPF文本框代码
  8. Windows终端中的自定义终端和用户界面(UI)
  9. 准确率创新高,北大开源中文分词工具包 pkuseg
  10. vue router children 路由变了 组件没变_Vue.js从零开始——路由(2)