工作中要把原来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. Solr 按照得分score跟指定字段相乘排序

    sort=product([you_field],query($q)) desc

  2. MySQL导入sql脚本中文乱码设置和常用命令

    1. use database_name; 2. set names utf8; (或其他需要的编码) 3. source example.sql (sql文件存放路径) Mysql安装目录数据库目录 ...

  3. 浅谈IOS8之size class

    文章目录 1. 简介 2. 实验 3. 实战 3.1. 修改 Constraints 3.2. 安装和卸载 Constraints 3.3. 安装和卸载 View 3.4. 其他 4. 后话 以前和安 ...

  4. python实现断点续传下载文件

    最近的任务里有一个功能是要我从日志服务器实时跟新日志到本地,日志在不断新增内容就需要我隔一段时间从上次下载的位置继续下载,并写入本地文件上次写完的位置后面. headers = {'Range': ' ...

  5. C#中的协变OUT和逆变

    泛型接口和泛型委托中经常使用可变性 in  逆变,out  协变 从 list<string>转到list<object> 称为协变 (string 从object 派生,那么 ...

  6. SQL Server 2012 - 数据库的基础操作

    数据库基本操作 --新建数据库卡 use master go create database SchoolDB on ( Name=SchoolDB, FileName='D;\DB\SchoolDB ...

  7. [项目回顾]基于Annotation与SpringAOP的缓存简单解决方案

    前言: 由于项目的原因,需要对项目中大量访问多修改少的数据进行缓存并管理,为达到开发过程中通过Annotation简单的配置既可以完成对缓存的设置与更新的需求,故而设计的该简易的解决方案. 涉及技术: ...

  8. 在ubtuntu中使用包管理器 linux-brew

    用惯了mac的程序员回到linux开发平台总觉得有点不适应,这是因为linux的界面没有mac那么精美,而且linux的包管理器没有mac上面的homebrew那么强大.mac程序员遇到库的依赖问题时 ...

  9. 8.DNS :域名系统

    前面已经提到了访问一台机器要靠IP地址和MAC地址,其中,MAC地址可以通过ARP协议得到,所以这对用户是透明的,但是IP地址就不行,无论如何用户都需要用一个指定的IP来访问一台计算机,而IP地址又非 ...

  10. OS X EI Capitan 10.11 & xcode 7.0 beta(7A120f) -- cocoapods安装失败

    1.sudo gem install cocoapods: ERROR:While executing gem ... (Errno:EPERM) Operation not permitted - ...