前2篇分析了echo server端的运行机制,本篇同样以echo client为例,分析netty的nio客户端的运行机制。
总体来说client端和server端的处理是类似的,NioWorker是重用的,也就意味着client和server的读写机制是一样的,都是通过worker线程来管理的。所不同的是Boss线程,server端的boss线程一个bind端口起一个,主要负责接收新请求,而client端的boss线程是一个可配置的数组,一个connect端口分配一个,主要负责connect过程,如果connect成功则将channle注册到worker线程中处理。在Client同样有PipelineSink,叫做NioClientSocketPipelineSink,也是负责底层IO和pipeline之间的交互。
EchoClient代码:
// 初始化Bootstrap和NioClientSocketChannelFactory,这一步将启动nioWorker线程,并初始化NioClientSocketPipelineSink,并将Boss线程创建
ClientBootstrap bootstrap = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// 用户自定义的pipeline工厂
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(
new EchoClientHandler(firstMessageSize));
}
});
// 异步创建连接
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
//等待连接关闭
future.getChannel().getCloseFuture().awaitUninterruptibly();
// 关闭资源,线程池等
bootstrap.releaseExternalResources();
具体connect过程:
一.创建client的channel
public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
......
//拿用户自定义的pipeline
ChannelPipeline pipeline;
try {
pipeline = getPipelineFactory().getPipeline();
} catch (Exception e) {
throw new ChannelPipelineException("Failed to initialize a pipeline.", e);
}
// 从ChannelFactory中,创建Channel,对于client来说factory是NioClientSocketChannelFactory,Channel是NioClientSocketChannel
Channel ch = getFactory().newChannel(pipeline);
// 通过channel连接
return ch.connect(remoteAddress);
}
二.创建channel时,分配worker
public SocketChannel newChannel(ChannelPipeline pipeline) {
return new NioClientSocketChannel(this, pipeline, sink, sink.nextWorker());
}
三.创建内部的SocketChannel,触发ChannelOpen事件,不过echo client的handler没有对channelOpen事件做处理
NioClientSocketChannel(
ChannelFactory factory, ChannelPipeline pipeline,
ChannelSink sink, NioWorker worker) {
//创建socketChannel,并配置成异步模式
super(null, factory, pipeline, sink, newSocket(), worker);
//触发open事件
fireChannelOpen(this);
}
四.创建socketChannel过程
private static SocketChannel newSocket() {
SocketChannel socket;
//创建SocketChannel
try {
socket = SocketChannel.open();
} catch (IOException e) {
throw new ChannelException("Failed to open a socket.", e);
}
boolean success = false;
//使用异步模式
try {
socket.configureBlocking(false);
success = true;
} catch (IOException e) {
throw new ChannelException("Failed to enter non-blocking mode.", e);
} finally {
......
}
return socket;
}
五.通过Channel进行connect
public static ChannelFuture connect(Channel channel, SocketAddress remoteAddress) {
if (remoteAddress == null) {
throw new NullPointerException("remoteAddress");
}
//是否连接成功的future
ChannelFuture future = future(channel, true);
//触发connected的downstream事件,对于echo client来说,由于没有downstream handler,所以直接被PipelineSink处理了
channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
channel, future, ChannelState.CONNECTED, remoteAddress));
return future;
}
六.NioClientSocketPipelineSink拿到CONNECTED的downstream事件,处理connect
case CONNECTED:
if (value != null) {
connect(channel, future, (SocketAddress) value);
} else {
channel.worker.close(channel, future);
}
break;
private void connect(
final NioClientSocketChannel channel, final ChannelFuture cf,
SocketAddress remoteAddress) {
try {
//尝试连接,如果成功,则直接将channel注册到worker线程中
if (channel.channel.connect(remoteAddress)) {
channel.worker.register(channel, cf);
}
//尝试连接失败,则将channel注册到某个boss线程中处理,该boss线程会接管该channel的connect过程
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;
nextBoss().register(channel);
}
} catch (Throwable t) {
cf.setFailure(t);
fireExceptionCaught(channel, t);
channel.worker.close(channel, succeededFuture(channel));
}
}
七.具体register过程
void register(NioClientSocketChannel channel) {
//client的注册任务,让boss线程监听connect事件
Runnable registerTask = new RegisterTask(this, channel);
Selector selector;
synchronized (startStopLock) {
//初始化boss线程,并启动之
if (!started) {
//
try {
//创建一个Selector
this.selector = selector = Selector.open();
} catch (Throwable t) {
throw new ChannelException(
"Failed to create a selector.", t);
}
// 启动boss线程
boolean success = false;
try {
DeadLockProofWorker.start(bossExecutor,
new ThreadRenamingRunnable(this,
"New I/O client boss #" + id + '-' + subId));
success = true;
} finally {
......
started = true;
//异步提交注册任务
boolean offered = registerTaskQueue.offer(registerTask);
assert offered;
}
//根据超时时间,启动一个超时提醒任务
int timeout = channel.getConfig().getConnectTimeoutMillis();
if (timeout > 0) {
if (!channel.isConnected()) {
channel.timoutTimer = timer.newTimeout(wakeupTask,
timeout, TimeUnit.MILLISECONDS);
}
}
}
八.register之后,主线程就返回了,Boss线程异步执行
public void run() {
boolean shutdown = false;
int selectReturnsImmediately = 0;
Selector selector = this.selector;
// use 80% of the timeout for measure
final long minSelectTimeout = SelectorUtil.SELECT_TIMEOUT_NANOS * 80 / 100;
boolean wakenupFromLoop = false;
for (;;) {
wakenUp.set(false);
try {
long beforeSelect = System.nanoTime();
//select,500ms超时
int selected = SelectorUtil.select(selector);
.......
//处理注册任务,将channel注册到自己的selector范围中
processRegisterTaskQueue();
//处理IO就绪事件,这里是connect事件
processSelectedKeys(selector.selectedKeys());
// 处理超时,如果超时,则设置future失败
long currentTimeNanos = System.nanoTime();
processConnectTimeout(selector.keys(), currentTimeNanos);
......
}
九.注册任务执行
public void run() {
try {
//注册一个connect key到boss的selector上
channel.channel.register(
boss.selector, SelectionKey.OP_CONNECT, channel);
} catch (ClosedChannelException e) {
channel.worker.close(channel, succeededFuture(channel));
}
//设置超时点
int connectTimeout = channel.getConfig().getConnectTimeoutMillis();
if (connectTimeout > 0) {
channel.connectDeadlineNanos = System.nanoTime() + connectTimeout * 1000000L;
}
}
十.boss处理IO,client是connect就绪
private void processSelectedKeys(Set<SelectionKey> selectedKeys) {
......
for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {
SelectionKey k = i.next();
i.remove();
if (!k.isValid()) {
close(k);
continue;
}
try {
//如果connect就位,判断connect是否确实完成
if (k.isConnectable()) {
connect(k);
}
} catch (Throwable t) {
......
}
}
}
private void connect(SelectionKey k) throws IOException {
NioClientSocketChannel ch = (NioClientSocketChannel) k.attachment();
//如果连接成功,则将channel注册上worker线程中处理读写
if (ch.channel.finishConnect()) {
k.cancel();
if (ch.timoutTimer != null) {
ch.timoutTimer.cancel();
}
ch.worker.register(ch, ch.connectFuture);
}
}
十一.注册过程
public void run() {
......
//默认监听read事件
synchronized (channel.interestOpsLock) {
channel.channel.register(
selector, channel.getRawInterestOps(), channel);
}
//通知等待线程成功
if (future != null) {
channel.setConnected();
future.setSuccess();
}
......
//触发ChannelConnected事件
fireChannelConnected(channel, remoteAddress);
......
}
}
十二.echo client handler处理channelConnected事件,开始发送数据
public void channelConnected(
ChannelHandlerContext ctx, ChannelStateEvent e) {
// Send the first message. Server will not send anything here
// because the firstMessage's capacity is 0.
e.getChannel().write(firstMessage);
}
十三.write之后的过程client端和server是一样的,可以参考前一篇server端的读写
分享到:
相关推荐
《深入浅出Netty》是针对Java网络编程框架Netty的一份详细讲解材料,由淘宝工程师精心制作,特别适合初学者。Netty是一款高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。...
资源名称:深入浅出Netty资源截图: 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。
《深入浅出Netty》是一本专注于讲解Netty框架的编程指南,非常适合初学者入门。Netty是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。这本书通过详实的代码案例,...
《深入浅出Netty_netty5.0_》是针对Netty 5.0版本的一本详细教程,旨在帮助读者理解并熟练运用这一强大的网络编程框架。Netty是一个开源的Java框架,它为开发高效、稳定、可扩展的网络应用提供了全面的支持。在本文...
最近几年,Netty社区的发展如火如荼,无论是大数据领域,还是微服务架构,底层都需要一个高效的分布式通信框架作为基础组件。 Netty凭借优异的性能、灵活的可扩展新得到了广泛的应用。短短几年间,Netty已经成为...
赠送jar包:transport-netty4-client-5.5.1.jar; 赠送原API文档:transport-netty4-client-5.5.1-javadoc.jar; 赠送源代码:transport-netty4-client-5.5.1-sources.jar; 赠送Maven依赖信息文件:transport-netty...
赠送jar包:transport-netty4-client-6.3.0.jar; 赠送原API文档:transport-netty4-client-6.3.0-javadoc.jar; 赠送源代码:transport-netty4-client-6.3.0-sources.jar; 赠送Maven依赖信息文件:transport-netty...
这个“深入浅出Netty文档”压缩包包含了一份详细的PDF文档,旨在帮助开发者深入理解Netty的工作原理及其在实际应用中的使用。 Netty 的核心是它的事件驱动模型,基于Java NIO(非阻塞I/O)实现。它提供了一种优雅的...
《深入浅出Netty》是针对Java网络编程框架Netty的一份详细讲解文档,由阿里巴巴的专家编写。Netty是一个高性能、异步事件驱动的网络应用程序框架,它为开发可维护的高性能协议服务器和客户端提供了丰富的组件和API。...
赠送jar包:transport-netty4-client-5.5.1.jar; 赠送原API文档:transport-netty4-client-5.5.1-javadoc.jar; 赠送源代码:transport-netty4-client-5.5.1-sources.jar; 赠送Maven依赖信息文件:transport-netty...
Netty通过利用Java的NIO类库,提供了高效的网络处理能力,并且具有很高的可定制性和扩展性,它支持快速开发可维护的高性能协议服务器和客户端。 Netty的设计目标是让网络应用开发变得更加简单,它抽象了网络编程中...
赠送jar包:transport-netty3-client-5.5.1.jar; 赠送原API文档:transport-netty3-client-5.5.1-javadoc.jar; 赠送源代码:transport-netty3-client-5.5.1-sources.jar; 赠送Maven依赖信息文件:transport-netty...