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