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

原创
2019/11/08 15:37
阅读数 2.2K

背景:

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

 

前提:

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

 

关键信息:

类:

ChannelUpstreamHandler

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

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,所以打印了一句日志,然后就返回了,也就是不处理了。。

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

 

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

 

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部