首页 > 代码库 > gRPC中Any类型的使用(Java和NodeJs端)
gRPC中Any类型的使用(Java和NodeJs端)
工作中要把原来Java服务端基于SpringMVC的服务改为使用gRPC直接调用。由于原Service的返回值为动态的Map类型,key值不确定,且value的类型不唯一,因此使用了protobuf 3中的map和Any类型。在这个过程中遇到了一些困难,查阅资料时发现这一块的资料不是很多,尤其是在NodeJS的gRPC-Client处理google.protobuf.Any类型,完全找不到相关的资料。好在自己摸索和调试后解决了问题,因此记录下来以供他人之需。
testservice.proto:
1 syntax = "proto3"; 2 3 import "google/protobuf/any.proto"; 4 option java_package = "com.zfp.demo.grpc"; 5 6 service TestService { 7 rpc getMapData (Param) returns (GenericMap); 8 } 9 10 message GenericMap {11 map<string, google.protobuf.Any> value = http://www.mamicode.com/1;12 }13 14 message Param{15 string value = http://www.mamicode.com/1;16 }17 18 message ListParam{19 repeated string value = http://www.mamicode.com/1; 20 }
其中,Any类型的作用是在protobuf中不需要明确定义值的结构和类型,而是在gRPC的Server端通过pack()将任何message打包成Any类型(不可以直接打包Java Object),在client可以通过unPack()将message从Any中取出,实现了protobuf对泛型的支持。
Java Server:
1 import com.google.protobuf.Any;
2 @Overried 3 public void getMapData(Testservice.Param request, StreamObserver<Testservice.GenericMap> responseObserver) { 4 5 Testservice.Param stringValue =http://www.mamicode.com/ Testservice.Param 6 .newBuilder() 7 .setValue("this is String type") 8 .build(); 9 10 List<String> tempList = Lists.newArrayList("this is", "List type");11 Testservice.ListParam listValue =http://www.mamicode.com/ Testservice.ListParam12 .newBuilder()13 .addAllValue()14 .build();15 16 Map<String, Any> reMap = Maps.newHashMap();17 reMap.put("k1", Any.pack(stringValue));18 reMap.put("k2", Any.pack(listValue));19 20 Testservice.GenericMap genericMap = Testservice.GenericMap21 .newBuilder()22 .putAllValue(reMap)23 .build();24 25 responseObserver.onNext(genericMap);26 responseObserver.onCompleted();27 }
Java Client:
1 @Test 2 public void getMapDataTest() throws ExecutionException, InterruptedException { 3 4 ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 6565) 5 .usePlaintext(true) 6 .build(); 7 8 TestServiceGrpc.RoomServiceBlockingStub bkStub = TestServiceGrpc.newBlockingStub(channel); 9 10 11 Testservice.Param param = Testservice.Param.newBuilder().setValue("test param").build();12 13 Map<String, Any> reMap = bkStub.getMapData(param).get().getValueMap();14 15 Map<String, Object> dataMap = Maps.newHashMap();16 17 reMap.forEach((k, v) -> {18 if (k.equals("k1")) {19 try {20 dataMap.put(k, v.unpack(Testservice.Param.class).getValue());21 } catch (InvalidProtocolBufferException e) {22 e.printStackTrace();23 }24 } else {25 try {26 dataMap.put(k, v.unpack(Testservice.ListParam.class).getValueList());27 } catch (InvalidProtocolBufferException e) {28 e.printStackTrace();29 }30 }31 });32 33 logger.info(JSON.toJSONString(dataMap, true));34 }
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端)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。