Netty + ProtoBuf使用反射实现多种类型的传输方式

  本文是该文章的后续(https://www.cnblogs.com/Binhua-Liu/p/5577622.html),该文章介绍了在ProtoBuf上再加一层Header从来归避ProtoBuf在Netty框架下只能有一有对象的局限。 在蘑菇街开源的TeamTalk中,也是加了一个Header,根据Header中的操作类型,做出相应的逻辑处理操作。

  下面是我们的配置文件,用于实现ServerCustomDecodeHandler中的decodeBody的逻辑。ProtoBuf通过反射生成对象需要使用“包$类”的形式。

<?xml version="1.0" encoding="UTF-8"?>
<decodetypes>
    <decodetype type="com.zywj.protobuf.out.IMBase$MessageCmdID" val="CID_MSG_DATA_VALUE" class="com.zywj.protobuf.out.IMMessage$MsgData" />
    <decodetype type="com.zywj.protobuf.out.IMBase$OtherCmdID" val="CID_OTHER_HEARTBEAT_VALUE" class="com.zywj.protobuf.out.IMOther$HeartBeat" />
</decodetypes>

  读取XML的工作放在Server中,声明为private static。这里是根据我自己的逻辑生成的一个Map变量。

private static Map parseXmlData(String xmlFilePath){
        SAXReader reader = new SAXReader();
        Document doc = null;
        try {
            doc = reader.read(new File(xmlFilePath));
        } catch (DocumentException e) {
            e.printStackTrace();
        }

        Element root  = doc.getRootElement();
        Map<String,Map> map = new HashMap<>();
        for(Iterator i_action=root.elementIterator();i_action.hasNext();){
            Element e_action = (Element)i_action.next();

            Iterator it = e_action.attributeIterator();
            Attribute key = (Attribute) it.next();
            String type = key.getValue();

            Map<String, String> inner = new HashMap<>();
            while(it.hasNext()) {
                Attribute attribute = (Attribute) it.next();
                inner.put(attribute.getName(), attribute.getValue());
            }
            map.put(type, inner);
        }

        return map;
    }

   ServerCustomDecodeHandler的decodeBody的工作是根据Map中的数据通过反射生成对象,这里需要判断当前对象的enum值是否是与commandId相等,如果找到,就生成相对应的ProtoBuf对象。

    public MessageLite decodeBody(short commandId, byte[] array, int offset, int length) throws Exception {
        Map<String, Map> decodeMap = Server.decodeMap;

        for (Map.Entry<String, Map> entry : decodeMap.entrySet()) {
            Map<String, String> m = decodeMap.get(entry.getKey());
            Class clazz = Class.forName(entry.getKey());
            Field f = clazz.getField(m.get("val"));
            if ((int)f.get(clazz) == commandId) {
               Class cls = Class.forName(m.get("class"));
               Method method = cls.getMethod("getDefaultInstance");
               Object obj = method.invoke(cls, null);

               method = cls.getMethod("getParserForType");
               obj = method.invoke(obj, null);
               return ((Parser<MessageLite>) obj).parseFrom(array, offset, length);
            }
        }
        return null;
    }

  到这里,还没完,因为ServerHandler里面传进来的是Object msg,所以还要用instanceof判断该Object到底是ProtoBuf的对象。所以又是一个XML,再反射。

<?xml version="1.0" encoding="UTF-8"?>
<msgtypes>
    <msgtype type="com.zywj.protobuf.out.IMMessage$MsgData" class="com.zywj.server.LogicHandler" method="handleMsgData" />
    <msgtype type="com.zywj.protobuf.out.IMOther$HeartBeat" class="com.zywj.server.LogicHandler" method="handleHeartBeat" />
</msgtypes>

  ServerHanlder中的channelRead0改成

  @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        Map<String, Map> dispatchMap = Server.dispatchMap;

        for (Map.Entry<String, Map> entry : dispatchMap.entrySet()) {
            Class clazz = Class.forName(entry.getKey());

            if (msg.getClass().isAssignableFrom(clazz)) {
                Map<String, String> m = dispatchMap.get(entry.getKey());

                Class clazzLogic = Class.forName(m.get("class"));
                Method handle = clazzLogic.getDeclaredMethod(m.get("method"), ChannelHandlerContext.class, Object.class);
                handle.invoke(clazzLogic.newInstance(), ctx, msg);

                break;
            }
        }
    }

  SeverCustomEncodeHandler也是相同方法,不再赘述。

  到这里,我们利用反射,把上面文章中过多的if...else...归避了。

  最后,我觉得这样实现会有过多的反射(循环反射再判断,时间复杂度O(n)),还有就是代码量其实并没有减少,只是转移到XML中去,最大的优点是降低耦合吧。

猜你喜欢

转载自www.cnblogs.com/zywj/p/9216144.html