上下文ChannelHandlerContext
的最大作用就是向它所属管道ChannelPipeline
的上游或下游传递事件。
那么它是如何实现的呢?
这就要看
ChannelHandlerContext
接口的实现类AbstractChannelHandlerContext
。
一. 成员属性
1.1 双向链表
// 组成双向链表
volatile AbstractChannelHandlerContext next;
volatile AbstractChannelHandlerContext prev;
通过
next
和prev
组成一个双向链表,这样就可以向上或者向下查找管道中的其他处理器上下文。
1.2 上下文状态
/**
* ChannelHandler.handlerAdded(ChannelHandlerContext) 即将被调用。
*/
private static final int ADD_PENDING = 1;
/**
* ChannelHandler.handlerAdded(ChannelHandlerContext) 已经被调用。
*/
private static final int ADD_COMPLETE = 2;
/**
* ChannelHandler.handlerRemoved(ChannelHandlerContext) 已经被调用。
*/
private static final int REMOVE_COMPLETE = 3;
/**
* 初始状态,
* ChannelHandler.handlerAdded(ChannelHandlerContext) 和
* ChannelHandler.handlerRemoved(ChannelHandlerContext) 都没有被调用。
*
*/
private static final int INIT = 0;
private volatile int handlerState = INIT;
private static final AtomicIntegerFieldUpdater HANDLER_STATE_UPDATER
= AtomicIntegerFieldUpdater.newUpdater(AbstractChannelHandlerContext.class, "handlerState");
- 状态一共分为
4
种:INIT
,ADD_PENDING
,ADD_COMPLETE
和REMOVE_COMPLETE
。- 通过
handlerState
和HANDLER_STATE_UPDATER
, 采用CAS
的方式原子化更新属性,这样就不用加锁处理并发问题。
1.3 不可变的属性
即被
final
修饰的属性,在创建ChannelHandlerContext
对象时就需要赋值。
-
DefaultChannelPipeline pipeline
当前上下文所属的管道
pipeline
, 而且它的类型就定死了是DefaultChannelPipeline
类。 -
name
表示上下文的名称 -
ordered
表示上下文的执行器是不是有序的 -
executor
上下文的执行器如果这个值是
null
,那么上下文的执行器用的就是所属通道Channel
的事件轮询器。 -
executionMask
表示事件执行器ChannelHandler
的执行标记用来判断是否跳过执行器
ChannelHandler
的某些事件处理方法。
二. 重要方法
2.1 构造方法
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor,
String name, Class extends ChannelHandler> handlerClass) {
this.name = ObjectUtil.checkNotNull(name, "name");
this.pipeline = pipeline;
this.executor = executor;
// 调用 ChannelHandlerMask.mask(handlerClass) 方法,获取执行标记
this.executionMask = mask(handlerClass);
// 表示上下文的事件执行器是不是有序的,即以有序/串行的方式处理所有提交的任务。
// executor == null,说明当前上下文用的是通道Channel的 channel().eventLoop(),这个肯定是有序的
ordered = executor == null || executor instanceof OrderedEventExecutor;
}
这个是
AbstractChannelHandlerContext
唯一的构造方法,基本上赋值了它所有的final
属性。
2.2 状态相关方法
-
等待添加
final void setAddPending() { boolean updated = HANDLER_STATE_UPDATER.compareAndSet(this, INIT, ADD_PENDING); // 这应该总是为真,因为它必须在 setAddComplete()或 setRemoved()之前调用。 assert updated; }
将上下文的状态变成等待添加状态
ADD_PENDING
。 -
已添加
final boolean setAddComplete() { for (;;) { int oldState = handlerState; if (oldState == REMOVE_COMPLETE) { return false; } // 确保当 handlerState 已经是REMOVE_COMPLETE时,我们永远不会更新。 // oldState 通常是 ADD_PENDING,但当使用不公开排序保证的 EventExecutor 时,也可能是 REMOVE_COMPLETE。 if (HANDLER_STATE_UPDATER.compareAndSet(this, oldState, ADD_COMPLETE)) { return true; } } }
通过
for (;;)
死循环,采用CAS
的方法,将上下文的状态变成已添加ADD_COMPLETE
。
只有已添加状态上下文的事件处理器ChannelHandler
才能处理事件。final void callHandlerAdded() throws Exception { // 我们必须在调用 handlerAdded 之前调用 setAddComplete,将状态改成 REMOVE_COMPLETE, // 否则这个上下文对应的事件处理器将不会处理任何事件,因为状态不允许。 if (setAddComplete()) { handler().handlerAdded(this); } }
将状态变成已添加,如果设置成功就调用
handler().handlerAdded(this)
方法,通知事件处理器已经被添加到管道上了。 -
已删除
final void setRemoved() { handlerState = REMOVE_COMPLETE; } final void callHandlerRemoved() throws Exception { try { // 只有 handlerState 状态变成 ADD_COMPLETE 时,才会调用 handler().handlerRemoved(this); // 也就是说 只有之前调用过 handlerAdded(…)方法,之后才会调用handlerRemoved(…) 方法。 if (handlerState == ADD_COMPLETE) { handler().handlerRemoved(this); } } finally { // 在任何情况下都要将该上下文标记为已删除 setRemoved(); } }
- 将上下文状态变成已删除。
- 如果上下文状态之前的状态是已添加,那么就会调用
handler().handlerRemoved(this)
方法。 - 也就是说,只有之前调用过
handlerAdded(…)
方法,之后才会调用handlerRemoved(…)
方法。
2.3 发送IO
事件
2.3.1 发送入站 IO
事件
/**
* 发送注册的IO事件
*/
@Override
public ChannelHandlerContext fireChannelRegistered() {
// 通过 findContextInbound 方法找到下一个入站处理器上下文
// 通过 invokeChannelRegistered 方法,调用下一个入站处理器的对应事件处理方法
invokeChannelRegistered(findContextInbound(MASK_CHANNEL_REGISTERED));
return this;
}
/**
* 保证在上下文 next 的事件执行器线程中调用对应方法
*/
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
// 如果当前线程是 next 事件执行器EventExecutor线程,直接调用
next.invokeChannelRegistered();
} else {
// 如果当前线程不是 next 事件执行器线程,
// 那么就通过事件执行器EventExecutor 的execute方法,
// 保证在执行器线程调用
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
/**
* 调用该上下文拥有的事件处理器 ChannelHandler 的对应方法
*/
private void invokeChannelRegistered() {
// 判断当前上下文有没有已经添加到管道上了
if (invokeHandler()) {
// 如果已经添加完成了,就调用对应事件处理器方法
try {
((ChannelInboundHandler) handler()).channelRegistered(this);
} catch (Throwable t) {
invokeExceptionCaught(t);
}
} else {
// 如果没有添加完成,即状态不是 ADD_COMPLETE,
// 就继续调用 fire 的方法,让管道下一个处理器处理
fireChannelRegistered();
}
}
我们以注册的 IO
事件为例,发现调用过程:
- 先通过
findContextInbound
方法,找到下一个入站处理器上下文。 - 再调用
invokeChannel...
系列静态方法,保证处理器方法的调用是在它的上下文事件执行器EventExecutor
线程中。 - 最后通过
invokeChannel...
系列成员方法,调用该上下文拥有的事件处理器ChannelHandler
的对应方法。要通过
invokeHandler()
方法判断该上下文是否已经添加到管道上,只有已经完全添加的上下文才能处理事件。
2.3.2 发送出站IO
操作
/**
* 发送绑定 IO 操作
*/
@Override
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
ObjectUtil.checkNotNull(localAddress, "localAddress");
// 检查 promise 是否有效
if (isNotValidPromise(promise, false)) {
// 返回 true, 说明已取消,直接返回,不做下面的处理
return promise;
}
// 通过 findContextOutbound 方法找到上一个出站处理器上下文
final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);
// 保证在上下文 next 的事件执行器线程中调用对应方法
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, false);
}
return promise;
}
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
// 判断当前上下文有没有已经添加到管道上了
if (invokeHandler()) {
// 如果已经添加完成了,就调用对应事件处理器方法
try {
((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
// 如果没有添加完成,即状态不是 ADD_COMPLETE,
// 就继续调用 fire 的方法,让管道下一个处理器处理
bind(localAddress, promise);
}
}
我们以绑定 IO
事件为例,你会发现调用流程和入站事件差不多,只不过出站事件没有中间那个静态方法。
2.3.3 invokeTasks
作用
你会发现有的入站和出站事件的处理,与上面的流程不一样,有四个事件:
-
channelReadComplete
读完成的入站事件 -
channelWritabilityChanged
可读状态改变的入站事件 -
read
设置读的出站事件 -
flush
刷新数据的出站事件
public ChannelHandlerContext fireChannelReadComplete() {
invokeChannelReadComplete(findContextInbound(MASK_CHANNEL_READ_COMPLETE));
return this;
}
static void invokeChannelReadComplete(final AbstractChannelHandlerContext next) {
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelReadComplete();
} else {
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(tasks.invokeChannelReadCompleteTask);
}
}
private void invokeChannelReadComplete() {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelReadComplete(this);
} catch (Throwable t) {
invokeExceptionCaught(t);
}
} else {
fireChannelReadComplete();
}
}
你会发现发送读完成事件的处理过程和上面有区别,不同的是:
- 上面是通过
executor.execute(new Runnable())
,每次都创建新的Runnable
对象。- 而这里是通过一个
invokeTasks
对象,不用每次都创建新的Runnable
对象,减少对象创建的实例。
private static final class Tasks {
private final AbstractChannelHandlerContext next;
// `channelReadComplete` 读完成的入站事件
private final Runnable invokeChannelReadCompleteTask = new Runnable() {
@Override
public void run() {
next.invokeChannelReadComplete();
}
};
// `read` 设置读的出站事件
private final Runnable invokeReadTask = new Runnable() {
@Override
public void run() {
next.invokeRead();
}
};
// `channelWritabilityChanged` 可读状态改变的入站事件
private final Runnable invokeChannelWritableStateChangedTask = new Runnable() {
@Override
public void run() {
next.invokeChannelWritabilityChanged();
}
};
// `flush` 刷新数据的出站事件
private final Runnable invokeFlushTask = new Runnable() {
@Override
public void run() {
next.invokeFlush();
}
};
Tasks(AbstractChannelHandlerContext next) {
this.next = next;
}
}
为什么这四个事件可以呢?
- 你仔细观察,这四个事件都没有参数,也就是说每次调用的时候,没有变化。
- 也许你会说
channelRegistered
,channelUnregistered
,channelActive
和channelInactive
这几个事件也没有参数啊,为什么它们不这么照着上面的处理呢?主要是因为这几个比较特殊,它们只会调用一次,所以没有必要那么处理。
2.3.4 写操作
写操作的处理过程也和上面的不是太一样,多了点特别处理。
我们知道写操作
write
和 刷新操作flush
是一对的,不调用刷新的话,写入的数据永远不会发送到远端。
2.3.4.1 刷新操作
/**
* 在管道中寻找下一个事件处理器 进行刷新的IO操作
*/
@Override
public ChannelHandlerContext flush() {
final AbstractChannelHandlerContext next = findContextOutbound(MASK_FLUSH);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeFlush();
} else {
// 因为刷新操作没有参数,不用每次都创建新的 Runnable 实例,
// 直接复用
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
safeExecute(executor, tasks.invokeFlushTask, channel().voidPromise(), null, false);
}
return this;
}
/**
* 刷新
*/
private void invokeFlush() {
if (invokeHandler()) {
// 调用当前上下文对应处理器的 flush 处理方法
invokeFlush0();
} else {
// 当前上下文对应的处理器不处理,
// 继续在管道中寻找下一个事件处理器处理
flush();
}
}
private void invokeFlush0() {
try {
((ChannelOutboundHandler) handler()).flush(this);
} catch (Throwable t) {
invokeExceptionCaught(t);
}
}
- 寻找下一个事件处理器
- 保证
invokeFlush
方法调用在下一个事件处理器的执行器线程中;又因为刷新操作没有额外参数,不用每次都创建新的Runnable
实例,直接复用invokeTasks
- 通过
invokeHandler()
判断是否跳过当前事件处理器的处理方法。
2.3.4.2 写入操作
/**
* 在管道中寻找下一个事件处理器 进行写入的IO操作
*/
@Override
public ChannelFuture write(final Object msg, final ChannelPromise promise) {
// 写入消息,不刷新
write(msg, false, promise);
return promise;
}
/**
* 在管道中寻找下一个事件处理器 进行写入并刷新的IO操作
*/
@Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
// 写入消息并且刷新
write(msg, true, promise);
return promise;
}
提供写入和写入并刷新两个方法。它们都调用了
write(Object, boolean, ChannelPromise)
方法。
private void write(Object msg, boolean flush, ChannelPromise promise) {
ObjectUtil.checkNotNull(msg, "msg");
try {
// 检查 promise 是否有效
if (isNotValidPromise(promise, true)) {
// 回收引用
ReferenceCountUtil.release(msg);
// cancelled
return;
}
} catch (RuntimeException e) {
// 回收引用
ReferenceCountUtil.release(msg);
throw e;
}
// 通过 findContextOutbound 方法找到上一个出站处理器上下文
final AbstractChannelHandlerContext next = findContextOutbound(flush ?
(MASK_WRITE | MASK_FLUSH) : MASK_WRITE);
// 只是添加附加信息,在内存泄露的时候,可以获取到这个附加信息
final Object m = pipeline.touch(msg, next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
// 在当前上下文线程中
if (flush) {
// 如果包括刷新,就调用 invokeWriteAndFlush 方法
next.invokeWriteAndFlush(m, promise);
} else {
// 如果不包括刷新,就调用 invokeWrite 方法
next.invokeWrite(m, promise);
}
} else {
// 将写操作封装成一个 WriteTask,也是一个 Runnable 子类。
final WriteTask task = WriteTask.newInstance(next, m, promise, flush);
if (!safeExecute(executor, task, promise, m, !flush)) {
// We failed to submit the WriteTask. We need to cancel it so we decrement the pending bytes
// and put it back in the Recycler for re-use later.
//
// See https://github.com/netty/netty/issues/8343.
task.cancel();
}
}
}
- 这个方法流程和之前的流程没有区别,唯一不同的就是它创建了一个
WriteTask
对象,而不是一个简单的Runnable
实例。- 因为写操作比较特殊,我们需要控制等待写入数据的大小,当等待写入数据太多,那么发送
channelWritabilityChanged
入站IO
事件,告诉用户当前通道不可写了,先将缓冲区数据发送到远端。- 如何知道等待写入数据的大小?就是通过这个
WriteTask
类事件,计算写入对象m
的大小,累加加入到写入数据的大小中。
2.3.5 查找下一个处理器上下文
-
findContextInbound(int mask)
private AbstractChannelHandlerContext findContextInbound(int mask) { AbstractChannelHandlerContext ctx = this; EventExecutor currentExecutor = executor(); do { ctx = ctx.next; // 通过 MASK_ONLY_INBOUND 表示查找的是入站事件 // mask 代表处理的方法,是否需要被跳过 } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND)); return ctx; }
-
findContextOutbound
private AbstractChannelHandlerContext findContextOutbound(int mask) { AbstractChannelHandlerContext ctx = this; EventExecutor currentExecutor = executor(); do { ctx = ctx.prev; // 通过 MASK_ONLY_OUTBOUND 表示查找的是出站事件 // mask 代表处理的方法,是否需要被跳过 } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_OUTBOUND)); return ctx; }
-
skipContext
private static boolean skipContext( AbstractChannelHandlerContext ctx, EventExecutor currentExecutor, int mask, int onlyMask) { // Ensure we correctly handle MASK_EXCEPTION_CAUGHT which is not included in the MASK_EXCEPTION_CAUGHT // 这个方法返回 true,表示跳过这个 ctx,继续从管道中查找下一个。 // 因为使用的是 || 或逻辑符,两个条件只要有一个为 true,就返回 true。 // (ctx.executionMask & (onlyMask | mask)) == 0 表示这个 ctx 属于入站事件还是出站事件 // (ctx.executor() == currentExecutor && (ctx.executionMask & mask) == 0) // 只有当 EventExecutor 相同的时候,才会考虑是否跳过 ctx,因为我们要保证事件处理的顺序。 return (ctx.executionMask & (onlyMask | mask)) == 0 || // We can only skip if the EventExecutor is the same as otherwise we need to ensure we offload // everything to preserve ordering. // See https://github.com/netty/netty/issues/10067 (ctx.executor() == currentExecutor && (ctx.executionMask & mask) == 0); }
这个方法代码很简单,但是逻辑很有意思。
- 方法的返回值表示是否需要跳过这个上下文
ctx
,返回true
则跳过。 -
(ctx.executionMask & (onlyMask | mask)) == 0
,如果等于true
,那就表明当前这个上下文的执行标记executionMask
就没有(onlyMask | mask)
中的任何方法啊,很容易就判断它不属于入站事件或者出站事件。 - 第二个条件是判断上下文的执行标记是否包含这个方法
ctx.executionMask & mask
,如果包含结果就不是1
, 不包含就是0
(表示跳过,返回true
),所以当(ctx.executionMask & mask) == 0
的时候,返回true
, 跳过这个上下文ctx
,寻找下一个。 - 不过这里多了一个
ctx.executor() == currentExecutor
判断, 为了事件处理的顺序性,如果事件执行器线程不一样,那么不允许跳过处理器方法,即使这个方法被@Skip
注解也没用。
- 方法的返回值表示是否需要跳过这个上下文
2.3.6 invokeHandler
/**
* 尽最大努力检测 ChannelHandler.handlerAdded(ChannelHandlerContext) 是否被调用。
* 如果没有被调用则返回false,如果调用或无法检测返回true。
*
* 如果这个方法返回false,我们将不调用ChannelHandler,而只是转发事件,调用管道中下一个 ChannelHandler 处理。
*
* 因为可能管道DefaultChannelPipeline已经将这个 ChannelHandler放在链接列表中,
* 但没有调用 ChannelHandler.handlerAdded(ChannelHandlerContext) 方法,
* 有可能用户在 ChannelHandler.handlerAdded 中做了一些初始化操作,当它没有被调用时,
* 不能将 IO 事件交个这个 ChannelHandler 处理。
*/
private boolean invokeHandler() {
// Store in local variable to reduce volatile reads.
int handlerState = this.handlerState;
return handlerState == ADD_COMPLETE || (!ordered && handlerState == ADD_PENDING);
}
一般情况下,必须当上下文状态是
ADD_COMPLETE
才返回true
。
但是如果上下文的事件执行器是顺序的,那么当上下文状态是ADD_PENDING
就可以返回true
了。
三. WriteTask 类
static final class WriteTask implements Runnable {
// 使用一个对象池,复用 WriteTask 实例
private static final ObjectPool RECYCLER = ObjectPool.newPool(new ObjectCreator() {
@Override
public WriteTask newObject(Handle handle) {
return new WriteTask(handle);
}
});
// 通过静态方法得到 WriteTask 实例
static WriteTask newInstance(AbstractChannelHandlerContext ctx,
Object msg, ChannelPromise promise, boolean flush) {
// 从对象池中获取 WriteTask 实例
WriteTask task = RECYCLER.get();
// 初始化
init(task, ctx, msg, promise, flush);
return task;
}
private static final boolean ESTIMATE_TASK_SIZE_ON_SUBMIT =
SystemPropertyUtil.getBoolean("io.netty.transport.estimateSizeOnSubmit", true);
// Assuming compressed oops, 12 bytes obj header, 4 ref fields and one int field
private static final int WRITE_TASK_OVERHEAD =
SystemPropertyUtil.getInt("io.netty.transport.writeTaskSizeOverhead", 32);
private final Handle handle;
// 当前上下文对象
private AbstractChannelHandlerContext ctx;
// 写入的数据对象
private Object msg;
private ChannelPromise promise;
// 写入数据的大小
private int size; // sign bit controls flush
@SuppressWarnings("unchecked")
private WriteTask(Handle extends WriteTask> handle) {
this.handle = (Handle) handle;
}
protected static void init(WriteTask task, AbstractChannelHandlerContext ctx,
Object msg, ChannelPromise promise, boolean flush) {
task.ctx = ctx;
task.msg = msg;
task.promise = promise;
// 是否需要估算写入数据大小
if (ESTIMATE_TASK_SIZE_ON_SUBMIT) {
// 估算数据大小
task.size = ctx.pipeline.estimatorHandle().size(msg) + WRITE_TASK_OVERHEAD;
// 增加等待写入数据的大小
ctx.pipeline.incrementPendingOutboundBytes(task.size);
} else {
task.size = 0;
}
if (flush) {
// 将size 大小变成负数,那么就会调用 写入并刷新的方法
task.size |= Integer.MIN_VALUE;
}
}
@Override
public void run() {
try {
// 减小等待写入数据大小
decrementPendingOutboundBytes();
if (size >= 0) {
// 只调用写入操作
ctx.invokeWrite(msg, promise);
} else {
// 当 size < 0 ,写入并刷新操作
ctx.invokeWriteAndFlush(msg, promise);
}
} finally {
recycle();
}
}
void cancel() {
try {
// 取消的话,也需要减小等待写入数据大小
decrementPendingOutboundBytes();
} finally {
recycle();
}
}
/**
* 减小等待写入数据大小
*/
private void decrementPendingOutboundBytes() {
if (ESTIMATE_TASK_SIZE_ON_SUBMIT) {
ctx.pipeline.decrementPendingOutboundBytes(size & Integer.MAX_VALUE);
}
}
private void recycle() {
// Set to null so the GC can collect them directly
ctx = null;
msg = null;
promise = null;
handle.recycle(this);
}
}
这个类的实现很简单
- 使用静态方法从对象池中获取
WriteTask
实例(这样会复用WriteTask
)- 每次调用初始化
init
方法,如果配置项ESTIMATE_TASK_SIZE_ON_SUBMIT
为true
,都会增加等待写入数据的大小。- 真正运行(
run
被调用),或者取消的时候,都会减少等待写入数据的大小。
在 DefaultChannelPipeline
中
@UnstableApi
protected void incrementPendingOutboundBytes(long size) {
ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
if (buffer != null) {
buffer.incrementPendingOutboundBytes(size);
}
}
@UnstableApi
protected void decrementPendingOutboundBytes(long size) {
ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
if (buffer != null) {
buffer.decrementPendingOutboundBytes(size);
}
}
都是调用
ChannelOutboundBuffer
类的对应方法。
在 ChannelOutboundBuffer
中
void incrementPendingOutboundBytes(long size) {
incrementPendingOutboundBytes(size, true);
}
private void incrementPendingOutboundBytes(long size, boolean invokeLater) {
if (size == 0) {
return;
}
long newWriteBufferSize = TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, size);
if (newWriteBufferSize > channel.config().getWriteBufferHighWaterMark()) {
setUnwritable(invokeLater);
}
}
private void setUnwritable(boolean invokeLater) {
for (;;) {
final int oldValue = unwritable;
final int newValue = oldValue | 1;
if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) {
if (oldValue == 0) {
fireChannelWritabilityChanged(invokeLater);
}
break;
}
}
}
void decrementPendingOutboundBytes(long size) {
decrementPendingOutboundBytes(size, true, true);
}
private void decrementPendingOutboundBytes(long size, boolean invokeLater, boolean notifyWritability) {
if (size == 0) {
return;
}
long newWriteBufferSize = TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, -size);
if (notifyWritability && newWriteBufferSize < channel.config().getWriteBufferLowWaterMark()) {
setWritable(invokeLater);
}
}
private void setWritable(boolean invokeLater) {
for (;;) {
final int oldValue = unwritable;
final int newValue = oldValue & ~1;
if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) {
if (oldValue != 0 && newValue == 0) {
fireChannelWritabilityChanged(invokeLater);
}
break;
}
}
}
在
ChannelOutboundBuffer
中有一个totalPendingSize
变量表示写缓冲区等待数据大小。
- 当它的值大于
channel.config().getWriteBufferHighWaterMark()
时,表示不能写了,通过setUnwritable(invokeLater)
发送当前通道可写状态改变的 入站IO
事件。- 当它的值小于
channel.config().getWriteBufferLowWaterMark()
时,表示又可以写了,通过setWritable(invokeLater)
发送当前通道可写状态改变的 入站IO
事件。
四. 总结
ChannelHandlerContext
的主要实现原理已经介绍完毕了,你也明白了,它是如何向上游或下游传递事件了。