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 = 1;
12 }
13
14 message Param{
15     string value = 1;
16 }
17
18 message ListParam{
19     repeated string value = 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 = 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 = Testservice.ListParam
12                                      .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.GenericMap
21                            .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

时间: 2024-10-29 16:10:46

gRPC中Any类型的使用(Java和NodeJs端)的相关文章

java中整数类型的2进制表示

java中的4种整数类型:byte,short,int,long均采用补码表示. 如下图右边所示 在java中对二进制数没有"字面"表示方法.十六进制可以用OX或者Ox指示,八进制可以用O指示. 下面的代码可以输出byte类型的数据的2进制表示形式: public class Test { public static void main(String[] args) { byte a = -128; test(a); } public static void test(byte m){

Java中值类型与引用类型

JAVA中值类型和引用类型的不同? 1.定义 引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值,那么调用这个方法是传入的变量的值也将改变:值类型表示复制一个当前变量传给方法,当你在这个方法中改变这个变量的值时,最初生命的变量的值不会变.通俗说法: 值类型就是现金,要用直接用:引用类型是存折,要用还得先去银行取现. 2.分类 (1)值类型 值类型也就是基本数据类型 基本数据类型常被称为四类八种. 四类:1.整型 2.浮点型 3.字符型4.逻辑

Java中String类型详解

这篇博客是我一直想总结的,这两天一直比较忙,先上传下照片吧,过后有时间再弄成正常的. 本文主要是对Java中String类型的总结,包括其在JVM中是怎么存储的...

Java中double类型的数据精确到小数点后两位

Java中double类型的数据精确到小数点后两位 多余位四舍五入,四种方法 一: double f = 111231.5585;BigDecimal b = new BigDecimal(f); double f1 = b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue(); 二: new java.text.DecimalFormat("#.00").format(3.1415926) 三: double d = 3.1415926

java中String类型转换方法

integer to String : int i = 42;String str = Integer.toString(i);orString str = "" + idouble to String :String str = Double.toString(i);long to String :String str = Long.toString(l);float to String :String str = Float.toString(f);String to intege

Java中泛型 类型擦除

转自:Java中泛型是类型擦除的 Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类型在编译后都会被清除掉,看下面一个列子,代码如下: public class Foo { public void listMethod(List<String> stringList){ } public void listMethod(List<Integer> intList) {

详解Java 8中Stream类型的“懒”操作

在进入正题之前,我们需要先引入Java 8中Stream类型的两个很重要的操作: 中间和终结操作(Intermediate and Terminal Operation) Stream类型有两种类型的方法: 中间操作(Intermediate Operation) 终结操作(Terminal Operation) 官方文档给出的描述为[不想看字母的请直接跳过]: Stream operations are divided into intermediate and terminal operat

(转)Java中String类型的参数传递问题

这篇文章主要介绍了简单谈谈Java中String类型的参数传递问题的相关资料,需要的朋友可以参考下 提要:本文从实现原理的角度上阐述和剖析了:在Java语言中,以 String 作为类型的变量在作为方法参数时所表现出的“非对象”的特性. 一.最开始的示例 写代码最重要的就是实践,不经过反复试验而得出的说辞只能说是凭空遐想罢了.所以,在本文中首先以一个简单示例来抛出核心话题: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public clas

java中基本类型封装对象所占内存的大小(转)

这是一个程序,java中没有现成的sizeof的实现,原因主要是java中的基本数据类型的大小都是固定的,所以看上去没有必要用sizeof这个关键字. 实现的想法是这样的:java.lang.Runtime类中有一些简单的能涉及到内存管理的函数: Every Java application has a single instance of class Runtime that allows the application to interface with the environment in