问题描述
使用netty构造服务端,使用浏览器在地址栏输入访问地址时,除了会发送目标url之外,浏览器还会额外发送一个url–>/favicon.ico,以前是在一个业务处理的hanlder中使用if判断uri,如果uri是这个的话,就不去执行业务逻辑,但是这样的话会造成业务代码与判断逻辑的耦合。所以使用一个新的handler去处理,利用netty的执行连完成后序的业务。
handler的构造执行顺序如下
HttpRequestDecoder(netty自带)–>FilterurlHandler–>HttpNettyRequestDecoder(二次解码器,自定义)–>serviceHandler(业务handler)–>HttpNettyResponseEncoder(二次编码器,自定义)–>HttpResponseEncoder(netty自带)
其中filterurlhandler,serviceHandler继承SimpleChannelInboundHandler

netty自定义url过滤器抛引用异常

netty自定义url过滤器抛引用异常
但是当浏览器访问的时候,比如url【/queryapi?index=1】,浏览器可以收到netty服务端返回的信息,但是netty的控制台会抛出异常
postman模拟发送
netty自定义url过滤器抛引用异常

netty自定义url过滤器抛引用异常

控制台异常如下
netty自定义url过滤器抛引用异常
而且根据异常的堆栈信息也可以看出,里面没有涉及到自己编写的业务代码。
排查
首先是filterUnuseHandler,通过代码执行,可以看到,该handler已经生效了(注意红圈中的FullRequest对象的内存地址,稍微会用到),并且通过调用firechannelRead方法通知下一个符合入参的handler
netty自定义url过滤器抛引用异常
上述firechannelRead通知的handler是二次解码器HttpNettyRequestDecoder,该类是继承SimpleChannelInboundHandler。
根据执行链进入到MessageToMessageDecoder.channelRead方法
netty自定义url过滤器抛引用异常
HttpNettyRequestDecoder继承的就是这个类,并且实现了decode方法
会过来继续看MessageToMessageDecoder.channelRead方法
netty自定义url过滤器抛引用异常
idea断点信息
netty自定义url过滤器抛引用异常
根据代码可知,cast是msg的一个备份,在decode二次解码之后,在finally中被释放
同时,在channelRead方法的finally块中,继续执行责任链,注意入参,此时已经变成了HttpNettyRequest(二次解码后我们自定义的对象),因为我们在二次解码器中完成解码之后,需要将二次解码对象加入到list中(所以这个意思就是寻找下一个handler,并且该handler的一个入参的类型是HttpNettyRequest)
netty自定义url过滤器抛引用异常
firechannelRead的执行顺序还是和上面说的一样
最终进入到业务handler中,执行代码(注意,此时原先的那个request对象依然存在在内存中,[email protected],没有被释放)
业务代码具体执行就不说了,业务类是继承SimpleChannelInboundHandler,最终业务handler中生成了业务消息,需要将其编码为HttpRespone,但是此时是HttpNettyResponse(我们自定义的对象)。
编码过程(HttpNettyResponse–>HttpResponse)与request的二次解码过程类似,也是netty在业务代码执行完之后,获得了HttpNettyResponse对象,需找handler,该handler的入参是HttpNettyResponse,进行二次编码,最终代码回到FilterUnuseUrl中
netty自定义url过滤器抛引用异常
注意红圈内,原先的request对象依旧存在
执行下一步,退出了重写方法MessageReceived。由于之前说过,FilterUnuseUrl是继承SimpleChannelInboundHandler类的,所以返回到父类中
netty自定义url过滤器抛引用异常
这个finally是不是很熟悉,之前也看到过。同时看finally中释放的msg的地址,就是我们一开始说留意的那个request对象,所以在finally中,request对象又被释放
所以request对象被释放了两次,所以会抛出异常。
解决办法
过滤url的handler不要继承SimpleChannelInboundHandler,直接继承ChannelHandlerAdaptor即可。因为request的第一次被释放引用是在MessageToMessage中,暨二次解码完毕后,但是这是没法改的,所以直接继承适配器类,避免二次释放request

最后,不知道这算不算netty的一个bug,就是不能二次解码器继承MessageToMessageDecoder,同时另外一个类继承SimpleChannelInboudHandler,会抛非法引用异常

相关文章: