1. 程式人生 > >Netty + ProtoBuf使用反射實現多種類型的傳輸方式

Netty + ProtoBuf使用反射實現多種類型的傳輸方式

sta attribute short ext 低耦合 就是 是否 AD pat

  本文是該文章的後續(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中去,最大的優點是降低耦合吧。

Netty + ProtoBuf使用反射實現多種類型的傳輸方式