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,跳过了正常解析 。

原文地址:https://www.cnblogs.com/heshana/p/13597461.html