gRPC中Any类型的使用(Java和NodeJs端)
工作中要把原来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端)的更多相关文章
- 详解Java 8中Stream类型的“懒”加载
在进入正题之前,我们需要先引入Java 8中Stream类型的两个很重要的操作: 中间和终结操作(Intermediate and Terminal Operation) Stream类型有两种类型的 ...
- Java中枚举类型简单学习
/* * enum类型不允许继承 * 除了这一点,我们基本上可以将enum看作一个常规的类 * 我们可以添加自己的方法与属性,我们也可以覆盖其中的方法. * 不过一定要在enum实例序列的最后添加分号 ...
- Java中double类型的数据精确到小数点后两位
Java中double类型的数据精确到小数点后两位 多余位四舍五入,四种方法 一: double f = 111231.5585;BigDecimal b = new BigDecimal(f); d ...
- java中基本类型封装对象所占内存的大小(转)
这是一个程序,java中没有现成的sizeof的实现,原因主要是java中的基本数据类型的大小都是固定的,所以看上去没有必要用sizeof这个关键字. 实现的想法是这样的:java.lang.Runt ...
- Java中泛型 类型擦除
转自:Java中泛型是类型擦除的 Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类 ...
- java中基本类型占用字节数
之前一直使用c/c++开发c中各种类型占用的位数和java还是有区别的,特地找了篇文章过来对比下. 在处理网络协议的时候需要注意 在Java中一共有8种基本数据类型,其中有4种整型,2种浮点类型,1种 ...
- Java进阶(二十三)java中long类型转换为int类型
java中long类型转换为int类型 由int类型转换为long类型是向上转换,可以直接进行隐式转换,但由long类型转换为int类型是向下转换,可能会出现数据溢出情况: 主要以下几种转换方法,供参 ...
- java和数据库中日期类型的常见用法
(1)java中日期类型:Date.Timestamp(2)数据库中:Date.Timestamp(3)字符串和Date之间的格式化转换: SimpleDateFormat类方法: format ...
- JAVA中值类型和引用类型的不同(面试常考)
转载:https://www.cnblogs.com/1ming/p/5227944.html 1. JAVA中值类型和引用类型的不同? [定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个 ...
随机推荐
- iOS开发——沙箱
iphone沙箱模型的有三个文件夹,documents,tmp,Library.有时开发时要求我们保存一些数据在本地,这就用到了. 1.Documents 目录:您应该将所有de应用程序数据文件写入到 ...
- Android和BLE模块连接通信
首先,进行一下科普: 1.BLE(Bluetooth Low Energy),蓝牙4.0核心profile,主要特点是快速搜索,快速连接,超低功耗保持连接和数据传输,缺点:数据传输速率低,由于其具有低 ...
- Android与JNI(三) ---- c++调用java(转载)
源码下载:JniDemo JNI就是Java Native Interface, 即可以实现Java调用本地库, 也可以实现C/C++调用Java代码, 从而实现了两种语言的互通, 可以让我们更加灵活 ...
- leangoo
leangoo网址:https://www.leangoo.com/
- android 菜单的总结
安卓菜单有三种菜单. 选项菜单: 点击系统菜单按钮会触发 上下文菜单:长按屏幕触发 子菜单:某一个菜单的下一级菜单 具体的描叙:http://blog.csdn.net/zqiang_55/artic ...
- iOS 之 Quartz2D
1. Quartz2D 之 简单介绍 2. Quartz2D 之 简单使用 3. Quartz2D 之 绘制文本
- 破解&屏蔽防止嵌入框架代码 top.location != self.location
<script type="text/javascript"> if (top.location != self.location) top.location = se ...
- 常用的JavaScript字符串处理函数及用法
最近参加了几次前端实习生招聘的笔试,发现很多笔试题都会考到字符串的处理,比方说去哪儿网笔试题.淘宝的笔试题等,如果你经常参加笔试或者也是一个过来人,相信你也跟我一样,发现字符串的处理是前端招聘过程中最 ...
- centos 安装mysql 5.5.12
1.安装gcc-c++ gcc make cmake编译器 2.安装ncurses 3.添加用户组 groupadd mysql useradd -r -g mysql mysql 4.安装 tar ...
- HTML 5 服务器发送事件、Input 类型、表单元素、表单属性
HTML5 服务器发送事件(server-sent event)允许网页获得来自服务器的更新. Server-Sent 事件 - 单向消息传递 Server-Sent 事件指的是网页自动获取来自服务器 ...