基于Netty4.0的框架源码分析,侧重于框架设计技巧和原理解析;以Example模块中的一个获取世界时钟例子的源码开始,逐步分析服务端与客户端的工作流程,在这个过程中,让我们先对Netty框架有个感性认识。
什么是Netty
- 提供异步的,事件驱动的网络应用程序开发框架和工具
- 可以开发高性能和高可靠性的网络服务器和客户端应用程序
- 提供丰富的协议编解码支持
- 自定义buffer系统,减少复制带来的消耗
- 整套channel的实现,channel是统一的异步I/O编程接口,抽象了所有点对点的通信工作
Netty4.0项目结构
模块 描述
netty project parent
common utility and logging
buffer buffer API
transport channel API and its core implementations
transport-rtrx RTRX transport implementation
transport-sctp SCTP transport implementation
transport-udt UDT transport implementation
handler channel handlers
codec codec framework
codec-http HTTP, Web Sockets, SPDY, and RTSP codec
codec-socks Socks codec
example examples
all generates an all-in-one JAR
tarball generates a tarball distribution
Netty框架核心
服务端启动过程
该实例为一个世界时钟的服务端启动过程,从他的主类WorldClockServer.java源码看起,该代码可以看作是启动服务端或者客户端的一个模板代码。
public void run() throws Exception { //线程数如果为空时,默认为8 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new WorldClockServerInitializer());b.bind(port).sync().channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }
}
多线程的任务调度器
JDK自1.5之后,增加了executor接口,给多线程的使用提供很大的便利空间,Netty框架也是基于executor设计了非常稳定的线程模型。任何实现了Executor接口的类即是一个多线程的任务调度器,当然,这个任务是在新线程中执行,还是在线程池,或者是在调用者的当前线程中执行,是由具体实现类来决定的。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
上面两行代码分别新建了2个任务调度器,一个用来执行boss任务,一个用来执行worker任务。注意:只是建立了任务调度器,但是并没有执行任务;其内部实现其实是建立线程任务处理器Executor,然后再建立单线程的事件处理器数组,其中存放的是具体实现子类NioEventLoop。
启动管理器
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new WorldClockServerInitializer());
ServerBootstrap是一个启动管理器,主要职责是将服务端启动时需要的各种资源进行装配和注册,并且可以启动服务,停止服务,释放资源等等相关事项。以上代码首先使用group方法将建立的2个任务调度器注册到启动管理器中,然后利用channel方法注册NioServerSocketChannel这个资源---这个资源可以看作就是一根管子,为基于socket的网络通信提供的一种载体。最后注册WorldClockServerInitializer这个ChannelHandler,通过childHandler方法,ChannelHandler就是用来处理在Channel上发生的事件的,比如像open,close,connect等等事件。
绑定端口及其他
以上就是将所有需要的资源在启动管理器中进行了注册,下面就可以开始启动进行一系列的初始化工作,并且启动服务了。
b.bind(port).sync().channel().closeFuture().sync();
首先看bind方法的逻辑。
- 先初始化以及开启NioServerSocketChannel
- 在任务调度器NioEventLoop(该调度器属于调度器组NioEventLoopGroup)中注册NioServerSocketChannel
- 将注册的NioServerSocketChannel绑定端口以及绑定监听器
也就是说这个任务调度器,对于NioServerSocketChannel这个管子中发生的所有的事件,都会进行处理,当然是以重新开启一个新线程的方式进行处理;我们看看bind方法的部分代码逻辑---调用了channel对应任务调度器的execute方法来处理该任务,该任务其实就是上面的逻辑3。
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
channel.eventLoop().execute(new Runnable() {
//在将来会执行该方法,是在一个新线程,还是在线程池里面取线程,或者是在当前调用线程执行,这个有具体实现来负责。
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});}
在回到原来的主线程执行逻辑中,继续sync以及其后续的几个方法,他们主要功能是等待future彻底完成,否则当前主线程会一直处于阻塞状态,当future彻底完成后,执行finally中的任务调度器清理逻辑,至此,主线程所有逻辑完成。那么在任务调度器中的所有任务,会被不停的执行下去,直到所有的任务都完成为止。
总结下过程,从线程角度,如下:
- 随着主线程的开启,第一个交给任务管理器的任务就是注册channel(该过程其实就已经打开一个channel,但是没有绑定端口)
- 第二个交给任务管理器的任务是给新打开的channel绑定端口和监听器,这2个任务都会被添加到任务队列taskQueue中去,在加入第一个任务的时候由任务调度器开启新线程来执行taskQueue中所有的任务队列,在执行每个任务的时候,也是单独开启新线程执行
- 在该过程中,有逻辑调用任务管理器执行时,就将新任务加入到任务队列中即可,不在开启新线程;此时,主线程继续执行,直到完成
- 在执行taskQueue的新线程中,会逐个执行该队列中所有的任务,直到全部完成
客户端启动过程
与服务端类似,首先在WorldClockClient中新建一个启动管理器Bootstrap对象,然后在该启动管理器中注册一个多线程的任务调度器(该任务调度器与服务端启动过程中的线程任务调度器是同一个类),同时将连接服务端的channel注册到该任务调度器中,然后将handler注册到channel上来处理各种channel上的操作;我们知道在Netty中,channel的操作不同,就有不同的handler与其对应。
channel的注册动作(与服务端的注册逻辑类似),也是交由线程任务调度器完成的;如果该channel中的所有的异步I/O都完成的话,就开始连接服务端。
开始调用channel上的pipeline进行服务端的连接操作,其实质也是交由线程任务调度器去完成该任务(即加入同一个任务队列中)。
连接服务端操作完成后,关闭连接及其他对象,主线程结束。
服务端启动序列图如下:
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于