在分析netty 服务端启动流程之前,我们先看下 java nio socket服务端的代码如何编写,因为netty nio 框架底层最终还是要用java nio那一套方式启动服务。
//创建channel
ServerSocketChannel channel = ServerSocketChannel.open();
//绑定端口
channel.socket().bind(new InetSocketAddress("localhost", 8080));
//设置成非阻塞
channel.configureBlocking(false);
Selector selector = Selector.open();
//注册到selector 对accept connection 感兴趣
channel.register(selector, SelectionKey.OP_ACCEPT);
//监听selector事件,对不同类型的事件做相应的处理
.....
下面来看下netty 启动socket 服务端的代码(Netty 版本4.1.10-Final)
//接收连接请求的事件循环
EventLoopGroup bossGroup =new NioEventLoopGroup();
//接收IO读写的事件循环
EventLoopGroup workerGroup =new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup).
//指定Channel(netty中的)
channel(NioServerSocketChannel.class)
//sockect channel的处理器链
.childHandler(new ServerInitializerXXX());
//绑定端口
ChannelFuture sync = serverBootstrap.bind(9000).
sync();
sync.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
代码中bind方法要传端口号,说明这是启动服务端的入口,接下来分析一下bind方法。代码一路跟踪下去,走到doBind方法。(分析的代码中砍掉了一些异常处理流程,和不重要的逻辑)
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
}
//else 里面的代码砍掉了
}
这里我们可以猜到netty做了2件事情
1.initAndRegister 方法:创建了Channel,并且注册到selector
2.doBind0 方法:传入了SocketAddress,说明进行了端口绑定操作
接下来我们看下netty是如何创建channel,注册到selector的。进入initAndRegister 方法
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//创建Channel对象
channel = channelFactory.newChannel();
//初始化channel
init(channel);
} catch (Throwable t) {
//相关代码砍掉了
}
//将Channel对象注册到selector
ChannelFuture regFuture = config().group().register(channel);
//判断注册到selector 是否有异常,如果有异常关闭channel
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
initAndRegister 做了三件事情
一.通过 channelFactory 创建了channel对象。此处的channelFactory是在创建了ServerBootstrap对象后调用channel方法时创建.
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
//ServerBootstrap中的channelFactory引用ReflectiveChannelFactory对象。
//ReflectiveChannelFactory 对象中引用了channel方法中传入的Channel.class,ReflectiveChannelFactory 通过反射的形式创建Channel对象
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
// 小插曲
//这里创建channel对象使用SelectorProvider.openServerSocketChannel() 的方式创建,使用原生的ServerSocketChannel.open()创建channel,会加锁有性能损失.(https://github.com/netty/netty/issues/2308)
二.通过init方法初始化channel
void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
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());
}
}
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()));
}
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() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
1.设置attribute
2.设置ChannelOption
3.注册了一个ChannelHandler,在channel pipleline中出入了一个 ServerBootstrapAcceptor对象。ServerBootstrapAcceptor主要用来将新的请求转发到child eventLoopGroup.
这块逻辑不是很重要
三.channel 注册
ChannelFuture regFuture = config().group().register(channel)
一路跟踪到AbstractChannel 的register方法
//砍掉了细枝末节的逻辑
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
AbstractChannel.this.eventLoop = eventLoop;
//判断是不是当前线程在做register操作,保证一个线程负责一个channel的整个生命周期,避免线程安全问题
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
}
}
}
register0方法
private void register0(ChannelPromise promise) {
try {
boolean firstRegistration = neverRegistered;
doRegister();
neverRegistered = false;
registered = true;
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
}
}
1.doRegister :将channel 注册到selector
2.invokeHandlerAddedIfNeeded :调用pipeline中channel的handlerAdded方法
3.fireChannelRegistered:调用pipeline中channelRegister方法
4.判断channel是否是active状态,如果channel 绑定了端口认为是active。此时还没有走到bind端口,不会触发调用ChannelActive方法。
Channel创建完成,注册到selector后开始绑定端口。我们回到doBind0方法,代码跟踪到AbstractChannelHandlerContext的bind 方法
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
//在一个线程中执行channel的端口绑定
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null);
}
return promise;
}
再一路跟踪到AbstractChannel的bind 方法
//判断是否绑定端口
boolean wasActive = isActive();
try {
// 执行ServerSocketChannel的bind方法,绑定端口
doBind(localAddress);
} catch (Throwable t) {
}
if (!wasActive && isActive()) {
//异步触发调用pipeline中channelHandler的ChannelActive方法
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
总结
netty 服务端启动流程
1.创建channel对象,设置option,attribute,添加ServerBootstrapAcceptor 接收器。
2.channel 注册到selector
3.触发channelAdd,channelRegistered
4.bind 端口
5.触发ChannelActive