Netty protobuf 整合 -- 使protobuf可同時處理多種類型

簡要介紹apache

在帶寬固定的情境下,壓縮消息大小能夠提高網絡傳輸效率。另外,若是消息須要通過多個組件,那麼收益更爲可觀。數組

消息序列化通常不會採用jdk自帶的Serializable,更多的會採用thrift或者protobuf來作編解碼。網絡

thriftapp

protobufgoogle

Netty 整合protobuf困境設計

在作序列化的時候,每每趕上一種困境,在pipeline添加編解碼的時候,只能添加一種數據類型編解碼。代理

以下所示netty

pipeline.addLast(new ProtobufDecoder(LoginAsk.getDefaultInstance));
pipeline.addLast(new ProtobufEncoder());

上述的代碼只能接收LoginAsk,沒法處理其餘類型的請求。code

有一種較爲直接的方式,開啓多個端口,每個端口處理特定的業務,不一樣消息之間用netty代理轉發。當業務須要常常和其餘類型交互時,因爲多了屢次轉發,代碼會變得複雜而不可控。ip

解決思路

ProtobufDecoder只接受一種實例化方式,傳遞特定的class,因此只能從這個類型突破。很容易想到,將這個類做爲轉發器,即,能經過這個類的特定字段定位到特定的class,而且包含body(結合class反序列化成instance)。

由此可得,消息協議能夠被設計成head,body,tail三部分。head可包含消息類型msgType,消息長度msgLen,業務消息全限定名clazzName; body爲業務數據序列化後的byte數組。tail可選,可加入CRC校驗等功能。

如今可將問題轉換成,獲取到特定的className和byte數組後,如何反序列化成instance。通過屢次嘗試,須要通過如下步驟,經過反射修改構造器,並獲取到實例,而後經過實例反射調用方法獲取到parser。

代碼以下:

// 經過classname獲取實例
public static Object getInstance(Class<?> clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
        boolean accessible = declaredConstructor.isAccessible();
        declaredConstructor.setAccessible(true);
        Object obj = declaredConstructor.newInstance();
        declaredConstructor.setAccessible(accessible);
        return obj;
}

   private static String PARSER_METHOD_NAME = "getParserForType";

// 反射調用獲取parser
    public static Parser reflectGetParser(Object instance) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> clazz = instance.getClass();
        Method[] declaredMethods = clazz.getDeclaredMethods();
        Method targetMethod = Stream.of(declaredMethods)
                .filter(method -> method.getName().equals(PARSER_METHOD_NAME))
                .findAny().get();
        Object invoke = targetMethod.invoke(instance);
        return (Parser) invoke;
    }

    public static Parser reflectGetParser(Class<?> clazz) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        Object instance = ReflectionHelper.getInstance(clazz);
        return reflectGetParser(instance);
    }

經過以上的步驟,修改了原有的消息定義模式。將業務消息加了一層包裝,在發送消息的時候,將業務消息包裝成一個MsgWrapper序列化後發送;在接收消息的時候,解開頭部消息,結合body,反序列化成特定消息的示例。

相關文章
相關標籤/搜索