簡要介紹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,反序列化成特定消息的示例。