dubbo服务端线程池溢出,异常客户端无响应超时-Dubbo,Netty代码解读

背景:this

客户端调用dubbo服务端,dubbo服务端线程溢出,客户端没法收到响应,请求超时spa

 

前提:线程

建议打开netty3.7.0Final代码,dubbox2.8.4代码,边读边看代码。3d

 

关键信息:日志

类:netty

ChannelUpstreamHandlercode

请求消息流进NettyServer端(当前代码重点关注)blog

ChannelDownstreamHandler继承

NettyServer端响应返回(当前代码分析不涉及)接口

ChannelPipeline

NettyServer解析数据的职责链

ChannelHandler

消息处理器(AllHandler)

Sink

消息最后一步处理

 

netty事件

MessageEvent:普通请求消息事件

ExceptionEvent:异常事件

 

方法:

sendUpStream()

流入信息总入口

handleUpstream()

处理流入信息

received()

消息事件处理

caught()

异常事件消息

 

先分析下NettyServer的构造,此处为静态信息(信息尚未流入进来)

编解码channelHandler

NettyServer -> pipeline.addLast("decoder", adapter.getDecoder());

NettyServer -> pipeline.addLast("encoder", adapter.getEncoder());

 

核心处理业务channelHandler

final NettyHandler nettyHandler = new NettyHandler(this.getUrl(), this);

NettyServer -> pipeline.addLast("handler", nettyHandler);

将NettyHandler增长到nettyServer的职责链中,处理请求消息

 

Handler类继承关系:

NettyHandler ->SimpleChannelHandler -> ChannelUpstreamHandler(处理流入消息总接口)->ChannelHandler(顶级接口sendUpstream()方法)

 

NettyServer会按照pipline职责链执行每一个ChannelHandler的sendUpstream()方法,ChannelUpstreamHandler实例的handleUpstream方法

因此此处 sendUpstream() 做为全部请求消息总入口,后面的分析均今后接口开始。

 

如下开始分析消息流入处理流程:

1.流入一个dubbo请求信息

DefaultChannelPipeline.sendUpstream()职责链驱动

核心代码在此,调用handler的handlerUpstream()(能够理解为reveived),若是received处理异常,则执行notifyHandlerException()

 

2.SimpleChannelHandler.handleUpstream()执行职责单元中的方法

 

3.消息类型为MessageEvent,交给messageReceived()方法处理, NettyHandler实现了messageReceived()方法,因此由NettyHandlermessageReceived()方法处理

NettyHandler.messageReceived(ctx, (MessageEvent)e);

 

4.messageReceived方法内调用handler.received()方法,

this.handler.received(channel, e.getMessage());

此时的handler使用装配器模式,已经变成了AllChannelHandler(主角出场了)

 

5.AllChannelHandler因为线程池溢出,致使执行失败,抛出异常

 

6.按调用链层层返回,则返回到DefaultChannelPipeline.sendUpstream()中,执行notifyHandlerException()方法

 

7.继续跟踪notifyHandlerException()方法

关注两个点:

1)当前事件仍是MessageEvent,因此不走第一个if

2)调用sink.exceptionCaught()方法处理异常,此处的sink是谁呢????

 

8.回看一下NettyServer里边是否认义了sink??(sink是pipline中的最后一个元素)

好像没有定义sink????

咱们再看下第一个红框中的代码,这里new了一个ChannelFactory,看看里边有什么玄机??

层层跟进,找到根构造方法,sink终于现身了 NioServerSocketPipelineSink继续看 NioServerSocketPipelineSink中的exceptionCaught()方法中干了啥??

很失望,NioServerSocketPipelineSink没有实现exceptionCaught()方法,那就找父类,最终找到了实现了exceptionCaught()方法的实例 AbstractChannelSink

 

代码好多,继续深刻,当前执行线程是iothreads

看一下这个if判断里作了啥???

AbstractNioChannelSink重写了AbstractChannelSink的该方法,此处该方法,应该返回false

如今代码执行到了fireExceptionCaught,继续跟踪

向管道里推了一个异常事件sendUpstream(异常事件),继续跟踪,代码执行和前面一些步骤同样,只是事件类型变动了,因此咱们只看关键代码

 

跟踪到SimpleChannelHandler的handleUpstream方法,此次,走异常事件流程,执行exceptionCaught(异常事件)方法

一样,NettyHandler实现了exceptionCaught(异常事件)方法,代码以下

敲黑板,重点重点!!!,此时能够理解AllChannelHandler要执行caught方法了,截止到目前,终于跟踪到了AllChannelHandler第一次线程溢出异常,调用了本身的caught方法,基本找到了dubbo在何处调用caught方法的,本次分析任务就要结束了??

 

第二大段:

本着打破砂锅问到底的钻研精神,这个时候,若是dubbo处理了这个异常,那消费者应该会收到一个失败或者异常,消费者就会处理失败消息,可是现状为什么是消费者由于没有收到响应而超时呢????

继续跟踪

此时,执行AllChannelHandler的caught方法,会不会有问题呢? 实际状况是,确实有问题,因为业务线程池满了AllChannelHandler的caught方法继续执行失败。。啥?又抛异常了??!!!

我们再次回到,sendUpstream()方法,看看系统如何处理二次异常??

巩固记忆,入口图,再贴一次

继续跟踪第二次异常处理流程

和第一次处理异常流程不同了,此时的ChannelEvent为ExceptionEvent,因此打印了一句日志,而后就返回了,也就是不处理了。。

而后就没而后了,这个请求就这样被沉入大海了,客户端没有收到任何响应。。。

 

本次分析就完全结束了,客户端超时,真相大白。

相关文章
相关标签/搜索