Netty 服务端创建的时序图:
服务器端的启动代码:
public class NettyServer {
public void bind(int port){
// 创建EventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(); //创建BOSS线程组 用于服务端接受客户端的连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); //创建WORK线程组 用于进行SocketChannel的网络读写
try {
// 创建ServerBootStrap实例
// ServerBootstrap 用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度
ServerBootstrap b = new ServerBootstrap();
// 绑定Reactor线程池
b.group(bossGroup, workerGroup)
// 设置并绑定服务端Channel
// 指定所使用的NIO传输的Channel
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingServerHandler())
.childHandler(new ChannelInitializer(){
@Override
protected void initChannel(Channel ch) throws Exception {
//do something
}
});
// 绑定端口,同步等待成功
ChannelFuture future = b.bind(port).sync();
// 等待服务端监听端口关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 优雅地关闭
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private class LoggingServerHandler extends ChannelInboundHandlerAdapter{
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("loggin-channelActive");
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("loggin-channelRegistered");
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("loggin-handlerAdded");
}
}
public static void main(String[] args){
new NettyServer().bind(8899);
}
}
主要步骤为:
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
bossGroup 为 BOSS 线程组,用于服务端接受客户端的连接, workerGroup 为 worker 线程组,用于进行 SocketChannel 的网络读写。当然也可以创建一个并共享。
创建ServerBootstrap实例
ServerBootstrap b = new ServerBootstrap();
ServerBootStrap为Netty服务端的启动引导类,用于帮助用户快速配置、启动服务端服务。
调用 group() 方法,为 ServerBootstrap 实例设置并绑定 Reactor 线程池。
b.group(bossGroup, workerGroup)
当然也可以使用一个b.group(group),因为此方法在ServerBootstrap内部调用了group(bossGroup, workerGroup)方法。
@Override
public ServerBootstrap group(EventLoopGroup group) {
return group(group, group);
}
EventLoopGroup 为 Netty 线程池,它实际上就是 EventLoop 的数组容器。EventLoop 的职责是处理所有注册到本线程多路复用器 Selector 上的 Channel,Selector 的轮询操作由绑定的 EventLoop 线程 run 方法驱动,在一个循环体内循环执行。通俗点讲就是一个死循环,不断的检测 I/O 事件、处理 I/O 事件。bossGroup 的作用就是不断地接收新的连接,接收之后就丢给 workerGroup 来处理,workerGroup 负责干活就行(负责客户端连接的 IO 操作)。
group方法的源码如下:
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
this.childGroup = childGroup;
return this;
}
首先调用了父类的构造方法,并且参数是的bossGroup,然后将成员变量childGroup设置为我们传入的workerGroup。
关于EventLoopGroup的内部原理分析请见EventLoopGroup 源码分析文章。
绑定线程池后,则需要设置 channel 类型,服务端用的是 NioServerSocketChannel 。当然也可以使用BioServerSocketChannel。
.channel(NioServerSocketChannel.class)
此方法是设置AbstractBootstrap的channelFactory属性,ChannelFactory是一个工厂类,利用反射创建NioServerSocketChannel 对象,如下:
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
this.channelFactory = channelFactory;
return self();
}
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
channelFactory设置完毕后,和是利用此工厂类创建NioServerSocketChannel呢?答案是AbstractBootstrap.doBind -> AbstractBootstrap.initAndRegister->init(channel),在init通过工厂类创建NioServerSocketChannel。而在工厂类中是通过无参的构造函数来实例化一个NioServerSocketChannel。
public T newChannel() {
try {
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
设置完 Channel 参数后,用户可以为启动辅助类和其父类分别指定 Handler
.handler(new LoggingServerHandler())
.childHandler(new ChannelInitializer(){
//省略代码
})
这两个 Handler 不一样,前者(handler())设置的 Handler 是服务端 NioServerSocketChannel的,后者(childHandler())设置的 Handler 是属于每一个新建的 NioSocketChannel 的。跟踪源代码会发现两种所处的类不一样,handler 位于 AbstractBootstrap 中,childHandler 位于 ServerBootstrap 中。AbstracServerBootstrap 中的 Handler 是 NioServerSocketChannel 使用的,所有连接该监听端口的客户端都会执行它。
通过b.bind(port)来启动服务,下面将来进行分析。调用 ServerBootstrap 的 bind(int port) 方法进行端口绑定,该方法有调用bind(SocketAddress localAddress)方法。
public ChannelFuture bind(SocketAddress localAddress) {
validate();
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
return doBind(localAddress);
}
其中调用validate()和doBind(localAddress)两个方法。这两个方法在
AbstractBootstrap中。
//函数功能:检查相关参数是否设置了
@SuppressWarnings("unchecked")
public B validate() {
if (group == null) {
//这里的group指的是:b.group(bossGroup, workerGroup)代码中的bossGroup,
throw new IllegalStateException("group not set");
}
if (channelFactory == null) {
throw new IllegalStateException("channel or channelFactory not set");
}
return (B) this;
}
该方法主要检查了两个参数,一个是group,一个是channelFactory,前面已经提到。其中调用用group方法是将bossGroup赋值给了group,调用将channel()方法将创建ChannelFactory赋值给了channelFactory。
接下来看bind方法中的doBind(localAddress)方法
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();//1
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);//2
return promise;
} else {
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
doBind这个函数是我们要分析的重点,这个函数的主要工作有如下几点:
1、通过initAndRegister()方法得到一个ChannelFuture的实例regFuture。
2、通过regFuture.cause()方法判断是否在执行initAndRegister方法时产生来异常。如果产生来异常,则直接返回,如果没有产生异常则进行第3步。
3、通过regFuture.isDone()来判断initAndRegister方法是否执行完毕,如果执行完毕来返回true,然后调用doBind0进行socket绑定。如果没有执行完毕则返回false进行第4步。
4、regFuture会添加一个ChannelFutureListener监听,当initAndRegister执行完成时,调用operationComplete方法并执行doBind0进行socket绑定。
第3、4点想干的事就是一个:调用doBind0方法进行socket绑定。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();//A
init(channel);//B
} catch (Throwable t) {
if (channel != null) {
channel.unsafe().closeForcibly();
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
ChannelFuture regFuture = config().group().register(channel);//C
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
这里的channel为一个NioServerSocketChannel对象,通过反射使用channelFactory的newChannel()方法,该方法在2节提到过,channelFactory对象是通过channel()方法创建的。
@Override
void init(Channel channel) throws Exception {
//1、设置新接入channel的option
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
//2、设置新接入channel的attr
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
// 获取绑定的pipeline
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
// 为NioServerSocketChannel的pipeline添加一个初始化Handler,
// 当NioServerSocketChannel在EventLoop注册成功时,该handler的init方法将被调用
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
// 为NioServerSocketChannel的pipeline添加ServerBootstrapAcceptor处理器
// 该Handler主要用来将新创建的NioSocketChannel注册到EventLoopGroup中
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
init()整个过程可以分为三个步骤:
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
}
}
Channel child 是一个 NioSocketChannel 的实例,childGroup 是构造此对象是传入的 currentChildGroup, 即我们的 workerGroup,因此这里的 childGroup.register 就是将 workerGroup 中的某个 EventLoop 和 NioSocketChannel 关联了。, 那么现在的问题是, ServerBootstrapAcceptor.channelRead 方法是怎么被调用的呢? 其实当一个 client 连接到 server 时, Java 底层的 NIO ServerSocketChannel 会有一个 SelectionKey.OP_ACCEPT 就绪事件, 接着就会调用到 NioServerSocketChannel.doReadMessages:
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = javaChannel().accept();
... 省略部分代码
buf.add(new NioSocketChannel(this, ch));
return 1;
}
在 doReadMessages 中, 通过 javaChannel().accept() 获取到客户端新连接的 SocketChannel, 接着就实例化一个 NioSocketChannel, 并且传入 NioServerSocketChannel 对象(即 this), 由此可知, 我们创建的这个 NioSocketChannel 的父 Channel 就是 NioServerSocketChannel 实例 .
接下来就经由 Netty 的 ChannelPipeline 机制, 将读取事件逐级发送到各个 handler 中, 于是就会触发前面我们提到的 ServerBootstrapAcceptor.channelRead 方法。
这段代码的功能及时向EventLoopGroup中注册一个channel,注意这里的 group() 返回的是前面的 boss NioEvenLoopGroup,register方法实现在MultithreadEventLoopGroup,他的接口声明在EventLoopGroup中。下面我们进入register方法内部:
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
调用 next() 方法从 EventLoopGroup 中获取下一个 EventLoop,调用 register() 方法注册:
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
将Channel和EventLoop封装成一个DefaultChannelPromise对象,然后调用register()方法。DefaultChannelPromis为ChannelPromise的默认实现,而ChannelPromisee继承Future,具备异步执行结构,绑定Channel,所以又具备了监听的能力,故而ChannelPromise是Netty异步执行的核心接口。
public ChannelFuture register(ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
AbstractChannel.this.eventLoop = eventLoop;
// 必须要保证注册是由该EventLoop发起的
if (eventLoop.inEventLoop()) {
register0(promise); // 注册
} else {
// 如果不是单独封装成一个task异步执行
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
过程如下:
无论当前 EventLoop 的线程是否拥有执行权,最终都会要执行 register0(),如下:
private void register0(ChannelPromise promise) {
boolean firstRegistration = neverRegistered;
// 真正的注册动作
doRegister();
neverRegistered = false;
registered = true;
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
//如果Channel已经绑定端口、或者Connect/open,在ServerSocketChannel的注册过程不会执行。目前为止只是将ServerSocketChannel注册到Selector,并没有绑定端口。
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
}
如果 Channel 处于 open 状态,则调用 doRegister() 方法完成注册,然后将注册结果设置为成功。最后判断如果是首次注册且处于激活状态,则发起 pipeline 的 fireChannelActive()。
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 注册到NIOEventLoop的Selector上
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
}
这里注册时 ops 设置的是 0,也就是说 ServerSocketChannel 仅仅只是表示了注册成功,还不能监听任何网络操作,这样做的目的是(摘自《Netty权威指南(第二版)》):
接下来继续看 pipeline.fireChannelRegistered()做了什么事情。
public final ChannelPipeline fireChannelRegistered() {
AbstractChannelHandlerContext.invokeChannelRegistered(head);
return this;
}
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
private void invokeChannelRegistered() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRegistered();
}
}
在 invokeChannelRegistered() 会调用我们在前面设置的 handler (还记得签名的 handler(new LoggingServerHandler() )么)的 channelRegistered(),这个时候控制台应该会打印 loggin-channelRegistered。
到这里initAndRegister() (final ChannelFuture regFuture = initAndRegister();)就分析完毕了,该方法主要做如下三件事:
下面分析 doBind0()到底做了些什么。源码如下
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
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());
}
}
});
}
首先new 一个线程 task,然后将该任务提交到 NioEventLoop 中进行处理,我们先看 execute()。
@Override
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
调用 inEventLoop() 判断当前线程是否为该 NioEventLoop 所关联的线程,如果是,则调用 addTask() 将任务 task 添加到队列中,如果不是,则先启动线程,在调用 addTask() 将任务 task 添加到队列中。addTask() 如下:
protected void addTask(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
if (!offerTask(task)) {
reject(task);
}
}
final boolean offerTask(Runnable task) {
if (isShutdown()) {
reject();
}
return taskQueue.offer(task);
}
task 添加到任务队列 taskQueue成功后,执行任务会有如下的调用链:
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
}
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return tail.bind(localAddress, promise);
}
tail 在 DefaultChannelPipeline 中定义:final AbstractChannelHandlerContext tail; 有 tail 就会有 head ,在 DefaultChannelPipeline 中维护这一个 AbstractChannelHandlerContext 节点的双向链表,该链表是实现 Pipeline 机制的关键,更多详情会在 ChannelPipeline 中做详细说明。bind() 最终会调用 DefaultChannelPipeline 的 bind() 方法。如下:
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
if (!validatePromise(promise, false)) {
// cancelled
return promise;
}
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null);
}
return promise;
}
首先对 localAddress 、 promise 进行校验,符合规范则调用 findContextOutbound() ,该方法用于在 pipeline 中获取 AbstractChannelHandlerContext 双向链表中的一个节点,如下:
private AbstractChannelHandlerContext findContextOutbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
}
从该方法可以看出,所获取的节点是从 tail 开始遍历,获取第一个节点属性 outbound 为 true 的节点。其实该节点是 AbstractChannelHandlerContext 双向链表的 head 节点。获取该节点后,调用 invokeBind(),如下
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
bind(localAddress, promise);
}
}
这是因为HeadContext由于其继承AbstractChannelHandlerContext以及实现了ChannelHandler接口使其具有Context和Handler双重特性。然后调用其bind(),如下:
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
unsafe.bind(localAddress, promise);
}
unsafe 定义在 HeadContext 中,在构造函数中初始化(unsafe = pipeline.channel().unsafe();),调用 bind() 如下:
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
....
boolean wasActive = isActive();
// 最核心方法
doBind(localAddress);
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
内部调用 doBind() ,该方法为绑定中最核心的方法,位于 NioServerSocketChannel 中,如下
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
javaChannel()返回的是 NioServerSocketChannel 实例初始化时所产生的 Java NIO ServerSocketChannel 实例(ServerSocketChannelImple实例),然后调用其 bind(),在这里真正的开始监听端口。
其实在监听完成后,接着执行pipeline.fireChannelActive()这行代码,这里有个挑战:为什么能够进入if语句,而在注册时不能进入?答案是注册的时候只是想Selector注册Channel,并没有激活Channel(激活就是开始监听端口、Socket连接)下面分析pipeline.fireChannelActive方法,此方法的实现在DefaultChannelPipeline中,内部调用了HeadContext的channelActive:
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
readIfIsAutoRead();
}
第一行是继续激活后面的handler,第二行的实现:
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
@Override
public Channel read() {
pipeline.read();
return this;
}
public final ChannelPipeline read() {
tail.read();
return this;
}
readIfIsAutoRead中激活了Channel Read事件,read事件从Tail节点流向head
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
beginRead的实现AbstractChannel.AbstractUnsafe中:
@Override
public final void beginRead() {
assertEventLoop();
if (!isActive()) {
return;
}
doBeginRead();
}
channel.nio.AbstractNioChannel#doBeginRead实现如下:
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
在这里真正的把ACCEPT/READ事件注册给Selector
至此说完了 Server端的整体流程,后续会出客户端以及EventLoop相关的源码分析。