上一節學習了pipeline
初始化的過程。初始化了HeadContext
和TailContext
,並構建了pipeline
雙向鏈表,每個節點存儲ChannelHandlerContext
。
本節研究添加ChannelHandler
的過程。在學習之前先整理一些之前學到的內容。
- 在
服務端channel初始化channle
的過程中,bossGroup
爲服務端channel
的pipeline
添加了一個特殊的ChannelHandler
:ServerBootstrapAcceptor
,如下這幅圖。
可
客戶端channel
所在的workGroup
處理所有childHandler
,這裏的Handler
屬於客戶端channel
因此在
bossGroup
在初始化channel
的過程中,會調用addLast()
增加handler
,促發HandlerAdded
事件,而引導配置的時候觸發的是ChannelInitializer#handlerAdded()
。
入口
在引導的過程當中,重寫了ChannelInitializer#initChannel()
,其中獲取了pipeline
並調用了addLast()
方法,同事將ChannelHandler
作爲參數傳入addLast
方法中。
class DataServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new DefaultEventExecutorGroup(2),
new DataServerHandler(),
new DataServerOutHandler2(),
new DataServerOutHandler(),
new DataServerHandler2());
}
}
DefaultChannelPipeline#addLast()
@Override
public final ChannelPipeline addLast(ChannelHandler... handlers) {
return addLast(null, handlers);
}
@Override
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
if (handlers == null) {
throw new NullPointerException("handlers");
}
//循環添加
for (ChannelHandler h: handlers) {
if (h == null) {
break;
}
addLast(executor, null, h);
}
return this;
}
具體的addLast
方法做了以下幾件事
- 檢查
Handler
是否重複添加 - 創建數節點
ChannelHandlerContext
- 添加數據節點
- 觸發
handlerAdded
事件,執行對應Handler
的回調函數
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
//檢查是否重複添加
checkMultiplicity(handler);
//group:異步線程
//創建數據節點
//filterName:從頭節點開始遍歷檢查名字是否重複,如果沒有傳入name則生成一個name
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;
}
//觸發事件回調函數:handlerAdded
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
callHandlerAddedInEventLoop(newCtx, executor);
return this;
}
}
//觸發事件回調函數:handlerAdded
callHandlerAdded0(newCtx);
return this;
}
-
checkMultiplicity(handler);
檢查Handler
是否重複添加
邏輯主要時判斷是否有@Sharable
註解,從而判斷是否允許從夫,其次通過成員變量added
屬性判斷是否被添加過。如果重複的話則排除異常。
private static void checkMultiplicity(ChannelHandler handler) {
if (handler instanceof ChannelHandlerAdapter) {
ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
//判斷是否是共享 同時 是否時被添加過的
if (!h.isSharable() && h.added) {
throw new ChannelPipelineException(
h.getClass().getName() +
" is not a @Sharable handler, so can't be added or removed multiple times.");
}
h.added = true;
}
}
public boolean isSharable() {
/**
* Cache the result of {@link Sharable} annotation detection to workaround a condition. We use a
* {@link ThreadLocal} and {@link WeakHashMap} to eliminate the volatile write/reads. Using different
* {@link WeakHashMap} instances per {@link Thread} is good enough for us and the number of
* {@link Thread}s are quite limited anyway.
*
* See <a href="https://github.com/netty/netty/issues/2289">#2289</a>.
*/
Class<?> clazz = getClass();
Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
Boolean sharable = cache.get(clazz);
if (sharable == null) {
sharable = clazz.isAnnotationPresent(Sharable.class);
cache.put(clazz, sharable);
}
return sharable;
}
-
newCtx = newContext(group, filterName(name, handler), handler);
,創建數據節點
實例化DefaultChannelHandlerContext
的過程中將handler
作爲成員變量持有,並且標記了handler
是inbound
還是outbound
,並將一些重要的組件pipeline
,executor
保存起來。
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
//實例化DefaultChannelHandlerContext
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
private final ChannelHandler handler;
DefaultChannelHandlerContext(
DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
//標記類型
super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
if (handler == null) {
throw new NullPointerException("handler");
}
//持有handler
this.handler = handler;
}
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,
boolean inbound, boolean outbound) {
this.name = ObjectUtil.checkNotNull(name, "name");
this.pipeline = pipeline;
this.executor = executor;
this.inbound = inbound;
this.outbound = outbound;
// Its ordered if its driven by the EventLoop or the given Executor is an instanceof OrderedEventExecutor.
ordered = executor == null || executor instanceof OrderedEventExecutor;
}
指的注意的是childExecutor
,該方法處理添加handler
時,傳入的EventExecutorGroup
。該Executor
可用於用戶在回調方法中處理異步計算。
private EventExecutor childExecutor(EventExecutorGroup group) {
if (group == null) {
return null;
}
Boolean pinEventExecutor = channel.config().getOption(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP);
if (pinEventExecutor != null && !pinEventExecutor) {
return group.next();
}
//存儲異步線程的容器:管理異步線程
Map<EventExecutorGroup, EventExecutor> childExecutors = this.childExecutors;
if (childExecutors == null) {
// Use size of 4 as most people only use one extra EventExecutor.
//新建一個Map存儲異步線程
childExecutors = this.childExecutors = new IdentityHashMap<EventExecutorGroup, EventExecutor>(4);
}
// Pin one of the child executors once and remember it so that the same child executor
// is used to fire events for the same channel.
//存儲異步線程
EventExecutor childExecutor = childExecutors.get(group);
if (childExecutor == null) {
childExecutor = group.next();
childExecutors.put(group, childExecutor);
}
//返回異步線程
return childExecutor;
}
用戶配置異步線程,處理異步計算。
class DataServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
//配置異步線程處理異步計算
.addLast(new DefaultEventExecutorGroup(2),
new DataServerHandler(),
new DataServerOutHandler2(),
new DataServerOutHandler(),
new DataServerHandler2());
}
}
class DataServerHandler extends SimpleChannelInboundHandler<Object> {
public DataServerHandler() {
super(false);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println(MessageFormat.format("---> receive client msg = \"{0}\"", byteBuf.toString(CharsetUtil.UTF_8)));
System.out.println("<--- send to client msg = server msg");
ByteBuf serverMsg = Unpooled.copiedBuffer("server msg", CharsetUtil.UTF_8);
ctx.write(serverMsg);
ctx.executor().execute(new Runnable() {
public void run() {
try {
TimeUnit.SECONDS.sleep(20);
System.out.println("io async end ");
} catch (InterruptedException e) {
//ignore
}
}
});
System.out.println("<--- send to client channel msg = server channel msg");
ByteBuf serverChannelMsg = Unpooled.copiedBuffer("server channel msg", CharsetUtil.UTF_8);
//tail.write
ctx.channel().write(serverChannelMsg);
System.out.println("<--- send to client msg = server msg2");
ByteBuf serverMsg2 = Unpooled.copiedBuffer("server msg2", CharsetUtil.UTF_8);
ctx.write(serverMsg2);
ctx.fireChannelRead(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelReadComplete();
}
}
結合初始化NioEventLoopGroup
的過程實際上這裏保存的executor
就是ThreadPerTaskExecutor
,因此同樣時新增了一個EventLoop
數組並初始化一個chooser
,當group.next()
的時候調用chooser
選擇一個EventLoop
執行操作。
protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
this(nThreads, threadFactory == null ? null : new ThreadPerTaskExecutor(threadFactory), args);
}
- 添加數據節點
addLast0(newCtx);
其邏輯主要是鏈表的操作:將新的數據節點添加到尾節點(TailContext
)之前
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
-
callHandlerAdded0(newCtx);
:觸發handlerAdded事件
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
try {
//調用回調函數
ctx.callHandlerAdded();
} catch (Throwable t) {
//省略代碼
}
final void callHandlerAdded() throws Exception {
// We must call setAddComplete before calling handlerAdded. Otherwise if the handlerAdded method generates
// any pipeline events ctx.handler() will miss them because the state will not allow it.
if (setAddComplete()) {
//獲取handler調用handlerAdded觸發事件
handler().handlerAdded(this);
}
}
整理以下整個添加handler的過程
bossGroup
初始化的時候將對服務端channel
的pipeline
進行了添加ChannelInitializer
的操作,促發用戶initChannel
回調方法,添加具體的handler
,之後刪除ChannelInitializer
。其中
bossGroup
添加具體的handler
,包括兩個:引導調用handler()
配置的,另一個是ServerBootstrapAcceptor
。當新連接接入時候,會創建
客戶端channel
並初始化,當處理讀取事件的時候觸發ServerBootstrap.ServerBootstrapAcceptor#channelRead()
,調用如下代碼
//添加childHandler
child.pipeline().addLast(childHandler);
該childHandler
也是引導配置的時候調用childHandler()
方法,傳入ChannelInitializer
實例作爲參數。因此同樣的邏輯。不一樣的是,childHandler
的處理邏輯已經交給了childGroup
進行處理了,脫離了bossGroup
,查看如下代碼。
//選擇NioEventLoop並註冊Selector
childGroup.register(child)
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});