Netty protobuf 整合 -- 使protobuf可同時處理多種型別
簡要介紹
在頻寬固定的情境下,壓縮訊息大小可以提升網路傳輸效率。另外,如果訊息需要經過多個元件,那麼收益更為可觀。
訊息序列化一般不會採用jdk自帶的Serializable,更多的會採用thrift或者protobuf來做編解碼。
Netty 整合protobuf困境
在做序列化的時候,往往遇上一種困境,在pipeline新增編解碼的時候,只能新增一種資料型別編解碼。
如下所示
pipeline.addLast(new ProtobufDecoder(LoginAsk.getDefaultInstance)); pipeline.addLast(new ProtobufEncoder());
上述的程式碼只能接收LoginAsk,無法處理其他型別的請求。
有一種較為直接的方式,開啟多個埠,每一個埠處理特定的業務,不同訊息之間用netty代理轉發。當業務需要經常和其他型別互動時,由於多了多次轉發,程式碼會變得複雜而不可控。
解決思路
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,反序列化成特