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

相关文章
相关标签/搜索