netty優化 (-) websocket解碼異常后自動斷開連接處理


背景:

  公司需要25台設備組網,用戶通過客戶端登錄后對25台機子進行監控操作(包括視頻播放)。 

技術方案:

  產品分為設備端、客戶端、服務端。為兼容以后的瀏覽器訪問,選java搭建服務器。服務器主要業務包括客戶端用戶管理、客戶端業務指令、權限;設備端登記、發現、在線監測、分組管理、權限。

  由於環境比較簡單,后台服務采用netty的websocket協議進行通信,消息指令進行權限管理。

問題描述:

  1、25台設備搭建后進行壓力測試,百兆路由可25路視頻的2個客戶端,3個客戶端同時打開會導致設備掉線頻繁,(添加重連限制客戶端個數)。

  2、OOM,outof direct memory,此問題很懵逼。netty中derectmemory 是框架中進行計數處理的,測試中計數增長到一定值后保持穩定不存在超出;channelread0方法中會自動釋放bytebuf; 此問題無法重現,只好添加jvm內存待以后重現再處理!

  3、長時間掛機無任何操作出現客戶端或者設備掉線問題,查看日志多是和decode解碼有關,消息異常解碼出錯,netty自動關閉通道斷開了連接。

      測試結果:設備端掉線明顯;消息解析錯誤后直接關閉了連接;偶爾出現一個大的數據包接收一半后斷開連接;

    websocket基於TCP協議,在不穩定的網絡環境下發送大量數據,並且發送頻率非常高,很可能會出現錯誤(1、程序處理邏輯錯誤;2、多線程同步問題;3、緩沖區溢出等)。這掉線的頻率讓人很難接收,抓包也是抓的崩潰, 放棄了! 幾個同事之間可能也都踢了好幾周的皮球,呵呵,感覺對不起公司的同事們。首先讓客戶端和設備端全部添加了斷線重連、優化設備端發送頻率、服務端緩存一些消息。

   業務上做了優化之后,掉線有所緩解,但是偶爾一次的掉線的確讓人抓狂,尤其是這么小的局域網中,為了從這個鍋中脫離, 決定還是要有所優化, 可怕的框架bug ~~

   A.下netty參數,消息隊列默認128 ,加到1024 ; 避免數據包的緩存

    ServerBootstrap b = new ServerBootstrap();
    b.option(ChannelOption.SO_BACKLOG, 1024)
    .childOption(ChannelOption.TCP_NODELAY,true)

     B.  異常不關閉 

      重寫WebSocketDecoderConfig.closeOnProtocolViolation修改默認值。

      ByteToMessageDecoder 中解析完后,

      callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) 方法中 將 WebSocket08FrameDecoder的 state 重置為 WebSocket08FrameDecoder.State.READING_FIRST

      重寫ByteToMessageDecoder .java 中callDecode 添加抽象方法initState

    protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        try {
            while(true) {
                if (in.isReadable()) {
                    int outSize = out.size();
                    if (outSize > 0) {
                        fireChannelRead(ctx, out, outSize);
                        out.clear();
                        if (ctx.isRemoved()) {
                            return;
                        }

                        outSize = 0;
                    }

                    int oldInputLength = in.readableBytes();
                    this.decodeRemovalReentryProtection(ctx, in, out);
                    if (!ctx.isRemoved()) {
                        if (outSize == out.size()) {
                            if (oldInputLength != in.readableBytes()) {
                                continue;
                            }
                        } else {
                            if (oldInputLength == in.readableBytes()) {
                                throw new DecoderException(StringUtil.simpleClassName(this.getClass()) + ".decode() did not read anything but decoded a message.");
                            }

                            if (!this.isSingleDecode()) {
                                continue;
                            }
                        }
                    }
                }
                if (  ctx.name().equals("wsdecoder")){
                    try{
                        this.initState(ctx, in, out);
                    }catch (Exception E){
                    }
                }
                return;
            }



        } catch (DecoderException var6) {
            throw var6;
        } catch (Exception var7) {
            throw new DecoderException(var7);
        }
    }
    protected abstract void initState(ChannelHandlerContext var1, ByteBuf var2, List<Object> var3) throws Exception;

  

重寫WebSocket08FrameDecoder.java 中添加initState
    protected void initState(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if(this.state == WebSocket08FrameDecoder.State.CORRUPT){
            this.state = WebSocket08FrameDecoder.State.READING_FIRST;
        }

    }

      不將state 設置為READING_FIRST ,通道解析出現異常后,WebSocket08FrameDecoder每次消息解析都會走CORRUPT,跳過了正常解析 。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM