工作中要把原来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. CodeForces 629C Famil Door and Brackets

    DP. 具体做法:dp[i][j]表示长度为 i 的括号串,前缀和(左括号表示1,右括号表示-1)为 j 的有几种. 状态转移很容易得到:dp[i][j]=dp[i - 1][j + 1]+dp[i ...

  2. Vue.js起步

    Vue.js是一套构建用户界面的 渐进式框架,Vue 采用自底向上增量开发的设计,Vue 的核心库只关注视图层.Vue 完全有能力驱动采用单文件组件和 Vue 生态系统支持的库开发的复杂单页应用. V ...

  3. Java Base64、AES、SHA1、MD5加密算法(转载)

    package com.example.decript; import java.io.UnsupportedEncodingException; import java.security.Inval ...

  4. WIFI机器人网

    WIFI机器人网 WIFI智能小车机器人 外网远程控制WIFI智能小车机器人(WIFI板/703N)

  5. js - AO链 与 function

    先来看一下demo,如果你已经看出三个console.log分别输出什么.那直接关闭此笔记 function t(age) { console.log(age); var age = 99; cons ...

  6. Xcode6之后创建Pch预编译文件

    在Xcode6之前,创建一个新工程xcode会在Supporting files文件夹下面自动创建一个“工程名-Prefix.pch”文件,也是一个头文件,pch头文件的内容能被项目中的其他所有源文件 ...

  7. mac解压缩

    tar 解包:tar xvf FileName.tar打包:tar cvf FileName.tar DirName(注:tar是打包,不是压缩!)---------------.gz解压1:gunz ...

  8. 子窗口url调整导致父窗口刷新

    2014年3月19日 10:22:38 如题: 在弹窗里搜索时,url发生改变,导致父窗口的div消失.为何? 之前的逻辑是隐藏div 现在修改为插入节点 .可是还是刷新字窗口后,父窗口里面的div节 ...

  9. Python爬虫框架Scrapy安装使用步骤

    一.爬虫框架Scarpy简介Scrapy 是一个快速的高层次的屏幕抓取和网页爬虫框架,爬取网站,从网站页面得到结构化的数据,它有着广泛的用途,从数据挖掘到监测和自动测试,Scrapy完全用Python ...

  10. 关键词匹配(Ac自动机模板题)

    2772: 关键词匹配 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 10  Solved: 4[Submit][Status][Web Board] ...