在原生的java Nio SocketChannel只有一种write方法,将数据写到对端,
关于Netty NioSocketChannel 写入对端数据的过程,和写入相关的,在Netty Channel中有三种api方法:
ChannelFuture write(Object msg)
ChannelFuture write(Object msg, ChannelPromise promise);
ChannelOutboundInvoker flush();
ChannelFuture writeAndFlush(Object msg);
ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);
- write方法:将数据写到内存队列中,此时数据并没有写入到对端。
- flush方法:刷新内存队列,将其中的数据写入到对端。此时数据真正写入到对端。
- writeAndFlush方法:将数据写入到内存队列后,立马刷新内存队列,又将其中的数据写入到对端。此时数据已经写入对端。
然而,在Netty Channel中的write(…)和writeAndFlush(…)方法是异步写入的,需要通过监听返回的ChannelFuture来确定是真正的写入。
// 异步监听:
channel.write(msg).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
// ... 相关逻辑,例如是否成功?
}
});
// 同步异步写入结果:
channel.write(msg).sync();
这里write()方法返回的Promise对象,只有在数据真正被flush()方法调用方法执行后,才会被回调通知。
在AbstractChannel中对write()方法的实现:
@Override
public ChannelFuture write(Object msg) {
return pipeline.write(msg);
}
@Override
public ChannelFuture write(Object msg, ChannelPromise promise) {
return pipeline.write(msg, promise);
}
方法内部,调用对应ChannelPipeline中write()方法将write事件在pipeline上传播:
DefaultChannelPipeline中write()方法:
@Override
public final ChannelFuture write(Object msg) {
return tail.write(msg);
}
@Override
public final ChannelFuture write(Object msg, ChannelPromise promise) {
return tail.write(msg, promise);
}
在方法的内部会调用TailContext中write()方法,将write事件在pipeline中,从尾结点向头结点传播:
TailContext 对 write()方法的实现,是从AbstractChannelHandlerContext抽象类继承:
@Override
public ChannelFuture write(Object msg) {
return write(msg, newPromise());
//调用write(Object msg)方法,会调用write(Object msg, ChannelPromise promise)方法。(缺少的promise方法参数,通过调用newPromise()方法创建Promise对象,如下:)(放下面比较方便,以后都这样!)
/*
@Override
public ChannelPromise newPromise() {
return new DefaultChannelPromise(channel(), executor());
// 返回DefaultChannelPromise对象
}
*/
}
@Override
public ChannelFuture write(final Object msg, final ChannelPromise promise) {
// 消息( 数据 )为空,抛出异常
if (msg == null) {
throw new NullPointerException("msg");
}
try {
// 判断是否为合法的 Promise 对象
if (isNotValidPromise(promise, true)) {
// 释放消息( 数据 )相关的资源
ReferenceCountUtil.release(msg);
// cancelled
return promise;
}
} catch (RuntimeException e) {
// 发生异常,释放消息( 数据 )相关的资源
ReferenceCountUtil.release(msg);
throw e;
}
// 写入消息( 数据 )到内存队列
write(msg, false, promise);
return promise;
//返回的结果即为上面传入的promise对象
}
AbstractChannelHandlerContext抽象类继承的最后调用write(Object msg, boolean flush, ChannelPromise promise)方法,写入消息(数据)到内存队列:
private void write(Object msg, boolean flush, ChannelPromise promise) {
// 获得下一个 Outbound 节点
AbstractChannelHandlerContext next = findContextOutbound();
// 记录 Record 记录
final Object m = pipeline.touch(msg, next);
EventExecutor executor = next.executor();
// 在 EventLoop 的线程中
if (executor.inEventLoop()) {
// 执行 writeAndFlush 事件到下一个节点
if (flush) {
next.invokeWriteAndFlush(m, promise);
// 执行 write 事件到下一个节点
} else {
next.invokeWrite(m, promise);
}
} else {
AbstractWriteTask task;
// 创建 writeAndFlush 任务
if (flush) {
task = WriteAndFlushTask.newInstance(next, m, promise);
// 创建 write 任务
} else {
task = WriteTask.newInstance(next, m, promise);
}
// 提交到 EventLoop 的线程中,执行该任务
safeExecute(executor, task, promise, m);
}
}
由上面这段知:当flush为true时,执行的操作为write及flush(将数据写入到内存队列后,刷新内存队列,将其中的数据写入到对端)。
上面代码调用touch(Object msg, AbstractChannelHandlerContext next) 方法来记录 Record 记录:
// DefaultChannelPipeline.java
final Object touch(Object msg, AbstractChannelHandlerContext next) {
return touch ? ReferenceCountUtil.touch(msg, next) : msg;
}
// ReferenceCountUtil.java
/**
* Tries to call {@link ReferenceCounted#touch(Object)} if the specified message implements
* {@link ReferenceCounted}. If the specified message doesn't implement {@link ReferenceCounted},
* this method does nothing.
*/
@SuppressWarnings("unchecked")
public static <T> T touch(T msg, Object hint) {
if (msg instanceof ReferenceCounted) {
return (T) ((ReferenceCounted) msg).touch(hint);
}
return msg;
}
至此,再回到AbstractChannel对write()方法的实现,最终在pipeline中,write事件到HeadContext节点,将数据写入到内存队列:
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
unsafe.write(msg, promise);
}
这里调用了AbstractUnsafe中write(Object msg, ChannelPromise promise) 方法,将数据写到内存队列中:
/**
* 内存队列
*/
private volatile ChannelOutboundBuffer outboundBuffer = new ChannelOutboundBuffer(AbstractChannel.this);
//outboundBuffer属性,内存队列,用于缓存写入的数据。
@Override
public final void write(Object msg, ChannelPromise promise) {
assertEventLoop();
ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
// 内存队列为空,
if (outboundBuffer == null) {
// 内存队列为空,一般是 Channel 已经关闭,所以通知 Promise 异常结果
// If the outboundBuffer is null we know the channel was closed and so
// need to fail the future right away. If it is not null the handling of the rest
// will be done in flush0()
// See https://github.com/netty/netty/issues/2362
safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
// 释放消息( 对象 )相关的资源
// release message now to prevent resource-leak
ReferenceCountUtil.release(msg);
return;
}
int size;
try {
// 过滤写入的消息( 数据 )
msg = filterOutboundMessage(msg);
// 计算消息的长度
size = pipeline.estimatorHandle().size(msg);
if (size < 0) {
size = 0;
}
} catch (Throwable t) {
// 通知 Promise 异常结果
safeSetFailure(promise, t);
// 释放消息( 对象 )相关的资源
ReferenceCountUtil.release(msg);
return;
}
// 写入消息( 数据 )到内存队列
outboundBuffer.addMessage(msg, size, promise);
}
上面关于filterOutboundMessage(msg) 方法,过滤写入的消息( 数据 ):
// AbstractNioByteChannel.java
@Override
protected final Object filterOutboundMessage(Object msg) {
// 这里是当消息(数据)是 ByteBuf 类型的情况,如果不是该类型对象,则需调用newDirectBuffer(ByteBuf)方法,复制封装成Direct ByteBuf对象。
//原因是:在使用 Socket 传递数据时性能很好,由于数据直接在内存中,不存在从 JVM 拷贝数据到直接缓冲区的过程,性能好
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
// 已经是内存 ByteBuf
if (buf.isDirect()) {
return msg;
}
// 非内存 ByteBuf ,需要进行创建封装
return newDirectBuffer(buf);
}
// FileRegion 的情况
if (msg instanceof FileRegion) {
return msg;
}
// 不支持其他类型
throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg) + EXPECTED_TYPES);
}
接下来看AbstractWriteTask,实现Runnable接口,写入任务抽象类。它有两个子类实现:
- WriteTask ,write 任务实现类
- WriteAndFlushTask ,write + flush 任务实现类。
它们都是 AbstractChannelHandlerContext 的内部静态类。
关于AbstractWriteTask的构造方法:
/**
* 提交任务时,是否计算 AbstractWriteTask 对象的自身占用内存大小
*/
private static final boolean ESTIMATE_TASK_SIZE_ON_SUBMIT = SystemPropertyUtil.getBoolean("io.netty.transport.estimateSizeOnSubmit", true);
/**
* 每个 AbstractWriteTask 对象自身占用内存的大小。
*/
// Assuming a 64-bit JVM, 16 bytes object header, 3 reference fields and one int field, plus alignment
private static final int WRITE_TASK_OVERHEAD = SystemPropertyUtil.getInt("io.netty.transport.writeTaskSizeOverhead", 48);
/*
WRITE_TASK_OVERHEAD 静态字段,每个 AbstractWriteTask 对象自身占用内存的大小。为什么占用的 48 字节呢?
- 16 bytes object header ,对象头,16 字节。
- 3 reference fields ,3 个对象引用字段,3 * 8 = 24 字节。
- 1 int fields ,1 个 int 字段,4 字节。
padding ,补齐 8 字节的整数倍,因此 4 字节。
因此,合计 48 字节( 64 位的 JVM 虚拟机,并且不考虑压缩 )。
*/
private final Recycler.Handle<AbstractWriteTask> handle;
/**
* pipeline 中的节点
*/
private AbstractChannelHandlerContext ctx;
/**
* 消息( 数据 )
*/
private Object msg;
/**
* Promise 对象
*/
private ChannelPromise promise;
/**
* 对象大小
*/
private int size;
@SuppressWarnings("unchecked")
private AbstractWriteTask(Recycler.Handle<? extends AbstractWriteTask> handle) {
this.handle = (Recycler.Handle<AbstractWriteTask>) handle;
}
init(AbstractWriteTask task, AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) 方法,初始化 AbstractWriteTask 对象:
protected static void init(AbstractWriteTask task, AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
task.ctx = ctx;
task.msg = msg;
task.promise = promise;
// 计算 AbstractWriteTask 对象大小
if (ESTIMATE_TASK_SIZE_ON_SUBMIT) {
task.size = ctx.pipeline.estimatorHandle().size(msg) + WRITE_TASK_OVERHEAD;
// 增加 ChannelOutboundBuffer 的 totalPendingSize 属性
ctx.pipeline.incrementPendingOutboundBytes(task.size);
} else {
task.size = 0;
}
}
incrementPendingOutboundBytes(long size) 方法,增加 ChannelOutboundBuffer 的 totalPendingSize 属性:
// DefaultChannelPipeline.java
@UnstableApi
protected void incrementPendingOutboundBytes(long size) {
ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
if (buffer != null) {
buffer.incrementPendingOutboundBytes(size);
}
}
上面incrementPendingOutboundBytes(long size, …) 方法,增加 totalPendingSize 计数:
/**
* Increment the pending bytes which will be written at some point.
* This method is thread-safe!
*/
void incrementPendingOutboundBytes(long size) {
incrementPendingOutboundBytes(size, true);
}
private void incrementPendingOutboundBytes(long size, boolean invokeLater) {
if (size == 0) {
return;
}
// 增加 totalPendingSize 计数
long newWriteBufferSize = TOTAL_PENDING_SIZE_UPDATER.addAndGet(this, size);
// totalPendingSize 大于高水位阀值时,设置为不可写
if (newWriteBufferSize > channel.config().getWriteBufferHighWaterMark()) {
setUnwritable(invokeLater);
}
}
关于第 16 至 19 行:totalPendingSize 大于高水位阀值时,调用 #setUnwritable(boolean invokeLater) 方法,设置为不可写:
private void setUnwritable(boolean invokeLater) {
for (;;) {
final int oldValue = unwritable;
// 或位操作,修改第 0 位 bits 为 1
final int newValue = oldValue | 1;
// CAS 设置 unwritable 为新值
if (UNWRITABLE_UPDATER.compareAndSet(this, oldValue, newValue)) {
// 若之前可写,现在不可写,触发 Channel WritabilityChanged 事件到 pipeline 中。
if (oldValue == 0 && newValue != 0) {
fireChannelWritabilityChanged(invokeLater);
}
break;
}
}
}
这里写了一些flush相关源码,具体等下次更新。
再回到AbstractWriteTask中,run()实现方法:
@Override
public final void run() {
try {
// 减少 ChannelOutboundBuffer 的 totalPendingSize 属性
// Check for null as it may be set to null if the channel is closed already
if (ESTIMATE_TASK_SIZE_ON_SUBMIT) {
ctx.pipeline.decrementPendingOutboundBytes(size);
}
// 执行 write 事件到下一个节点
write(ctx, msg, promise);
} finally {
// 置空AbstractWriteTask 对象,help gc
// Set to null so the GC can collect them directly
ctx = null;
msg = null;
promise = null;
// 回收对象
handle.recycle(this);
}
}
上面调用 调用decrementPendingOutboundBytes(long size) 方法,减少 ChannelOutboundBuffer 的 totalPendingSize 属性。
@UnstableApi
protected void decrementPendingOutboundBytes(long size) {
ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
if (buffer != null) {
buffer.decrementPendingOutboundBytes(size);
}
}
回到run()方法实现,调用 write(ctx, msg, promise) 方法,执行 write 事件到下一个节点:
protected void write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
ctx.invokeWrite(msg, promise);
}
WriteTask: 实现了 SingleThreadEventLoop.NonWakeupRunnable 接口,继承 AbstractWriteTask 抽象类,write 任务实现类。
由于实现了SingleThreadEventLoop.NonWakeupRunnable 接口,write 操作,仅仅将数据写到内存队列中,无需唤醒 EventLoop ,从而提升性能,
WriteTask 无需实现 write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) 方法,直接重用父类该方法即可。
newInstance(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) 方法,创建 WriteTask 对象:
private static final Recycler<WriteTask> RECYCLER = new Recycler<WriteTask>() {
@Override
protected WriteTask newObject(Handle<WriteTask> handle) {
return new WriteTask(handle); // 创建 WriteTask 对象
}
};
private static WriteTask newInstance(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
// 从 Recycler 的对象池中获得 WriteTask 对象
WriteTask task = RECYCLER.get();
// 初始化 WriteTask 对象的属性
init(task, ctx, msg, promise);
return task;
}
构造方法:
private WriteTask(Recycler.Handle<WriteTask> handle) {
super(handle);
}
WriteAndFlushTask ,继承 WriteAndFlushTask 抽象类,write + flush 任务实现类:
newInstance(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) 方法,创建 WriteAndFlushTask 对象:
private static final Recycler<WriteAndFlushTask> RECYCLER = new Recycler<WriteAndFlushTask>() {
@Override
protected WriteAndFlushTask newObject(Handle<WriteAndFlushTask> handle) {
return new WriteAndFlushTask(handle); // 创建 WriteAndFlushTask 对象
}
};
private static WriteAndFlushTask newInstance(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
// 从 Recycler 的对象池中获得 WriteTask 对象
WriteAndFlushTask task = RECYCLER.get();
// 初始化 WriteTask 对象的属性
init(task, ctx, msg, promise);
return task;
}
构造方法:
private WriteAndFlushTask(Recycler.Handle<WriteAndFlushTask> handle) {
super(handle);
}
write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) 方法,在父类的该方法的基础上,增加执行 flush 事件到下一个节点:
@Override
public void write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
// 执行 write 事件到下一个节点
super.write(ctx, msg, promise);
// 执行 flush 事件到下一个节点
ctx.invokeFlush();
}
这里有write相关操作并且涵盖一些flush操作,(两者关系密切,下节flush操作继续)暂时结束。