原文部落格地址: pjmike的部落格
前言
本文主要分析 Netty服務端的啟動,以便對Netty框架有一個基本的認識,我用的Netty版本是 netty-4.1.29
,之前的文章Netty 系列文章之基本元件概覽
對Netty的基本元件做了一個簡單的介紹,算是對本文分析Netty服務端的啟動做一個基礎鋪墊
服務端程式碼
該原始碼出自 netty官方提供的 服務端demo,詳細地址: github.com/netty/netty…
我做了一點小改動,程式碼如下:
public final class EchoServer {
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static void main(String[] args) throws Exception {
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(serverHandler);
}
});
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
複製程式碼
服務端啟動流程概覽:
- 建立
ServerBootStrap
啟動類 - 設定並繫結
NioEventLoopGroup
執行緒池 - 建立服務端
Channel
- 新增並設定
ChannelHandler
- 繫結並啟動監聽埠
在之前的文章我就提到過,ServerBootstrap是Netty服務端的啟動輔助類,幫助Netty服務端完成初始化,下面我將深入程式碼,仔細分析Netty服務端啟動過程中各元件的建立與初始化
Channel的建立和初始化過程
Channel是Netty的網路操作抽象類,對應於JDK底層的 Socket,Netty服務端的Channel型別是 NioServerSocketChannel。下面來分析NioServerSocketChannel的建立和初始化
NioServerSocketChannel的建立
NioServerSocketChannel
的建立實際上是從 ServerBootStrap
的bind()
方法開始的,進入bind()
原始碼分析(AbstractBootstrap
的bind()方法):
......
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
......
/**
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(SocketAddress localAddress) {
validate();
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
return doBind(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()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
}
......
}
複製程式碼
在原始碼裡注意到一個 initAndRegister()
方法,這個方法就負責 NioServerSocketChannel
的初始化和註冊操作,走進initAndRegister()
方法,如下圖所示:
從上圖可以看出,原始碼裡是呼叫 channelFactory.newChannel()
來建立 channel , 走進ChannelFactory
發現該介面被 @Deprecated
註解標註了,說明是一個過時的介面:
@Deprecated
public interface ChannelFactory<T extends Channel> {
/**
* Creates a new channel.
*/
T newChannel();
}
複製程式碼
我用的Netty版本是 Netty-4.1.29
,其Netty API 文件 中介紹 io.netty.bootstrap.ChannelFactory
提到用 io.netty.channel.ChannelFactory
代替。
這裡 ChannelFactory
只是一個工廠介面,真正建立 Channel
的是ReflectiveChannelFactory類,它是ChannelFactory
的一個重要實現類,該類通過反射方式建立 Channel
,原始碼如下:
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
@Override
public T newChannel() {
try {
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
@Override
public String toString() {
return StringUtil.simpleClassName(clazz) + ".class";
}
}
複製程式碼
其中 newChannel()
方法通過 clazz.getConstructor().newInstance()
來建立 Channel,即通過反射方式來建立 Channel,而這個 clazz
就是 通過ServerBootStrap
的 channel
方法傳入的,最開始的服務端程式碼傳入的NioServerSocketChannel,所以對應通過反射建立了NioServerSocketChannel,並且 ChannelFactory
的初始化也是在該方法中進行的,程式碼如下:
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
複製程式碼
到此,NioServerSocketChannel
的建立過程大體結束,再次總結一下:
ServerBootstrap
中的ChannelFactory
的實現是ReflectiveChannelFactory
- 生成的 Channel的具體型別是
NioServerSocketChannel
Channel
的例項化過程其實就是呼叫ChannelFactory.newChannel
方法,實際上是通過反射方式進行建立的
NioServerSocketChanel的例項化過程
在前面的分析中,NioServerSocketChannel是通過反射建立的,它的構造方法如下:
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
/**
* Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
* {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
*
* See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
*/
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
/**
* Create a new instance
*/
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
複製程式碼
方法newSocket利用 provider.openServerSocketChannel()
生成Nio中的 ServerSocketChannel
物件,然後呼叫過載的構造器:
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
複製程式碼
該構造器中,呼叫了父類的構造器,傳入的引數是 SelectionKey.OP_ACCEPT
,這個引數對於有Java NIO程式設計經驗的人來說應該非常熟悉,在Java NIO中服務端要監聽客戶端的連線請求,就向多路複用器 Selector
註冊 SelectionKey.OP_ACCEPT
客戶端連線事件,而Netty又是基於 Java NIO開發的,這裡可見一斑。接著進入父類構造器:
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
}
複製程式碼
然後:
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) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
複製程式碼
設定當前 ServerSocketChannel為非阻塞通道,然後再次進入父類構造器 AbstractChannel(Channel parent)
:
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
複製程式碼
- parent屬性設定為null
- 初始化unsafe,用來負責底層的connect,register,read和write操作
- 初始化pipeline,在例項化一個Channel的同時,當有事件發生的時候,pipeline負責呼叫相應的Handler進行處理
關於 unsafe
Netty中的unsafe
不是JDK中的sun.misc.Unsafe
,該unsafe
實際是封裝了 Java 底層 Socket的操作,因此是溝通 Netty上層和Java 底層重要的橋樑。
ChannelPipeline的初始化
每個Channel都有對應的 ChannelPipeline
,當一個Channel被建立時,對應的ChannelPipeline也會自動建立,在上面分析 NioServerSocketChannel
例項化過程就看到,在其父類構造器中,有初始化一個 pipeline
,對應原始碼如下:
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
.....
private final Channel parent;
private final ChannelId id;
private final Unsafe unsafe;
private final DefaultChannelPipeline pipeline;
......
/**
* Creates a new instance.
*
* @param parent
* the parent of this channel. {@code null} if there's no parent.
*/
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
......
/**
* Returns a new {@link DefaultChannelPipeline} instance.
*/
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
複製程式碼
從上面程式碼看到,pipeline最終被初始化為一個 DefaultChannelPipeline
,DefaultChannelPipeline
是ChannelPipeline
的實現類,進入它的構造方法,如下:
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
複製程式碼
該構造方法有一個引數 channel
,這個 channel
就是我們之前傳入的 NioServerSocketChannel
。關於該構造器其他方法以及ChannelPipeline
更詳細的介紹將在後續文章分析。
NioEventLoopGroup
在我們最開始的Netty服務端程式碼中初始化了兩個 NioEventLoopGroup
,即一個處理客戶端連線請求的執行緒池——bossGroup
,一個處理客戶端讀寫操作的執行緒池——workerGroup
:
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
複製程式碼
NioEventLoopGroup
的類繼承結構圖如下所示:
從圖中可以看到,NioEventLoopGroup
實現了 Executor
介面,Executor
框架可以用來建立執行緒池的,也是一個執行緒執行器。關於 Executor
框架更加詳細的介紹請參閱 《Java併發程式設計的藝術》
NioEventLoopGroup
看了NioEventLoopGroup
的類繼承結構,下面來分析一下它的初始化過程,構造器原始碼如下:
......
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
......
public NioEventLoopGroup(int nThreads, Executor executor) {
this(nThreads, executor, SelectorProvider.provider());
}
......
public NioEventLoopGroup(
int nThreads, Executor executor, final SelectorProvider selectorProvider) {
this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
複製程式碼
上面幾個過載構造器其實沒做啥,最終呼叫父類 MultithreadEventLoopGroup 的構造器,
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
複製程式碼
這裡需要注意的是如果我們傳入的執行緒數 nThreads
是 0 的話,那麼Netty將會為我們設定預設的執行緒數 DEFAULT_EVENT_LOOP_THREADS
,這個預設值是處理器核心數 * 2,如下:
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
}
}
複製程式碼
在 bossGroup
這個執行緒池我們傳入的 nThread
是1,實際上在 bossGroup
中只會有一個執行緒用於處理客戶端連線請求,所以這裡設定為1,而不使用預設的執行緒數,至於為什麼只用一個執行緒處理連線請求還需用執行緒池,在Stack Overflow有相關問題的討論。
然後回來再次進入父類MultithreadEventExecutorGroup的構造器,
......
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
......
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
if (!success) {
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j ++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
chooser = chooserFactory.newChooser(children);
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
複製程式碼
MultithreadEventExecutorGroup
管理著 eventLoop
的生命週期,它有幾個變數:
- children: EventExecutor陣列,儲存eventloop
- chooser: 執行緒選擇器,從children中選取一個 eventloop的策略
MultithreadEventExecutorGroup
的構造器主要分為以下幾個步驟:
- 建立執行緒執行器——
ThreadPerTaskExecutor
- 呼叫
newChild
方法初始化children
陣列 - 建立執行緒選擇器——
chooser
建立ThreadPerTaskExecutor
我們一開始初始化 NioEventLoopGroup
,並沒有傳入 Executor
引數:
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
複製程式碼
所以到父類MultithreadEventExecutorGroup
構造器時,executor 為null, 然後執行:
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
複製程式碼
ThreadPerTaskExecutor是一個執行緒執行器,它實現了 Executor
介面,
public final class ThreadPerTaskExecutor implements Executor {
private final ThreadFactory threadFactory;
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
this.threadFactory = threadFactory;
}
@Override
public void execute(Runnable command) {
threadFactory.newThread(command).start();
}
}
複製程式碼
ThreadPerTaskExecutor 實現了 execute
方法,每次通過呼叫execute
方法執行執行緒任務
呼叫 newChild 方法初始化 children 陣列
children[i] = newChild(executor, args);
複製程式碼
在一個for迴圈裡,nThread執行緒數是總的迴圈次數,通過 newChild方法初始化 EventExecutor陣列的每個元素,而 newChild
方法如下:
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
複製程式碼
每次迴圈通過newChild例項化一個NioEventLoop
物件。
建立執行緒選擇器——chooser
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
......
private static boolean isPowerOfTwo(int val) {
return (val & -val) == val;
}
複製程式碼
根據EventExecutor[]
陣列的大小,採用不同策略初始化一個 Chooser,如果大小為 2的冪次方則採用 PowerOfTwoEventExecutorChooser
,否則使用 GenericEventExecutorChooser
。
無論使用哪個 chooser,它們的功能都是一樣的,即從 EventExecutor[]
陣列中,這裡也就是 NioEventLoop
陣列中,選擇一個合適的 NioEventLoop
。
NioEventLoop的初始化
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
複製程式碼
從前面的分析就可以看到,通過 newChild方法初始化 NioEventLoopGroup
中的 NioEventLoop
,下面來看下NioEventLoop
的構造方法是怎樣的:
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
final SelectorTuple selectorTuple = openSelector();
selector = selectorTuple.selector;
unwrappedSelector = selectorTuple.unwrappedSelector;
selectStrategy = strategy;
}
複製程式碼
之前在 NioEventLoopGroup
的構造器中通過 SelectorProvider.provider()
建立了一個 SelectorProvider
,這裡傳遞給了NioEventLoop中的provider,而NioEventLoop
又通過 openSelector()
方法獲取一個 selector物件,實際上是通過 provider
的openSelector
方法。這不就是 對應Java NIO中的建立多路複用器 selector。(這裡只是簡單闡述NioEventLoop的構造方法,後續文章會對NioEventLoop做更加詳細的分析)
Channel的註冊過程
前面已經介紹了 Channel的建立和初始化過程,是在 initAndRegister
方法中進行的,這個方法裡還會將初始化好的 channel註冊到 EventLoop
執行緒中去
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {
.....
}
ChannelFuture regFuture = config().group().register(channel);
......
}
複製程式碼
呼叫config().group().register
方法將 channel註冊到 EventLoopGroup
中去,其目的就是為了實現NIO中把ServerSocketChannel註冊到 Selector中去,這樣就是可以實現client請求的監聽,程式碼如下:
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
......
public EventLoop next() {
return (EventLoop) super.next();
}
複製程式碼
父類MultithreadEventExecutorGroup的next()方法,next方法使用 chooser策略從 EventExecutor[]
陣列中選擇一個 SingleThreadEventLoop
:
public EventExecutor next() {
return chooser.next();
}
.....
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
複製程式碼
然後再執行 SingleThreadEventLoop
的 register()
註冊方法:
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
...
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
複製程式碼
上面程式碼呼叫了 unsafe的register方法,具體是AbstractUnsafe.register
,而unsafe主要用於實現底層的 rergister,read,write等操作。該register
方法是:
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
......
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
......
}
}
複製程式碼
將 eventLoop 賦值給 Channel 的 eventLoop 屬性,然後又呼叫了 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) {
......
}
}
複製程式碼
上面有個關鍵方法就是 doRegister()
,doRegister
才是最終Nio的註冊方法:
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;
}
}
}
}
複製程式碼
通過javaChannel().register(eventLoop().unwrappedSelector(), 0, this)
將 Channel對應的Java NIO ServerSocketChannel註冊到 EventLoop
中的Selector上,最終完成了channel向eventLoop的註冊過程。
這裡總結下 Channel註冊過程中函式呼叫鏈: AbstractBootstrap.initAndRegister -> MultithreadEventLoopGroup.register -> SingleThreadEventLoop.register -> AbstractUnsafe.register -> AbstractUnsafe.register0 -> AbstractNioChannel.doRegister()
新增 ChannelHandler
在之前的 initAndRegister()
方法裡,裡面有個 init()
方法,如下:
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(0));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
}
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));
}
});
}
});
}
複製程式碼
在init()
方法中設定服務端自定義的 ChannelOptions,ChannelAttrs
屬性和為服務端Channel建立出來的新連線的Channel設定的自定義屬性ChildOptions,ChildAttrs
,這裡就不多敘述設定引數的問題了,重點關注 pipeline
的 addLast
方法,該方法就是新增用於處理出站和入站資料流的 ChannelHandler,而pipeline
是從 channel
中獲取的,之前分析過當建立channel
時會自動建立一個對應的 channelPipeline
。
至於ChannelInitializer
又是什麼,來看下它的類繼承結構圖就知道了:
ChannelInitializer
是一個抽象類,實現了ChannelHandler
介面,它有一個抽象方法initChannel
,上面程式碼實現了該方法並且新增了bootstrap
的handler,邏輯如下:
.....
//1
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
......
//2
public final ChannelHandler handler() {
return bootstrap.handler();
}
......
//3
final ChannelHandler handler() {
return handler;
}
複製程式碼
initChannel
新增的Handler
就是我們服務端程式碼中 serverbootstrap
設定的handler
,如下:
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(serverHandler);
}
});
複製程式碼
示例程式碼設定的handler為LoggingHandler
,用於處理日誌,這裡不細說。上面的initChannel
方法可以新增 Handler
,這裡的serverbootstrap
啟動類還增加了 childHandler方法,也是用來新增 handler,只不過是向已經連線的 channel客戶端的 channnelpipeline
新增handler
。
serverbootstrap.handler()
設定的handler
在初始化就會執行,而 serverbootstrap.childHandler()
設定的childHandler
在客戶端連線成功才會執行
小結
由於自身知識與經驗有限,對Netty的服務端啟動原始碼分析得不是很全面,在此過程中也參考了一些大佬的Netty原始碼分析文章,本文如有錯誤之處,歡迎指出。