简要介绍
在带宽固定的情境下,压缩消息大小可以提升网络传输效率。另外,如果消息需要经过多个组件,那么收益更为可观。
消息序列化一般不会采用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,反序列化成特定消息的示例。
来源:oschina
链接:https://my.oschina.net/u/992559/blog/3062120