在第二小节中我们分析到,父通道接收请求之后NioServerSocketChannel#doReadMessage
protected int io.netty.channel.socket.nio.NioServerSocketChannel#doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = javaChannel().accept();
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, childEventLoopGroup().next(), ch));
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
NioSocketChannel类图
在接收完请求后创建了NioSocketChannel通道
public NioSocketChannel(Channel parent, EventLoop eventLoop, SocketChannel socket) {
super(parent, eventLoop, socket);
config = new DefaultSocketChannelConfig(this, socket.socket());
}
|
V
protected AbstractNioByteChannel(Channel parent, EventLoop eventLoop, SelectableChannel ch) {
super(parent, eventLoop, ch, SelectionKey.OP_READ);
}
|
V
protected AbstractNioChannel(Channel parent, EventLoop eventLoop, SelectableChannel ch, int readInterestOp) {
super(parent, eventLoop);
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);
}
}
|
V
protected AbstractChannel(Channel parent, EventLoop eventLoop) {
this.parent = parent;
this.eventLoop = validate(eventLoop);
//NioByteUnsafe实例
unsafe = newUnsafe();
pipeline = new DefaultChannelPipeline(this);
}
基本上和创建NioServerSocketChannel是一样的。
创建完毕后会触发read事件,还记得在前面初始化父通道的时候想父通道相关的排管中添加了一个ServerBootstrapAcceptor
private static class ServerBootstrapAcceptor extends ChannelHandlerAdapter {
private final ChannelHandler childHandler;
private final Entry<ChannelOption<?>, Object>[] childOptions;
private final Entry<AttributeKey<?>, Object>[] childAttrs;
。。。。。。
}
上面三个成员变量是在构建ServerBootstrap时设置的,在初始化父通道的时候会设置到这个ServerBootstrapAcceptor中,用于定制子通道的属性,操作参数和子通道处理器
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Channel child = (Channel) msg;
//向子通道的排管中注册处理器
child.pipeline().addLast(childHandler);
//设置操作参数
for (Entry<ChannelOption<?>, Object> e: childOptions) {
try {
if (!child.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {
logger.warn("Unknown channel option: " + e);
}
} catch (Throwable t) {
logger.warn("Failed to set a channel option: " + child, t);
}
}
//设置属性
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
//注册通道到selector中
child.unsafe().register(child.newPromise());
}
其实子通道的注册和父通道的注册是一样的,只是触发激活事件的条件变成了通道是否被打开,通道是否已经连接
在触发激活前还会触发通道的注册事件,其中netty给子通道注册了我们自定义的通道处理器,这个通道处理器继承了ChannelInitializer,这个类对通道的注册感兴趣
public final void io.netty.channel.ChannelInitializer#channelRegistered(ChannelHandlerContext ctx) throws Exception {
//获取排管
ChannelPipeline pipeline = ctx.pipeline();
boolean success = false;
try {
//初始化通道
initChannel((C) ctx.channel());
//初始化完毕后移除
pipeline.remove(this);
//调用下一个的处理器的注册方法
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();
}
}
}
NioSocketChannel和NioServerSocketChannel一样使用的执行器都是NioEventLoop,所以在轮询上是一样的,我们先来看下connect事件
public void io.netty.channel.nio.AbstractNioChannel.AbstractNioUnsafe#finishConnect() {
// Note this method is invoked by the event loop only if the connection attempt was
// neither cancelled nor timed out.
assert eventLoop().inEventLoop();
assert connectPromise != null;
try {
//获取老的状态
boolean wasActive = isActive();
//(*1*)
doFinishConnect();
//(*2*)
fulfillConnectPromise(connectPromise, wasActive);
} catch (Throwable t) {
if (t instanceof ConnectException) {
Throwable newT = new ConnectException(t.getMessage() + ": " + requestedRemoteAddress);
newT.setStackTrace(t.getStackTrace());
t = newT;
}
// Use tryFailure() instead of setFailure() to avoid the race against cancel().
//设置失败
connectPromise.tryFailure(t);
closeIfClosed();
} finally {
// Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used
// See https://github.com/netty/netty/issues/1770
if (connectTimeoutFuture != null) {
connectTimeoutFuture.cancel(false);
}
connectPromise = null;
}
}
//(*1*)
protected void io.netty.channel.nio.AbstractNioChannel.doFinishConnect() throws Exception {
if (!javaChannel().finishConnect()) {
throw new Error();
}
}
//(*2*)
private void io.netty.channel.nio.AbstractNioChannel.fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
// trySuccess() will return false if a user cancelled the connection attempt.
//设置成功
boolean promiseSet = promise.trySuccess();
// Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
// because what happened is what happened.
if (!wasActive && isActive()) {
//激活后触发通道激活事件
pipeline().fireChannelActive();
}
// If a user cancelled the connection attempt, close the channel, which is followed by channelInactive().
if (!promiseSet) {
close(voidPromise());
}
}
NioByteUnsafe#read
public void io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe#read() {
//通道配置
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
//ByteBuf分配器
final ByteBufAllocator allocator = config.getAllocator();
//获取每次读取的最大消息
final int maxMessagesPerRead = config.getMaxMessagesPerRead();
//分配处理器
RecvByteBufAllocator.Handle allocHandle = this.allocHandle;
if (allocHandle == null) {
//赋值给成员变量,其实例为io.netty.channel.AdaptiveRecvByteBufAllocator.HandleImpl
//用于分配ByteBuf,对分配的大小进行猜测
this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle();
}
if (!config.isAutoRead()) {
//移除读事件
removeReadOp();
}
ByteBuf byteBuf = null;
int messages = 0;
boolean close = false;
try {
//猜测缓存大小
int byteBufCapacity = allocHandle.guess();
int totalReadAmount = 0;
//循环读取数据
do {
//分配一个byteBufCapacity大小的byteBuf
byteBuf = allocator.ioBuffer(byteBufCapacity);
//获取可写字节数
int writable = byteBuf.writableBytes();
//从通道中读取字节
//(*1*)
int localReadAmount = doReadBytes(byteBuf);
//如果小于等于零,那么释放byteBuf,并标记将关闭通道
if (localReadAmount <= 0) {
// not was read release the buffer
byteBuf.release();
close = localReadAmount < 0;
break;
}
//触发读事件,并把读取到的部分数据传到处理器中
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
if (totalReadAmount >= Integer.MAX_VALUE - localReadAmount) {
// Avoid overflow.
//避免溢出
totalReadAmount = Integer.MAX_VALUE;
break;
}
totalReadAmount += localReadAmount;
//如果读到的字节数小于可以装到byteBuf中的字节数,那么说明本次最多就只能读取这么多数据,期待下次读取
//如果等于可写的,那么可能还有剩余的
if (localReadAmount < writable) {
// Read less than what the buffer can hold,
// which might mean we drained the recv buffer completely.
break;
}
//读取计数,每次最大可读maxMessagesPerRead次,到达了最大限制,即使还有未读完的数据也会等到下次再读取
} while (++ messages < maxMessagesPerRead);
//触发读取完成事件
pipeline.fireChannelReadComplete();
//通过本次读取的字节数,推断下次将要初始化byteBuf的大小
allocHandle.record(totalReadAmount);
if (close) {
//取消读取事件
closeOnRead(pipeline);
close = false;
}
} catch (Throwable t) {
//触发读取事件和异常事件
handleReadException(pipeline, byteBuf, t, close);
}
}
}
//(*1*)
protected int io.netty.channel.socket.nio.NioSocketChannel#doReadBytes(ByteBuf byteBuf) throws Exception {
//(*2*)
return byteBuf.writeBytes(javaChannel(), byteBuf.writableBytes());
}
//(*2*)
public int io.netty.buffer.AbstractByteBuf#writeBytes(ScatteringByteChannel in, int length) throws IOException {
ensureWritable(length);
//(*3*)
int writtenBytes = setBytes(writerIndex, in, length);
if (writtenBytes > 0) {
writerIndex += writtenBytes;
}
return writtenBytes;
}
//(*3*)
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
ensureAccessible();
try {
return in.read((ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length));
} catch (ClosedChannelException e) {
return -1;
}
}
在前面的例子中我们给子渠道设置了一个自定义的通道处理器
private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));//加入换行符解码器
arg0.pipeline().addLast(new StringDecoder());//加入字符串解码器,将会把msg直接变成String类型的信息
arg0.pipeline().addLast(new TimeServerHandler());
}
}
我们首先了分析下这个LineBasedFrameDecoder,这个处理器是把接收到的信息通多换行符分割,它继承了ByteToMessageDecoder,其中这个ByteToMessageDecoder实现了read方法
public void io.netty.handler.codec.ByteToMessageDecoder#channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
//创建一个可回收的ArrayList,与ArrayList的区别就是可以调用它的recycle方法,它会清空内容,但是这个ArrayList会压入栈,然后存储到
//本地线程中,下次可以直接复用这个实例
RecyclableArrayList out = RecyclableArrayList.newInstance();
try {
ByteBuf data = (ByteBuf) msg;
first = cumulation == null;
if (first) {
//如果刚进来的,那么直接将netty读取到的ByteBuf赋值给这个成员变量
cumulation = data;
} else {
//如果不是第一次读取了,那么可能存在上次读取过后的内容,判断这个ByteBuf的容量是否充足,如果不够,将会进行扩充
if (cumulation.writerIndex() > cumulation.maxCapacity() - data.readableBytes()) {
expandCumulation(ctx, data.readableBytes());
}
//将新的内容添加到旧数据后面
cumulation.writeBytes(data);
//释放调用新的,帮助gc
data.release();
}
//具体的分割操作
//(*1*)
callDecode(ctx, cumulation, out);
} catch (DecoderException e) {
throw e;
} catch (Throwable t) {
throw new DecoderException(t);
} finally {
//如果所有的数据都完美的分割,那么就可以释放了,但是话说回来,会不会出现数据丢失的情况
//或者没有设置换行符,那么cumulation将会添加到很多内容,最后超过限制报错
if (cumulation != null && !cumulation.isReadable()) {
cumulation.release();
cumulation = null;
}
int size = out.size();
decodeWasNull = size == 0;
//循环每个被行分割的数据,触发下一个处理器
for (int i = 0; i < size; i ++) {
ctx.fireChannelRead(out.get(i));
}
//回收这个ArrayList对象
out.recycle();
}
} else {
//其他类型的,直接跳过,调用下一个感兴趣的处理器
ctx.fireChannelRead(msg);
}
}
//(*1*)
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
try {
//还存在可读的内容
while (in.isReadable()) {
int outSize = out.size();
//可读字节长度
int oldInputLength = in.readableBytes();
//具体的分割方法
//(*2*)
decode(ctx, in, out);
// Check if this handler was removed before continuing the loop.
// If it was removed, it is not safe to continue to operate on the buffer.
//
// See https://github.com/netty/netty/issues/1664
//如果当前处理器被移除,直接返回,不再处理
if (ctx.isRemoved()) {
break;
}
//如果没有找到分割符,并且可读的和原来一样,那么就没必要继续了
if (outSize == out.size()) {
if (oldInputLength == in.readableBytes()) {
break;
} else {
//继续寻找
continue;
}
}
//哈哈,报错
if (oldInputLength == in.readableBytes()) {
throw new DecoderException(
StringUtil.simpleClassName(getClass()) +
".decode() did not read anything but decoded a message.");
}
//只读取一次
if (isSingleDecode()) {
break;
}
}
} catch (DecoderException e) {
throw e;
} catch (Throwable cause) {
throw new DecoderException(cause);
}
}
//(*2*)
protected final void io.netty.handler.codec.LineBasedFrameDecoder#decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
//(*3*)
Object decoded = decode(ctx, in);
if (decoded != null) {
out.add(decoded);
}
}
//(*3*)
protected Object LineBasedFrameDecoder.decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
//(*4*)
//寻找换行符下标
final int eol = findEndOfLine(buffer);
//是否需要丢弃一些数据
if (!discarding) {
if (eol >= 0) {
final ByteBuf frame;
//需要读取的字节数
final int length = eol - buffer.readerIndex();
final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
//如果读取的字节数超过了最大长度
if (length > maxLength) {
//跳过,将读取下标设置到找到的换行符后面
buffer.readerIndex(eol + delimLength);
//触发异常处理
fail(ctx, length);
return null;
}
//是否去掉换行符
if (stripDelimiter) {
//读取
frame = buffer.readBytes(length);
//跳过换行符
buffer.skipBytes(delimLength);
} else {
//包括换行符一起获取
frame = buffer.readBytes(length + delimLength);
}
return frame;
} else {
//木有找到
final int length = buffer.readableBytes();
//如果超过了限制
if (length > maxLength) {
//下次将会丢弃这么多字节的数据
discardedBytes = length;
//直接将读取下标移动到写下标
buffer.readerIndex(buffer.writerIndex());
//丢弃
discarding = true;
//快速失败,触发异常处理
if (failFast) {
fail(ctx, "over " + discardedBytes);
}
}
return null;
}
} else {
if (eol >= 0) {
//丢去掉上次超过最大长度的数据到当前找打的换行符的位置的数据
final int length = discardedBytes + eol - buffer.readerIndex();
final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
buffer.readerIndex(eol + delimLength);
discardedBytes = 0;
discarding = false;
if (!failFast) {
fail(ctx, length);
}
} else {
//如果没有找到换行符,把可读数据设置为下一轮就要抛弃的数据长度
discardedBytes = buffer.readableBytes();
//读下标移动到写下标中
buffer.readerIndex(buffer.writerIndex());
}
return null;
}
}
StringDecoder
这个就很简单了,就是把前面的传递过了的byteBuf变成了字符串
public void io.netty.handler.codec.MessageToMessageDecoder#channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
RecyclableArrayList out = RecyclableArrayList.newInstance();
try {
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I cast = (I) msg;
try {
//(*1*)
decode(ctx, cast, out);
} finally {
ReferenceCountUtil.release(cast);
}
} else {
out.add(msg);
}
} catch (DecoderException e) {
throw e;
} catch (Exception e) {
throw new DecoderException(e);
} finally {
int size = out.size();
for (int i = 0; i < size; i ++) {
ctx.fireChannelRead(out.get(i));
}
out.recycle();
}
}
//(*1*)
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
out.add(msg.toString(charset));
}
下面来看下例子中优雅退出
//优雅退出,释放线程资源
bossGroup.shutdownGracefully();
public Future<?> io.netty.util.concurrent.AbstractEventExecutorGroup#shutdownGracefully() {
return shutdownGracefully(DEFAULT_SHUTDOWN_QUIET_PERIOD, DEFAULT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
}
public Future<?> io.netty.util.concurrent.MultithreadEventExecutorGroup#shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
for (EventExecutor l: children) {
l.shutdownGracefully(quietPeriod, timeout, unit);
}
//返回terminationFuture
return terminationFuture();
}
可以看到netty循环调用了组内的每个执行器的优雅关闭方法
public Future<?> io.netty.util.concurrent.SingleThreadEventExecutor#shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
//退出周期
if (quietPeriod < 0) {
throw new IllegalArgumentException("quietPeriod: " + quietPeriod + " (expected >= 0)");
}
//退出超时
if (timeout < quietPeriod) {
throw new IllegalArgumentException(
"timeout: " + timeout + " (expected >= quietPeriod (" + quietPeriod + "))");
}
if (unit == null) {
throw new NullPointerException("unit");
}
//如果已经关闭,直接返回即可
if (isShuttingDown()) {
return terminationFuture();
}
boolean inEventLoop = inEventLoop();
boolean wakeup = true;
synchronized (stateLock) {
//如果已经关闭,直接返回,双重锁定检查
if (isShuttingDown()) {
return terminationFuture();
}
gracefulShutdownQuietPeriod = unit.toNanos(quietPeriod);
gracefulShutdownTimeout = unit.toNanos(timeout);
//如果当前线程就是执行器的向后才能,那么直接设置状态即可
if (inEventLoop) {
assert state == ST_STARTED;
state = ST_SHUTTING_DOWN;
} else {
//其他的情况
switch (state) {
case ST_NOT_STARTED:
state = ST_SHUTTING_DOWN;
//没有启动的直接设置关闭状态,然后启动一下线程,线程检查状态为关闭,那么就会清理资源,然后关闭
doStartThread();
break;
case ST_STARTED:
state = ST_SHUTTING_DOWN;
break;
default:
wakeup = false;
}
}
}
//唤醒在NioEventLoop的run方法中执行的线程
if (wakeup) {
wakeup(inEventLoop);
}
//返回关闭结果
return terminationFuture();
}
我们直接看到NioEventLoop的run方法中有下面一段代码
//检查状态是否为关闭
if (isShuttingDown()) {
//(*1*)
closeAll();
//确认关闭
//(*3*)
if (confirmShutdown()) {
break;
}
}
//(*1*)
private void io.netty.channel.nio.NioEventLoop#closeAll() {
//在关闭前selectNow
selectAgain();
Set<SelectionKey> keys = selector.keys();
Collection<AbstractNioChannel> channels = new ArrayList<AbstractNioChannel>(keys.size());
for (SelectionKey k: keys) {
Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
channels.add((AbstractNioChannel) a);
} else {
//取消
k.cancel();
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
invokeChannelUnregistered(task, k, null);
}
}
for (AbstractNioChannel ch: channels) {
//关闭每个NioChannel
//(*2*)
ch.unsafe().close(ch.unsafe().voidPromise());
}
}
//(*2*)
public final void io.netty.channel.AbstractChannel.AbstractUnsafe#close(final ChannelPromise promise) {
if (inFlush0) {
invokeLater(new Runnable() {
@Override
public void run() {
close(promise);
}
});
return;
}
//如果已经关闭
if (closeFuture.isDone()) {
// Closed already.
promise.setSuccess();
return;
}
boolean wasActive = isActive();
ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
this.outboundBuffer = null; // Disallow adding any messages and flushes to outboundBuffer.
try {
//直接调用jdk的通道关闭方法
doClose();
closeFuture.setClosed();
//关闭成功
promise.setSuccess();
} catch (Throwable t) {
closeFuture.setClosed();
promise.setFailure(t);
}
// Fail all the queued messages
try {
//设置通道关闭异常
outboundBuffer.failFlushed(CLOSED_CHANNEL_EXCEPTION);
outboundBuffer.close(CLOSED_CHANNEL_EXCEPTION);
} finally {
if (wasActive && !isActive()) {
//取法通道未激活事件
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelInactive();
}
});
}
//取消注册
deregister();
}
}
//(*3*)
protected boolean confirmShutdown() {
//未关闭,返回false
if (!isShuttingDown()) {
return false;
}
//不允许外部线程调用
if (!inEventLoop()) {
throw new IllegalStateException("must be invoked from an event loop");
}
//取消延时任务的执行,然后清空延时队列
//取消就是给延时任务设置取消标志
cancelDelayedTasks();
if (gracefulShutdownStartTime == 0) {
//设置优雅关闭时间
gracefulShutdownStartTime = ScheduledFutureTask.nanoTime();
}
//是否正在运行任务或者关闭钩子
if (runAllTasks() || runShutdownHooks()) {
//如果此时已经关闭,返回true
if (isShutdown()) {
// Executor shut down - no new tasks anymore.
return true;
}
// There were tasks in the queue. Wait a little bit more until no tasks are queued for the quiet period.
//唤醒线程,继续执行
wakeup(true);
return false;
}
final long nanoTime = ScheduledFutureTask.nanoTime();
//已经关闭,或者已经关闭超时,返回true,强行退出,不管是否还存在的任务
if (isShutdown() || nanoTime - gracefulShutdownStartTime > gracefulShutdownTimeout) {
return true;
}
//如果执行周期还没有到达上限,继续
if (nanoTime - lastExecutionTime <= gracefulShutdownQuietPeriod) {
// Check if any tasks were added to the queue every 100ms.
// TODO: Change the behavior of takeTask() so that it returns on timeout.
wakeup(true);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Ignore
}
return false;
}
//其他情况返回true,即使还任务也会被抛弃
// No tasks were added for last quiet period - hopefully safe to shut down.
// (Hopefully because we really cannot make a guarantee that there will be no execute() calls by a user.)
return true;
}
我们前面看到通道的注册都是0,但是却没有看到注册读取或者接收事件
public ChannelPipeline io.netty.channel.DefaultChannelPipeline#fireChannelActive() {
//触发处理器链激活事件
head.fireChannelActive();
//如果是自动读取配置,那么调用通道的read,准备绑定感兴趣的事件
if (channel.config().isAutoRead()) {
//(*1*)
channel.read();
}
return this;
}
|
V
public Channel read() {
pipeline.read();
return this;
}
:
:
V
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
:
:
V
protected void io.netty.channel.nio.AbstractNioChannel#doBeginRead() throws Exception {
if (inputShutdown) {
return;
}
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
好了,netty的整个流程代码已经结束,有人会说了,客户端Bootstrap都没有分析,其实他们的操作方式一样的,只是一个是绑定端口,一个是链接,最终启动的NioEventLoop是一样的,所以没必要重复赘述了。
可以看出其实ServerBootStrap和Bootstrap其实就是起到辅助启动的作用,他们俩的启动方式稍许不同罢了,一个是绑定,另一个是链接,最后具体工作都交由NioEventLoop处理了。
在分析源码的过程中,出现了ByteBuf这个类,这个类是netty为了简化操作实现的类,它比JDK原生的ByteBuffer好用多了,并且提供了池化的ByteBuf,对于池化的内存申请内容比较复杂,它的内存块是通过二叉树来维护的,更具体的源代码分析可以参考博文https://my.oschina.net/zhangxufeng/blog/3030653
这位作者写的netty池化技术的文章我觉得是我看过的文章中写的最好的,他对netty池化技术的理解可谓是功力深厚,大家可以去膜拜一下。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~
2016-09-29 hadoop
2016-09-29 solr集群