netty客户端引发的线程血案(三)
前言
前文讲述了netty 3.10.5客户端的实现,我们知道了为什么每次递增一定数量的线程,这个跟客户端的线程池的实现机制有很大关系,本文,我们梳理一下3.2.4的客户端流程,寻找一下3.2.4版本不存在线程雪崩的原因。
netty客户端
我们开始先从引导类ClientBootstrap开始我们的旅程,在实例化ClientBootstrap时,我们需要构建channelFactory,代码见下:
// Configure the client.
ClientBootstrap bootstrap = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
可以看到构建了2个newCachedThreadPool线程池,分别是boss和work线程池,work线程池的大小为CPU核数*2,初始的时候,线程池中没有线程,这里跟3.10.5有很大区别,3.10.5在初始化的时候,就把需要的boss线程和work线程都启动起来了。
bootstrap.connect(new InetSocketAddress(host, port))中创建channel和3.10.5基本一致,不在详述,重点看一下ChannelState.CONNECTED触发的连接操作,代码:
private void connect(
final NioClientSocketChannel channel, final ChannelFuture cf,
SocketAddress remoteAddress) {
try {
if (channel.socket.connect(remoteAddress)) {
channel.worker.register(channel, cf);
} else {
//走此分支
channel.getCloseFuture().addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture f)
throws Exception {
if (!cf.isDone()) {
cf.setFailure(new ClosedChannelException());
}
}
});
cf.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
channel.connectFuture = cf;
//调用boss.register
boss.register(channel);
}
} catch (Throwable t) {
cf.setFailure(t);
fireExceptionCaught(channel, t);
channel.worker.close(channel, succeededFuture(channel));
}
}
boss.register(channel)用来创建select,将select注册到channel上,然后select监听key,监听到事件后,按照下面调用流程处理:
processSelectedKeys(selector.selectedKeys())-> connect(k)->ch.worker.register(ch, ch.connectFuture)
在register中会创建select,创建一个work线程放到线程池中,然后启动线程,