通常我們用ServerBootstrap引導服務器,
public final class SimpleServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new SimpleServerHandler())
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
}
});
ChannelFuture f = b.bind(8888).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
其中group()無非傳入兩個EventLoopGroup,一個負責接收連接,一個負責處理連接上的響應。這之後的邏輯基本上在EventLoop跟ChannelPipeLine中理得差不多了。
這次我們重點來看ChannelFuture f = b.bind().sync(); bind()方法使netty開始對本地端口的綁定與監聽。
bind邏輯的實現在其超類AbstractBootstrap中
public ChannelFuture bind() {
//檢查group和channelFactory屬性是否爲null
validate();
SocketAddress localAddress = this.localAddress;
if (localAddress == null) {
throw new IllegalStateException("localAddress not set");
}
return doBind(localAddress);
}
其中validate()方法是對netty中的group跟channelFactory進行非null驗證,保證都是已經配置完成的(gorup是在.goup()中綁定的,channelFactory是在第一次.channel時候,生成靜態BootstrapChannelFactory配置進去)。然後判斷本地地址是否已經綁定,最後調用doBind()繼續後面的綁定。
private ChannelFuture doBind(final SocketAddress localAddress) {
//1.初始化channel並將初始化好的channel註冊到事件循環
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
final ChannelPromise promise;
/**
* 2.判斷初始化且註冊任務是否已經完成,如果已經完成,則調用bind端口方法
* 注意這裏regFuture.isDone()方法,返回ture,只是標示任務是否已經完成,完成的情況可能有:
* 1.正常終止、2.異常、3.取消
*/
if (regFuture.isDone()) {
promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
promise = new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
doBind0(regFuture, channel, localAddress, promise);
}
});
}
return promise;
}
其中有涉及到channelFutrue的內容,會在後面的博文中詳細介紹。
首先調用initAndRegister(),將初始化好的Channel註冊到EventLoopGroup中。
final ChannelFuture initAndRegister() {
final Channel channel = channelFactory().newChannel();
try {
//初始化Channel實例:對channel實例進行相關參數設置。這裏調用的是對應子類的實現
init(channel);
} catch (Throwable t) {
channel.unsafe().closeForcibly();
return channel.newFailedFuture(t);
}
ChannelFuture regFuture = group().register(channel);
//如果註冊失敗,regFuture.cause會返回失敗的異常對象
//如果註冊出現異常,並channel已經註冊,那麼關閉。如果沒有註冊,那麼強行關閉
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
先通過反射得到channel實例,再調用具體子類的init()方法,對channel實例進行相關參數設置。
@Override
void init(Channel channel) throws Exception {
//將傳入Bootstrap
final Map<ChannelOption<?>, Object> options = options();
synchronized (options) {
channel.config().setOptions(options);
}
final Map<AttributeKey<?>, Object> attrs = attrs();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e : attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
}
//將AbstractBootstrap的Hanlder添加到NioServerketChannel
//就是把handler(new SimpleServerHandler())這個handler添加到NioServerketChannel的pipline
ChannelPipeline p = channel.pipeline();
if (handler() != null) {
p.addLast(handler());
}
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()));
}
/**
* ChannelInitializer是一個特殊的ChannelInboundHandler,當channelRegistered事件觸發後,
* 會調用initChannel方法,調完後,這個handler會從piplne中刪除
*/
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
首先在服務端通過加鎖的方式配置options跟atts屬性,取出NioServerChannel對應的pipline,將之前配置的handler添加到pipline中。
最後通過添加ChannelInitializer(類似於佔位符)的方式,向pipline中添加ServerBootstrapAcceptor實例。
我們先看看ChannelInitializer的巧妙實現方式,以下是它的channelRegistered方法,也就是在之後通道註冊之後,會鏈式調用pipline中的相應方法,也就會調用以下方法。
@Override
@SuppressWarnings("unchecked")
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ChannelPipeline pipeline = ctx.pipeline();
boolean success = false;
try {
//調用initChannel方法進行ChannelPipline初始化
initChannel((C) ctx.channel());
//從pipline刪除這個handler
pipeline.remove(this);
//鏈式觸發fireChannelRegistered事件
ctx.fireChannelRegistered();
success = true;
} catch (Throwable t) {
logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
} finally {
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
if (!success) {
ctx.close();
}
}
}
無非先initChannel()這是我們剛剛在ServerBootstrap.init()中實現的方法,再之後將ChannelInitializer這個handler從鏈中刪除,繼續調用後面的相應的事件。(其實通過這個類似於佔位符的方式我們可以在引導過程中添加多個channelhandler,無非在initChannel()的實現中完成)。
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
我們再回到ServerBootstrapAcceptor,ServerBootstrapAcceptor重寫了channelRead(ctx,msg)方法,主要的作用是當有client連接上Server的時,pipline觸發channelRead 事件,然後會給pipline添加用戶自己的handler。其中第二個形參msg 在Nio中,是Server accpet客戶端的connect連接後,產生的NioSocketChannel對象。
init()方法完成通道的配置之後,我們繼續initAndRegister的之後步驟ChannelFuture regFuture = group().register(channel);通道完成了後,無非把通道綁定到EventLoopGroup上,(這裏是group,而非childgroup,也就是最上文的bossGroup)。
調用了NioEventGroup的register(),具體的邏輯在其超類MultithreadEventLoopGroup中。
@Override
public EventLoop next() {
return (EventLoop) super.next();
}
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
@Override
public ChannelFuture register(Channel channel, ChannelPromise promise) {
return next().register(channel, promise);
}
無非調用next().register(),我們先來看super.next()的實現
public EventExecutor next() {
return this.children[Math.abs(this.childIndex.getAndIncrement() % this.children.length)];
}
由於EventLoopGroup在初始化時候創建了singleThreadEventLoop數組,其實就是NioEventLoop(詳細見之前的博客EventLoop解析),所以通過如上方式選擇相應的NioEventLoop返回,並調用其register(),childIndex是一個AtomicInteger類。
由於NioEventLoop extends SingleThreadEventLoop,NioEventLoop沒有重寫該方法,因此看 SingleThreadEventLoop類中的register方法。
@Override
public ChannelFuture register(Channel channel) {
return register(channel, new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
if (channel == null) {
throw new NullPointerException("channel");
}
if (promise == null) {
throw new NullPointerException("promise");
}
channel.unsafe().register(this, promise);
return promise;
}
具體調用了usafe類的register()方法,跟蹤代碼,具體邏輯的主要實現在AbstractNioUnsafe的doRegister()方法。 @Override
protected void doRegister() throws Exception {
boolean selected = false;
for (; ; ) {
try {
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException e) {
/**
* CancelledKeyException拋出的場景:
* 當前Channel已經註冊了給定的selector,但是相應個key卻已經被取消了
*/
if (!selected) {
// Force the Selector to select now as the "canceled" SelectionKey may still be
// cached and not removed because no Select.select(..) operation was called yet.
eventLoop().selectNow();
selected = true;
} else {
// We forced a select operation on the selector before but the SelectionKey is still cached
// for whatever reason. JDK bug ?
throw e;
}
}
}
}
selectionKey = javaChannel().register(eventLoop().selector, 0, this);就完成了ServerSocketChannel註冊到Selector中。
到這裏基本上把initAndRegister介紹完了。
確保註冊與初始化都已經完成,調用doBind0();(AbstractBootstrap.doBind0())
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.
//判斷註冊事件是否成功,如果註冊成功,則把bind任務丟到任務隊列
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
//channel註冊到eventloop成功後,纔開始調用Channel的bind方法
if (regFuture.isSuccess()) {
//調用Channel的bind方法
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
因爲之前initAndRegister已經完成,所以eventloop線程模型已經建立(channel已經註冊至eventLoop中),這裏只需將bind跟添加監聽器任務扔至Task隊列中,異步地完成。
channel的bind函數調用了pipline的bind調用了tail的bind,最終調用了跟tailhandler綁定DefaultChannelHandlerContext的bind函數
@Override
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
validatePromise(promise, false);
//從tail ---> head找Outbound執行
final DefaultChannelHandlerContext 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;
}
其中findContextOutBound()函數無非找到下一個類型爲outbound的handler
private DefaultChannelHandlerContext findContextOutbound() {
DefaultChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
}
這裏鏈式調用每一個outboundhandler的bind函數,最後到head的headhandler中bind實現 @Override
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
unsafe.bind(localAddress, promise);
}
具體邏輯在AbstractUnsafe.bind()
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
if (!ensureOpen(promise)) {
return;
}
// See: https://github.com/netty/netty/issues/576
if (!PlatformDependent.isWindows() && !PlatformDependent.isRoot() &&
Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress()) {
// Warn a user about the fact that a non-root user can't receive a
// broadcast packet on *nix if the socket is bound on non-wildcard address.
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()方法,如果使用的NIO那麼這裏調用的就是NioServerSocketChannel實現的doBind()方法
doBind(localAddress);
} catch (Throwable t) {
promise.setFailure(t);
closeIfClosed();
return;
}
promise.setSuccess();
//判斷端口是否已經成功,成功則觸發fireChannelActive事件
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
}
最後調用了子類的dobind(),在nioServerSocketChannel中實現
//調用ServerSocketChannel的bind方法
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
//
javaChannel().socket().bind(localAddress, config.getBacklog());
}
最後無非調用底層ServerSocketChannel的bind方法~已經跑到jdk層面了