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

testservice.proto:

 syntax = "proto3";

 import "google/protobuf/any.proto";
option java_package = "com.zfp.demo.grpc"; service TestService {
rpc getMapData (Param) returns (GenericMap);
} message GenericMap {
map<string, google.protobuf.Any> value = ;
} message Param{
string value = ;
} message ListParam{
repeated string value = ;
}

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

Java Server:

 import com.google.protobuf.Any;
@Overried
public void getMapData(Testservice.Param request, StreamObserver<Testservice.GenericMap> responseObserver) {    Testservice.Param stringValue = Testservice.Param
             .newBuilder()
          .setValue("this is String type")
             .build();    List<String> tempList = Lists.newArrayList("this is", "List type");
   Testservice.ListParam listValue = Testservice.ListParam
          .newBuilder()
                .addAllValue()
              .build();    Map<String, Any> reMap = Maps.newHashMap();
   reMap.put("k1", Any.pack(stringValue));
   reMap.put("k2", Any.pack(listValue));    Testservice.GenericMap genericMap = Testservice.GenericMap
21                            .newBuilder()
                          .putAllValue(reMap)
                       .build();    responseObserver.onNext(genericMap);
   responseObserver.onCompleted();
}

Java Client:

 @Test
public void getMapDataTest() throws ExecutionException, InterruptedException {     ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 6565)
                          .usePlaintext(true)
                        .build();     TestServiceGrpc.RoomServiceBlockingStub bkStub = TestServiceGrpc.newBlockingStub(channel);     Testservice.Param param = Testservice.Param.newBuilder().setValue("test param").build();     Map<String, Any> reMap = bkStub.getMapData(param).get().getValueMap();     Map<String, Object> dataMap = Maps.newHashMap();     reMap.forEach((k, v) -> {
if (k.equals("k1")) {
try {
dataMap.put(k, v.unpack(Testservice.Param.class).getValue());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
} else {
try {
dataMap.put(k, v.unpack(Testservice.ListParam.class).getValueList());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
}); logger.info(JSON.toJSONString(dataMap, true));
}

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

gRPC中Any类型的使用(Java和NodeJs端)的更多相关文章

  1. 详解Java 8中Stream类型的“懒”加载

    在进入正题之前,我们需要先引入Java 8中Stream类型的两个很重要的操作: 中间和终结操作(Intermediate and Terminal Operation) Stream类型有两种类型的 ...

  2. Java中枚举类型简单学习

    /* * enum类型不允许继承 * 除了这一点,我们基本上可以将enum看作一个常规的类 * 我们可以添加自己的方法与属性,我们也可以覆盖其中的方法. * 不过一定要在enum实例序列的最后添加分号 ...

  3. Java中double类型的数据精确到小数点后两位

    Java中double类型的数据精确到小数点后两位 多余位四舍五入,四种方法 一: double f = 111231.5585;BigDecimal b = new BigDecimal(f); d ...

  4. java中基本类型封装对象所占内存的大小(转)

    这是一个程序,java中没有现成的sizeof的实现,原因主要是java中的基本数据类型的大小都是固定的,所以看上去没有必要用sizeof这个关键字. 实现的想法是这样的:java.lang.Runt ...

  5. Java中泛型 类型擦除

    转自:Java中泛型是类型擦除的 Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类 ...

  6. java中基本类型占用字节数

    之前一直使用c/c++开发c中各种类型占用的位数和java还是有区别的,特地找了篇文章过来对比下. 在处理网络协议的时候需要注意 在Java中一共有8种基本数据类型,其中有4种整型,2种浮点类型,1种 ...

  7. Java进阶(二十三)java中long类型转换为int类型

    java中long类型转换为int类型 由int类型转换为long类型是向上转换,可以直接进行隐式转换,但由long类型转换为int类型是向下转换,可能会出现数据溢出情况: 主要以下几种转换方法,供参 ...

  8. java和数据库中日期类型的常见用法

    (1)java中日期类型:Date.Timestamp(2)数据库中:Date.Timestamp(3)字符串和Date之间的格式化转换:    SimpleDateFormat类方法: format ...

  9. JAVA中值类型和引用类型的不同(面试常考)

    转载:https://www.cnblogs.com/1ming/p/5227944.html 1. JAVA中值类型和引用类型的不同? [定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个 ...

随机推荐

  1. Ubuntu安装MongoDB和PHP扩展

    MongoDB是一个可伸缩的,高性能的开源NoSQL 文档数据库.主要用C++开发完成.面向文档存储,全索引支持,可复制和高可用性,自动分片等特征.其在非关系型数据库中是功能最丰富,最像关系型数据库 ...

  2. window.location.href 和 document.location.href

    document表示的是一个文档对象,window表示的是一个窗口对象,一个窗口下可以有多个文档对象. 所以一个窗口下只有一个window.location.href,但是可能有多个document. ...

  3. 转:css中overflow:hidden 不起作用了吗?

    css中overflow:hidden 不起作用了吗? 有同学遇到这样的问题,现象是给元素设置了overflow:hidden,但超出容器的部分并没有被隐藏,难道是设置的hidden失效了吗?其实看似 ...

  4. 使用flexbox来布局web应用

    使用 flexbox 可以帮助你设计出引人注目的布局,并且在pc端或移动端能够很好的缩放.告别使用浮动的 <div> 元素.绝对定位 和一些JavaScript hacks, 使用仅仅几行 ...

  5. X-002 Exyson4412芯片启动过程分析

    移植u-boot到FriendlyARM Tiny4412开发板上,首先我们需要对Samsung Exyson4412芯片的启动方式.系统时钟初始化.串口初始化.内存初始化以及开发板的内存地址空间分配 ...

  6. UIButton常用属性小结(编辑中。。。)

    Button的功能很黄很暴力,即能显示文字,又能显示图片,还能随时调整内部图片和文字的位置,用的地方很多. (1)按钮常用的四种状态: normal(普通状态) 默认情况(Default) 对应的枚举 ...

  7. Elasticsearch的使用场景深入详解

    了解了ES的使用场景,ES的研究.使用.推广才更有价值和意义. 1.场景-:使用Elasticsearch作为主要的后端 传统项目中,搜索引擎是部署在成熟的数据存储的顶部,以提供快速且相关的搜索能力. ...

  8. js jqery判断checkbox是否选中,全选,取消全选,反选,选择奇数偶数项

    // 一,判断选中 // js var ischecked2 = function(){ // this.checked == true $(document.getElementsByTagName ...

  9. Swift迁入第三方库时的版本错误解决

    我的swift的项目用的是swift 2.3的版本,但是用CocoaPods迁入一个第三方:ObjectMapper后,编译会出现这样一个问题: Use Legacy Swift Language V ...

  10. centos 6.5下编译安装、配置高性能服务器Nginx

    1.nginx是什么? Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,由俄罗斯的程序设计师Igor Sysoev所开发,其特点是占有内存少,并发能力 ...