工作中要把原来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. Apache处理请求步骤及过程

    Apache请求处理循环详解 : 1.Post-Read-Request阶段: 在正常请求处理流程中,这是模块可以插入钩子的第一个阶段.对于那些想很早进入处理请求的模块来说,这个阶段可以被利用. 2. ...

  2. PHP的几种排序算法的比较

    这里列出了几种PHP的排序算法的时间比较的结果,,希望对大家有所帮助 /* * php 四种排序算法的时间与内置的sort排序比较 * 3000个元素,四种算法的排序所用的时间比较 * 冒泡排序 85 ...

  3. MySQL master/slave 模式

    1 .复制 Mysql内建的复制功能是构建大型,高性能应用程序的基础.将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机的 数据复制到其它主机(slaves)上,并重 ...

  4. 一起学JUCE之HashMap

    基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.) ...

  5. ECSHOP中ajax的调用原理

    ECSHOP中ajax的调用原理 ecshop中ajax的调用原理. 1.首先ecshop是如何定义ajax对象的. ecshop中的ajax对象是在js/transport.js文件中定义的.里面是 ...

  6. BZOJ1478 Sgu282 Isomorphism

    Problem A: Sgu282 Isomorphism Time Limit: 15 Sec  Memory Limit: 64 MBSubmit: 172  Solved: 88[Submit] ...

  7. r.js build.js配置

    /* * This is an example build file that demonstrates how to use the build system for * require.js. * ...

  8. html5、css3及响应式设计入门

    一.响应式设计的定义 将三种已有的开发技巧(弹性网格布局.弹性图片.媒体和媒体查询)整合起来,命名为响应式网页设计.真正的响应式设计方法不仅仅只是根据视口大小改变网页布局.相反,它是要从整体上颠覆我们 ...

  9. Swift 添加到TableView实现动画效果

    let indexPath = NSIndexPath(forRow:0 ,inSection:0) self.tableView.insertRowsAtIndexPaths([indexPath] ...

  10. iOS 之 获取View所在控制器

    1. UIResponder UIViewController *uvc; UIResponder* nextResponder = [self.superview.superview.supervi ...