ServerBootstrap
在Netty中负责引导启动服务端程序,下面给出一个服务端启动的例子:
EventLoopGroup bossGroup = new NioEventLoopGroup(); //bossGroup负责处理TCP/IP连接的
EventLoopGroup workerGroup = new NioEventLoopGroup(); //workerGroup是负责处理Channel(通道)的I/O事件
ServerBootstrap sb = new ServerBootstrap();
sb.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_KEEPALIVE, true) //保持长连接
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sh) throws Exception {
sh.pipeline()
.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4))
.addLast(new LengthFieldPrepender(4, false))
.addLast(new ServerHandler()); // 业务handler
}
});
//绑定监听端口,调用sync同步阻塞方法等待绑定操作完
ChannelFuture future = sb.bind(8081).sync();
if (future.isSuccess()) {
System.out.println("服务端启动成功");
} else {
System.out.println("服务端启动失败");
future.cause().printStackTrace();
bossGroup.shutdownGracefully(); //关闭线程组
workerGroup.shutdownGracefully();
}
//成功绑定到端口之后,给channel增加一个管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程。
future.channel().closeFuture().sync();
1. ServerBootstrap
类图
2. ServerBootstrap
成员变量
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
// 主线程池
volatile EventLoopGroup group;
// 负责创建channel的工厂类,服务端为NioServerSocketChannel,客户端为NioSocketChannel
private volatile ChannelFactory<? extends C> channelFactory;
private volatile SocketAddress localAddress;
// socket及其它的一些属性设置
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
// 附加属性设置
private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
// handler业务逻辑处理类
private volatile ChannelHandler handler;
}
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
private final Map<ChannelOption<?>, Object> childOptions = new ConcurrentHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
// 工作线程组
private volatile EventLoopGroup childGroup;
// 客户端连接时会触发执行
private volatile ChannelHandler childHandler;
}
3. 服务端绑定逻辑AbstractBootstrap.bind()
- Netty服务端何时注册
SelectionKey.OP_ACCEPT
? - 主线程在哪里获取TCP请求 ,即执行s
elector.select()
方法?
带着疑问,进入到AbstractBootstrap.bind()
绑定方法
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
public ChannelFuture bind(SocketAddress localAddress) {
validate(); // 验证
return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
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;
}
}
3.1 AbstractBootstrap.initAndRegister()
大概流程:
- 实例化
NioServerSocketChannel
对象,创建服务端的Java Nio Channel
、设置通道非阻塞,初始化字段pipeline
和unsafe
等。 init()
方法,往ChannelPipeLine
链表中中添加一个ChannelInitializer
类型的事件处理。register(channel)
,将NioServerSocketChannel绑定主线程池的一个NioEventLoop
线程,
给Nio Channel感兴趣的事件0,并绑定当前NioServerSocketChannel
对象给SelectedKey的attach
,添加ServerBootstrapAcceptor
到pipeline
中。doBind0()
绑定地址和端口,当通道注册工作完成后绑定地址和端口,同时向通道中注册SelectionKey.OP_ACCEPT
事件。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel(); // 实例化NioServerSocketChannel对象
init(channel);
} 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);
// 注册失败,关闭Nio通道
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
3.1.1 NioServerSocketChannel
对象实例化
大概流程:
- 创建NIO原生的
Channel
通道。 - 初始化变量
AbstractNioChannel.readInterestOp = SelectionKey.OP_ACCEPT
。 - 设置通道非阻塞。
- 初始化变量
AbstractChannel.unsafe = new NioMessageUnsafe()
。 - 初始化变量
AbstractChannel.pipeline = new DefaultChannelPipeline(this)
。
// 多路复用选择器提供者SelectorProvider,用来创建多路复用选择器Selector
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
// 创建Java Nio Channel
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
继续调用父类AbstractNioChannel
的构造函数
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false); // 设置通道非阻塞
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
继续调用父类AbstractChannel
的构造函数
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
3.1.2 ServerBootstrap.init()
往ChannelPipeLine
链表中中添加一个ChannelInitializer
类型的事件处理,当Channel注册时会调用该initChannel()
方法。
@Override
void init(Channel channel) {
setChannelOptions(channel, options0().entrySet().toArray(newOptionArray(0)), logger);
setAttributes(channel, attrs0().entrySet().toArray(newAttrArray(0)));
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions =
childOptions.entrySet().toArray(newOptionArray(0));
final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
// AbstractUnsafe.register0()方法中的代码pipeline.invokeHandlerAddedIfNeeded();
// 此段代码会调用以下的initChannel方法
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
继续查看DefaultChannelPipeline
的addLast()
方法,大概流程为:
- 将handler添加到pipeline的双向链表中。
- 如果未注册说明
NioServerSocketChannel
未绑定到NioEventLoop
中,一旦注册则需要当前handler的handlerAdded()
方法。
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
checkMultiplicity(handler);
newCtx = newContext(group, filterName(name, handler), handler);
addLast0(newCtx);
// If the registered is false it means that the channel was not registered on an eventLoop yet.
// In this case we add the context to the pipeline and add a task that will call
// ChannelHandler.handlerAdded(...) once the channel is registered.
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
callHandlerAdded0(newCtx);
return this;
}
// 将handler封装成上下文环境ctx,插入到pipeline管道中
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
3.1.3 EventLoopGroup.register()
注册通道
大概流程:
- 调用主线程池中的线程
NioEventLoop.register()
方法,最终调用的是NioServerSocketChannel.unsafe.register()
方法。 - 将
NioServerSocketChannel
绑定主线程池的一个NioEventLoop
线程,之后Channel中的IO事件都交给该线程。 - 注册新连接的Channel到选择的
NioEventLoop
线程的Selector
上,注册感兴趣的事件0,并绑定当前NioServerSocketChannel
对象给SelectedKey,因此IO事件到来的时候能够获取到NioServerSocketChannel
对象。 - 通知pipeline处理handler的handlerAdded()方法,最终执行
ServerBootstrap.ChannelInitializer.initChannel()
方法,即想pipeline中添加ServerBootstrapAcceptor
,并移除当前ServerBootstrap.ChannelInitializer
。
查看NioEventLoopGroup
类的register()
方法,通过线程选择器轮询选择一个线程执行。
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
查看NioEventLoop
类的register()
方法,这里调用NioServerSocketChannel
类的unsafe
成员的register()
方法,服务端的unsafe
为NioMessageUnsafe
对象。
@Override
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
继续查看NioMessageUnsafe
类的register()
方法,调用父类AbstractUnsafe
的register()
方法,大概流程:
- 绑定
NioServerSocketChannel
所在的线程为轮询获取到的线程,即NioEventLoop
,Netty的无锁串行化体现在这里。 - 由于线程未启动,所以会执行启动
NioEventLoop
线程,并添加注册任务到NioEventLoop
的任务队列中。 - 注册新连接的Channel到选择的
NioEventLoop
线程的Selector
上,注册感兴趣的事件0,,将当前NioServerSocketChannel
对象绑定给SelectedKey
的attach
。
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
}
if (isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
// 无锁串行化
AbstractChannel.this.eventLoop = eventLoop;
// 线程已经启动
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
// 提交任务到线程的任务队列中,并启动线程
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);
}
}
}
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
// 注册感兴趣的事件并绑定SelctedKey的attach为NioServerSocketChannel
doRegister();
neverRegistered = false;
registered = true;
// 通知pipeline执行handlerAdded()方法
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
AbstractNioChannel
类的doRegister()
方法
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
}
3.2 AbstractBootstrap.doBind0()
绑定地址和端口
等待通道注册成功后绑定地址,最终调用的是NioServerSocketChannel.unsafe.bind()
方法。
- 绑定地址和端口。
- 注册
SelectionKey.OP_ACCEPT
事件。
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop();
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
logger.warn(
"A non-root user can't receive a broadcast packet if the socket " +
"is not bound to a wildcard address; binding to a non-wildcard " +
"address (" + localAddress + ") anyway as requested.");
}
boolean wasActive = isActive();
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
safeSetSuccess(promise);
}
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
注册SelectionKey.OP_ACCEPT
事件的逻辑有点长,继续看fireChannelActive()
。
@Override
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
执行HeadContext.active()
方法。
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
readIfIsAutoRead();
}
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
channel.read();
最终执行的是DefaultChannelPipeline.read()
方法。
@Override
public ChannelHandlerContext read() {
final AbstractChannelHandlerContext next = findContextOutbound(MASK_READ);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeRead();
} else {
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(tasks.invokeReadTask);
}
return this;
}
这里的next
是HeadContext
对象。
private void invokeRead() {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).read(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
read();
}
}
@Override
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
unsafe.beginRead()
最终调用的是AbstractNioChannel.doBeginRead()
,如果当前selectionKey为0 ,则重新绑定readInterestOp
,这里的readInterestOp
就是AbstractNioChannel
的构造函数中传进来的SelectionKey.OP_ACCEPT
。
@Override
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);
}
}