[netty4][netty-transport]netty之nio传输层

nio基本处理逻辑

查看这里

Selector的处理

Selector实例构建

NioEventLoop.openSelector()方法先用JDK API构建出来的Selector再用反射将其中的selectedKeys、publicSelectedKeys替换成他优化过的SelectedSelectionKeySet实例。

JDK API构建出来的Selector 代码:

// NioEventLoop
private final SelectorProvider provider; // SelectorProvider.provider() 直接用JDK提供的prodvider prodiver一下,在mac上返回值是sun.nio.ch.KQueueSelectorProvider@1b26f7b2 对应的selector实现是sun.nio.ch.KQueueSelectorImpl@23fe1d71
unwrappedSelector = provider.openSelector();
return new SelectorTuple(unwrappedSelector,
new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));

unwrappedSelector是指我们通常用JDK的API构建出来的Selector,实际netty使用的是经过他自己优化过的SelectorTuple。下面有分析SelectorTuple

用反射方法替换:

io.netty.channel.nio.NioEventLoop.openSelector() 用法反射的方式将netty声明的SelectedSelectionKeySet实例selectedKeySet (netty的SelectedSelectionKeySet类继承了java.util.AbstractSet)赋值给了selectorImplClass。因为SelectedSelectionKeySet更快,参见下面SelectorTuple分析部分。

selectorImplClass是什么呢?就是各平台的Selector的实现,netty是怎么获取的呢?

直接加载"sun.nio.ch.SelectorImpl"即可。

而且用这种方式替换完之后,每次select之后,只要处理(跌代理里面的key)刚才这个SelectedSelectionKeySet实例selectedKeys(selectedKeySet会赋值给selectedKeys),这个实例也是NioEventLoop的实例变量。简单地说,每次select之后,选中的有事件的key就已经在NioEventLoop的实例变量selectedKeys字段中了。

SelectorTuple分析

SelectorTuple依靠SelectedSelectionKeySetSelectorSelectedSelectionKeySet完成优化,SelectedSelectionKeySet内部采用了数组来替换了JDK实现中的hashset来维护SelectionKey,提升了add reset 与迭代的效率。真是佩服

final class SelectedSelectionKeySet extends AbstractSet<SelectionKey> {

    SelectionKey[] keys;
int size;
// ...... @Override
public boolean add(SelectionKey o) {
if (o == null) {
return false;
} keys[size++] = o;
if (size == keys.length) {
increaseCapacity();
} return true;
} void reset(int start) {
Arrays.fill(keys, start, size, null);
size = 0;
}
}
// ...
final class SelectedSelectionKeySetSelector extends Selector {
private final SelectedSelectionKeySet selectionKeys;
private final Selector delegate; SelectedSelectionKeySetSelector(Selector delegate, SelectedSelectionKeySet selectionKeys) {
this.delegate = delegate;
this.selectionKeys = selectionKeys;
}
} private static final class SelectorTuple {
final Selector unwrappedSelector;
final Selector selector; SelectorTuple(Selector unwrappedSelector) {
this.unwrappedSelector = unwrappedSelector;
this.selector = unwrappedSelector;
} SelectorTuple(Selector unwrappedSelector, Selector selector) {
this.unwrappedSelector = unwrappedSelector;
this.selector = selector;
}
}

ServerSocketChannel创建与初始化

ServerSocketChannel创建过程

bind的时候会创建ServerSocketChannel,并丢给NioServerSocketChannel实例的ch字段

调用栈:

Thread [main] (Suspended (modification of field ch in AbstractNioChannel))
io.netty.channel.socket.nio.NioServerSocketChannel(io.netty.channel.nio.AbstractNioChannel).<init>(io.netty.channel.Channel, java.nio.channels.SelectableChannel, int) line: 85
io.netty.channel.socket.nio.NioServerSocketChannel(io.netty.channel.nio.AbstractNioMessageChannel).<init>(io.netty.channel.Channel, java.nio.channels.SelectableChannel, int) line: 42
io.netty.channel.socket.nio.NioServerSocketChannel.<init>(java.nio.channels.ServerSocketChannel) line: 88
io.netty.channel.socket.nio.NioServerSocketChannel.<init>() line: 74
sun.reflect.NativeConstructorAccessorImpl.newInstance0(java.lang.reflect.Constructor<?>, java.lang.Object[]) line: not available [native method]
sun.reflect.NativeConstructorAccessorImpl.newInstance(java.lang.Object[]) line: 62
sun.reflect.DelegatingConstructorAccessorImpl.newInstance(java.lang.Object[]) line: 45
java.lang.reflect.Constructor<T>.newInstance(java.lang.Object...) line: 423
io.netty.channel.ReflectiveChannelFactory<T>.newChannel() line: 44
io.netty.bootstrap.ServerBootstrap(io.netty.bootstrap.AbstractBootstrap<B,C>).initAndRegister() line: 320
io.netty.bootstrap.ServerBootstrap(io.netty.bootstrap.AbstractBootstrap<B,C>).doBind(java.net.SocketAddress) line: 282
io.netty.bootstrap.ServerBootstrap(io.netty.bootstrap.AbstractBootstrap<B,C>).bind(java.net.SocketAddress) line: 278
org.restexpress.RestExpress.bind(java.net.InetSocketAddress) line: 709
org.restexpress.RestExpress.bind(java.lang.String, int) line: 686
com.code260.ss.resetexpress.RestExpressApp1.main(java.lang.String[]) line: 26

bind的处理

AbstractBootstrap doBind逻辑

NioServerSocketChannel类继承体系:

// AttributeMap体系
251 AttributeMap
--251.2 DefaultAttributeMap
----251.2.1 AbstractChannel
------251.2.1.7 AbstractNioChannel
--------251.2.1.7.1 AbstractNioMessageChannel
----------251.2.1.7.1.5 NioServerSocketChannel // Channel体系
--463.1 Channel
----463.1.2 AbstractChannel // 与pipeline绑定。 构建时就会创建pipeline,newChannelPipeline。
------463.1.2.7 AbstractNioChannel // 支持多种不同实现的SelectableChannel,ServerSocketChannel与SocketChannel都是SelectableChannel的实现。支持构建AbstractNioChannel时指定感兴趣的事件(readInterestOp)并与该Channel实例绑定(会赋值给实例字段readInterestOp),且在该类构造方法中将channel设置为非阻塞。可见,其意在Select的Channel与感兴趣的事件的封装
--------463.1.2.7.1 AbstractNioMessageChannel // 意在定义读写Message(其实就是消息对象,一个Object或者一组Object)接口。并对doWrite做了一些实现。
----------463.1.2.7.1.5 NioServerSocketChannel
  1. initAndRegister并返回ChannelFuture实例,具体逻辑参见下面小节
  2. doBind0 交由boss线程去做真正bind动作

io.netty.bootstrap.AbstractBootstrap.initAndRegister()逻辑

  1. 创建Channel,即ServerSocketChannel实例,并将值包装到netty自己封装的NioServerSocketChannel中。channel = channelFactory.newChannel();
  2. 初始化channel。init(channel);根据io.netty.bootstrap.AbstractBootstrap.options给Channel配置一些参数。server端chanel的参数有{SO_BACKLOG=1024, SO_REUSEADDR=true, CONNECT_TIMEOUT_MILLIS=10000, SO_RCVBUF=262140, RCVBUF_ALLOCATOR=io.netty.channel.AdaptiveRecvByteBufAllocator@17cdf2d0}。并在Channel对应的pipeline上绑定ChannelInitializer
  3. 注册Channel,先 选出一个Executor即NioEventLoop , 选的方法逻辑在io.netty.util.concurrent.DefaultEventExecutorChooserFactory$PowerOfTwoEventExecutorChooser@15cafec7 中 executors[idx.getAndIncrement() & executors.length - 1]。然后用io.netty.channel.SingleThreadEventLoop.register(Channel)注册Channel到Exceutor(即NioEventLoop)中。注册时限看是否是当前EventLoop(对于单线程执行器来说就是看是否是本线程),如果是 直接注册,如果不是则new一个Runnbale出来把注册逻辑包进去 并交给当前eventLoop执行这个runnable。

    注册的细节是: io.netty.channel.AbstractChannel.AbstractUnsafe.register(EventLoop, ChannelPromise)

    执行的细节是: io.netty.util.concurrent.SingleThreadEventExecutor.execute(Runnable)

    内部实现是将task放到io.netty.util.concurrent.SingleThreadEventExecutor.taskQueue这个队列中,这个队列目前实现是io.netty.util.internal.shaded.org.jctools.queues.MpscUnboundedArrayQueue。并在此时判断是否是本线程,如果不实现本线程就把对应的线程给启动了。

    io.netty.channel.AbstractChannel.AbstractUnsafe.register0(ChannelPromise) 这是 register的后处理
  • 先设置ChannelPromise为成功
  • 重要的事情之一: 就是会触发ChannelInboundHandler的channelRegistered事件,这是业务侧可以定制的,执行是在boss线程做的
  • 绑定成功了 然后 触发active事件,是否绑定成功的判断方式是:
 public boolean isActive() {
return javaChannel().socket().isBound();
}

其实就是靠JDK的API isBound完成

register0的提交方式就是靠下面这种代码完成,netty中有好多这种代码,应该包装一下:

if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}

上述逻辑因为是异步的,所以可能执行不完,所以接下来要做出处理:

// regFuture就是上面初始化并注册的返回的Future,此处先判断他是否完成
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise); // 真正的做bind,在boss线程。且此处是regFuture已经完成的情况下 那就不要用回调了就直接调用doBind0就行了。
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() { // 给regFuture注册监听器,等他完成后回调这里,妙啊
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered(); doBind0(regFuture, channel, localAddress, promise); // 真正的做bind,在boss线程
}
}
});
return promise;
}

io.netty.util.concurrent.SingleThreadEventExecutor.startThread() 启动线程的写法值得学习,确保正好启动一次。

addTaskWakesUp??

 if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
  1. 创建PendingRegistrationPromise。 对初始化和注册过的ChannelFuture增加监听器,监听ChannelFutureListener.operationComplete完成后回调。因为上面初始化并注册那事是异步的,可能这时还没完成。

    // 3的wakeup和4要看下

    以上都没涉及到真正bind端口的地方

真正bind

调用栈:

// Thread [boss-0] (Suspended)
io.netty.channel.socket.nio.NioServerSocketChannel.doBind(java.net.SocketAddress) line: 130
io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe(io.netty.channel.AbstractChannel$AbstractUnsafe).bind(java.net.SocketAddress, io.netty.channel.ChannelPromise) line: 562
io.netty.channel.DefaultChannelPipeline$HeadContext.bind(io.netty.channel.ChannelHandlerContext, java.net.SocketAddress, io.netty.channel.ChannelPromise) line: 1332
io.netty.channel.DefaultChannelPipeline$HeadContext(io.netty.channel.AbstractChannelHandlerContext).invokeBind(java.net.SocketAddress, io.netty.channel.ChannelPromise) line: 501
io.netty.channel.DefaultChannelPipeline$TailContext(io.netty.channel.AbstractChannelHandlerContext).bind(java.net.SocketAddress, io.netty.channel.ChannelPromise) line: 486
io.netty.channel.DefaultChannelPipeline.bind(java.net.SocketAddress, io.netty.channel.ChannelPromise) line: 984
io.netty.channel.socket.nio.NioServerSocketChannel(io.netty.channel.AbstractChannel).bind(java.net.SocketAddress, io.netty.channel.ChannelPromise) line: 258
io.netty.bootstrap.AbstractBootstrap$2.run() line: 366
io.netty.util.concurrent.AbstractEventExecutor.safeExecute(java.lang.Runnable) line: 163
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).runAllTasks(long) line: 404
io.netty.channel.nio.NioEventLoop.run() line: 495
io.netty.util.concurrent.SingleThreadEventExecutor$5.run() line: 905
java.lang.Thread.run() line: 748

boss线程中NioServerSocketChannel.doBind这才是真正bind端口的地方,调用栈如上,代码如下:

// NioServerSocketChannel
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}

select和业务处理过程的抽象与逻辑组织

NioEventLoop主要逻辑

  1. 根据select策略处理select的事情,可能情况包括:
  2. 根据ioRatio配比进行selectkey的处理(即IO事件)还是跑所有任务(业务逻辑、自定义事件等)。

    关于selectkey的处理:

    Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");

    Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

sun.nio.ch.KQueueSelectorImpl@153cf15c

3. 处理异常,注意此处抓的是Throwable异常,这样能防止线程跑飞。目前的异常处理是打了警告日志并sleep 1s防止CPU被完全吃掉。

注册OP_ACCEPT事件

真正注册accept事件的地方:

先注册感兴趣的事件为0,注册出key:

Thread [boss-0] (Suspended (breakpoint at line 386 in io.netty.channel.nio.AbstractNioChannel))
io.netty.channel.socket.nio.NioServerSocketChannel(io.netty.channel.nio.AbstractNioChannel).doRegister() line: 386
io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe(io.netty.channel.AbstractChannel$AbstractUnsafe).register0(io.netty.channel.ChannelPromise) line: 508
io.netty.channel.AbstractChannel$AbstractUnsafe.access$200(io.netty.channel.AbstractChannel$AbstractUnsafe, io.netty.channel.ChannelPromise) line: 427
io.netty.channel.AbstractChannel$AbstractUnsafe$1.run() line: 486
io.netty.util.concurrent.AbstractEventExecutor.safeExecute(java.lang.Runnable) line: 163
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).runAllTasks(long) line: 404
io.netty.channel.nio.NioEventLoop.run() line: 495
io.netty.util.concurrent.SingleThreadEventExecutor$5.run() line: 905
java.lang.Thread.run() line: 748

代码

// io.netty.channel.nio.AbstractNioChannel.doRegister()
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
// Force the Selector to select now as the "canceled" SelectionKey may still be
// cached and not removed because no Select.select(..) operation was called yet.
eventLoop().selectNow();
selected = true;
} else {
// We forced a select operation on the selector before but the SelectionKey is still cached
// for whatever reason. JDK bug ?
throw e;
}
}
}
}

再在doBeginRead中注册16,真正注册:

boss调用栈:

Thread [boss-0] (Suspended (breakpoint at line 420 in io.netty.channel.nio.AbstractNioChannel))
io.netty.channel.socket.nio.NioServerSocketChannel(io.netty.channel.nio.AbstractNioChannel).doBeginRead() line: 420
io.netty.channel.socket.nio.NioServerSocketChannel(io.netty.channel.nio.AbstractNioMessageChannel).doBeginRead() line: 55
io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe(io.netty.channel.AbstractChannel$AbstractUnsafe).beginRead() line: 851
io.netty.channel.DefaultChannelPipeline$HeadContext.read(io.netty.channel.ChannelHandlerContext) line: 1360
io.netty.channel.DefaultChannelPipeline$HeadContext(io.netty.channel.AbstractChannelHandlerContext).invokeRead() line: 693
io.netty.channel.DefaultChannelPipeline$TailContext(io.netty.channel.AbstractChannelHandlerContext).read() line: 673
io.netty.channel.DefaultChannelPipeline.read() line: 1015
io.netty.channel.socket.nio.NioServerSocketChannel(io.netty.channel.AbstractChannel).read() line: 288
io.netty.channel.DefaultChannelPipeline$HeadContext.readIfIsAutoRead() line: 1420
io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(io.netty.channel.ChannelHandlerContext) line: 1398
io.netty.channel.DefaultChannelPipeline$HeadContext(io.netty.channel.AbstractChannelHandlerContext).invokeChannelActive() line: 213
io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(io.netty.channel.AbstractChannelHandlerContext) line: 199
io.netty.channel.DefaultChannelPipeline.fireChannelActive() line: 906
io.netty.channel.AbstractChannel$AbstractUnsafe$2.run() line: 573
io.netty.util.concurrent.AbstractEventExecutor.safeExecute(java.lang.Runnable) line: 163
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).runAllTasks(long) line: 404
io.netty.channel.nio.NioEventLoop.run() line: 495
io.netty.util.concurrent.SingleThreadEventExecutor$5.run() line: 905
java.lang.Thread.run() line: 748

代码:

// io.netty.channel.nio.AbstractNioChannel.doBeginRead()
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
} readPending = true; final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp); // accept的readInterestOp是16
}
}

select出OP_ACCEPT的处理与SocketChannel的创建

也就是在run方法中select的逻辑:

在策略判断时如果有任务的情况下会做io.netty.channel.nio.NioEventLoop.selectNowSupplier的get方法调用,这个方法中会selectNow:

在策略判断时如果没有任务的话,策略判断则会返回-1即SelectStrategy.SELECT,并执行带超时时间参数的select:

 case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));

此时会走下面的调用栈:

SelectedSelectionKeySetSelector.select(long) line: 62
NioEventLoop.select(boolean) line: 786
NioEventLoop.run() line: 434
SingleThreadEventExecutor$5.run() line: 905
Thread.run() line: 748

关于io.netty.channel.SelectStrategy.calculateStrategy(IntSupplier, boolean)策略的返回值整理情况如下:

① -2,跳过本次处理;目前还未构造出这种场景。

② -3,忙等也是跳过;目前还未构造出这种场景。

③ -1,没有任务时需要select,并判断是否要wakeup;没有任务时就是这种这种场景。

④ >=0的其他值,也就是default,啥都不干往下走,走处理selectkey(即IO事件)或者所有任务(业务逻辑等)。

io.netty.channel.DefaultSelectStrategy.calculateStrategy(IntSupplier, boolean) 计算逻辑分析:

如果没有任务了 就返回 SelectStrategy.SELECT

如果有任务 就io.netty.channel.nio.NioEventLoop.new IntSupplier() {...}.() selectNow一次,注意这个selectNow是NioEventLoop的selectNow方法,但是最终还会对应到JDK的Selector的selectNow上去。

io.netty.channel.nio.NioEventLoop.select(boolean)分析

  1. select时超时时间怎么定

    这个有超时时间的select,超时时间这个值是怎么确定的?

    deadline减去当前时间(这两个都是用纳秒计量)后四舍五入到毫秒粒度

    deadline又是怎么计算的?

    默认值是1s,如果scheduledTaskQueue中有调度任务,以优先级队列顶部的scheduledTask的调度时间结合当前时间算出deadline。

  2. 该select(boolean)的逻辑和避免epoll select空转bug的规避

    整体是一个spin循环包起来的逻辑

    如果deadline已到,则selector.selectNow();并退出spin循环。

    如果有任务且wakeup是false(是false要将其设置成true),则selector.selectNow();并退出spin循环。

    上面两种情况都没命中则selector.select(timeoutMillis);并计数。如果selected到key了或者oldWakenUp为true或者wakenUp字段为true或者有任务或者有调度任务都退出spin循环。

    如果在这个spin循环中seletc次数大于SELECTOR_AUTO_REBUILD_THRESHOLD(默认值512),则重建这个方法内局部变量Selector实例,并将之前selector上的事件注册到这个新的上面来。这个处理就是为了规避linux上epoll的bug,epoll可能select方法会直接返回,即使没有超时并且也没有IO事件到达,这就是著名的epoll bug,这是一个比较严重的bug,它会导致线程陷入死循环,会让CPU飙到100%。参见这里

    相关代码:

io.netty.channel.nio.NioEventLoop.rebuildSelector0()
SelectionKey newKey = key.channel().register(newSelectorTuple.unwrappedSelector, interestOps, a);

Selector到key之后的处理

final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}

processSelectedKeys

调用栈 :

NioServerSocketChannel.doReadMessages(List<Object>) line: 143
AbstractNioMessageChannel$NioMessageUnsafe.read() line: 75
NioEventLoop.processSelectedKey(SelectionKey, AbstractNioChannel) line: 677
NioEventLoop.processSelectedKeysOptimized() line: 612
NioEventLoop.processSelectedKeys() line: 529
NioEventLoop.run() line: 491
SingleThreadEventExecutor$5.run() line: 905
Thread.run() line: 748

涉及的部分代码:

有accept出SocketChannel的关键部分

// NioEventLoop
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read(); // unsafe是io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe
}
// NioServerSocketChannel
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel()); try {
if (ch != null) {
buf.add(new NioSocketChannel(this, 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;
}

注册OP_READ事件

woker调用栈:

注册出key:

Thread [worker-0] (Suspended (breakpoint at line 386 in io.netty.channel.nio.AbstractNioChannel))
io.netty.channel.socket.nio.NioSocketChannel(io.netty.channel.nio.AbstractNioChannel).doRegister() line: 386
io.netty.channel.socket.nio.NioSocketChannel$NioSocketChannelUnsafe(io.netty.channel.AbstractChannel$AbstractUnsafe).register0(io.netty.channel.ChannelPromise) line: 508
io.netty.channel.AbstractChannel$AbstractUnsafe.access$200(io.netty.channel.AbstractChannel$AbstractUnsafe, io.netty.channel.ChannelPromise) line: 427
io.netty.channel.AbstractChannel$AbstractUnsafe$1.run() line: 486
io.netty.util.concurrent.AbstractEventExecutor.safeExecute(java.lang.Runnable) line: 163
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).runAllTasks(long) line: 404
io.netty.channel.nio.NioEventLoop.run() line: 495
io.netty.util.concurrent.SingleThreadEventExecutor$5.run() line: 905
java.lang.Thread.run() line: 748

真正注册:

Thread [worker-0] (Suspended (breakpoint at line 420 in io.netty.channel.nio.AbstractNioChannel))
io.netty.channel.socket.nio.NioSocketChannel(io.netty.channel.nio.AbstractNioChannel).doBeginRead() line: 420
io.netty.channel.socket.nio.NioSocketChannel$NioSocketChannelUnsafe(io.netty.channel.AbstractChannel$AbstractUnsafe).beginRead() line: 851
io.netty.channel.DefaultChannelPipeline$HeadContext.read(io.netty.channel.ChannelHandlerContext) line: 1360
io.netty.channel.DefaultChannelPipeline$HeadContext(io.netty.channel.AbstractChannelHandlerContext).invokeRead() line: 693
io.netty.channel.DefaultChannelHandlerContext(io.netty.channel.AbstractChannelHandlerContext).read() line: 673
io.netty.handler.timeout.ReadTimeoutHandler(io.netty.channel.ChannelDuplexHandler).read(io.netty.channel.ChannelHandlerContext) line: 95
io.netty.channel.DefaultChannelHandlerContext(io.netty.channel.AbstractChannelHandlerContext).invokeRead() line: 693
io.netty.channel.DefaultChannelHandlerContext(io.netty.channel.AbstractChannelHandlerContext).read() line: 673
io.netty.handler.codec.http.HttpResponseEncoder(io.netty.channel.ChannelOutboundHandlerAdapter).read(io.netty.channel.ChannelHandlerContext) line: 93
io.netty.channel.DefaultChannelHandlerContext(io.netty.channel.AbstractChannelHandlerContext).invokeRead() line: 693
io.netty.channel.DefaultChannelHandlerContext(io.netty.channel.AbstractChannelHandlerContext).read() line: 673
io.netty.handler.stream.ChunkedWriteHandler(io.netty.channel.ChannelDuplexHandler).read(io.netty.channel.ChannelHandlerContext) line: 95
io.netty.channel.DefaultChannelHandlerContext(io.netty.channel.AbstractChannelHandlerContext).invokeRead() line: 693
io.netty.channel.DefaultChannelHandlerContext(io.netty.channel.AbstractChannelHandlerContext).read() line: 673
io.netty.handler.codec.http.HttpContentCompressor(io.netty.channel.ChannelDuplexHandler).read(io.netty.channel.ChannelHandlerContext) line: 95
io.netty.channel.DefaultChannelHandlerContext(io.netty.channel.AbstractChannelHandlerContext).invokeRead() line: 693
io.netty.channel.DefaultChannelPipeline$TailContext(io.netty.channel.AbstractChannelHandlerContext).read() line: 673
io.netty.channel.DefaultChannelPipeline.read() line: 1015
io.netty.channel.socket.nio.NioSocketChannel(io.netty.channel.AbstractChannel).read() line: 288
io.netty.channel.DefaultChannelPipeline$HeadContext.readIfIsAutoRead() line: 1420
io.netty.channel.DefaultChannelPipeline$HeadContext.channelActive(io.netty.channel.ChannelHandlerContext) line: 1398
io.netty.channel.DefaultChannelPipeline$HeadContext(io.netty.channel.AbstractChannelHandlerContext).invokeChannelActive() line: 213
io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(io.netty.channel.AbstractChannelHandlerContext) line: 199
io.netty.channel.DefaultChannelPipeline.fireChannelActive() line: 906
io.netty.channel.socket.nio.NioSocketChannel$NioSocketChannelUnsafe(io.netty.channel.AbstractChannel$AbstractUnsafe).register0(io.netty.channel.ChannelPromise) line: 522
io.netty.channel.AbstractChannel$AbstractUnsafe.access$200(io.netty.channel.AbstractChannel$AbstractUnsafe, io.netty.channel.ChannelPromise) line: 427
io.netty.channel.AbstractChannel$AbstractUnsafe$1.run() line: 486
io.netty.util.concurrent.AbstractEventExecutor.safeExecute(java.lang.Runnable) line: 163
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).runAllTasks(long) line: 404
io.netty.channel.nio.NioEventLoop.run() line: 495
io.netty.util.concurrent.SingleThreadEventExecutor$5.run() line: 905
java.lang.Thread.run() line: 748

select出OP_READ的处理与buf的读取

读取数据线程栈:

Thread [worker-2] (Suspended (breakpoint at line 57 in HttpContentDecoder))
HttpContentDecompressor(HttpContentDecoder).decode(ChannelHandlerContext, HttpObject, List<Object>) line: 57
HttpContentDecompressor(HttpContentDecoder).decode(ChannelHandlerContext, Object, List) line: 47
HttpContentDecompressor(MessageToMessageDecoder<I>).channelRead(ChannelHandlerContext, Object) line: 88
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelRead(Object) line: 362
AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) line: 348
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelRead(Object) line: 340
ByteToMessageDecoder.fireChannelRead(ChannelHandlerContext, CodecOutputList, int) line: 323
ByteToMessageDecoder.fireChannelRead(ChannelHandlerContext, List<Object>, int) line: 310
HttpRequestDecoder(ByteToMessageDecoder).callDecode(ChannelHandlerContext, ByteBuf, List<Object>) line: 426
HttpRequestDecoder(ByteToMessageDecoder).channelRead(ChannelHandlerContext, Object) line: 278
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelRead(Object) line: 362
AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) line: 348
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelRead(Object) line: 340
PipelineInitializer$ChannelActiveTester(ChannelInboundHandlerAdapter).channelRead(ChannelHandlerContext, Object) line: 86
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelRead(Object) line: 362
AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) line: 348
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelRead(Object) line: 340
ReadTimeoutHandler(IdleStateHandler).channelRead(ChannelHandlerContext, Object) line: 286
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelRead(Object) line: 362
AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) line: 348
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).fireChannelRead(Object) line: 340
DefaultChannelPipeline$HeadContext.channelRead(ChannelHandlerContext, Object) line: 1408
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeChannelRead(Object) line: 362
AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext, Object) line: 348
DefaultChannelPipeline.fireChannelRead(Object) line: 930
NioSocketChannel$NioSocketChannelUnsafe(AbstractNioByteChannel$NioByteUnsafe).read() line: 163
NioEventLoop.processSelectedKey(SelectionKey, AbstractNioChannel) line: 677
NioEventLoop.processSelectedKeysOptimized() line: 612
NioEventLoop.processSelectedKeys() line: 529
NioEventLoop.run() line: 491
SingleThreadEventExecutor$5.run() line: 905
Thread.run() line: 748

在processSelectedKey之后会用 NioSocketChannelUnsafe将数据读到Buf中。再交给Pipeline触发后面的每一个handler。

此处要注意下读的时候 selectkey的attachment中放的是啥:

private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// null out entry in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.keys[i] = null; final Object a = k.attachment(); // 附件中放的是AbstractNioChannel AbstractNioChannel中有unsafe对象,unsafe中能关联读取用的buf。 if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a); // ...
} private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe(); // AbstractNioChannel中有unsafe对象
// ...
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}

写出数据的处理

写数据的处理可以在SocketChannelImpl [entry] - write(ByteBuffer) 处打方法断点观察。

调用堆栈如下:

Thread [worker-3] (Suspended (entry into method write in SocketChannelImpl))
SocketChannelImpl.write(ByteBuffer) line: 458
NioSocketChannel.doWrite(ChannelOutboundBuffer) line: 405
NioSocketChannel$NioSocketChannelUnsafe(AbstractChannel$AbstractUnsafe).flush0() line: 938
NioSocketChannel$NioSocketChannelUnsafe(AbstractNioChannel$AbstractNioUnsafe).flush0() line: 360
NioSocketChannel$NioSocketChannelUnsafe(AbstractChannel$AbstractUnsafe).flush() line: 905
DefaultChannelPipeline$HeadContext.flush(ChannelHandlerContext) line: 1370
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeFlush0() line: 776
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeFlush() line: 768
DefaultChannelHandlerContext(AbstractChannelHandlerContext).flush() line: 749
ReadTimeoutHandler(ChannelDuplexHandler).flush(ChannelHandlerContext) line: 117
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeFlush0() line: 776
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeFlush() line: 768
DefaultChannelHandlerContext(AbstractChannelHandlerContext).flush() line: 749
HttpResponseEncoder(ChannelOutboundHandlerAdapter).flush(ChannelHandlerContext) line: 115
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeFlush0() line: 776
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeFlush() line: 768
DefaultChannelHandlerContext(AbstractChannelHandlerContext).flush() line: 749
ChunkedWriteHandler.doFlush(ChannelHandlerContext) line: 335
ChunkedWriteHandler.channelWritabilityChanged(ChannelHandlerContext) line: 148
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
HttpContentDecompressor(ChannelInboundHandlerAdapter).channelWritabilityChanged(ChannelHandlerContext) line: 119
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
HttpRequestDecoder(ChannelInboundHandlerAdapter).channelWritabilityChanged(ChannelHandlerContext) line: 119
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
PipelineInitializer$ChannelActiveTester(ChannelInboundHandlerAdapter).channelWritabilityChanged(ChannelHandlerContext) line: 119
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
ReadTimeoutHandler(ChannelInboundHandlerAdapter).channelWritabilityChanged(ChannelHandlerContext) line: 119
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
DefaultChannelPipeline$HeadContext.channelWritabilityChanged(ChannelHandlerContext) line: 1431
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelPipeline.fireChannelWritabilityChanged() line: 942
ChannelOutboundBuffer$2.run() line: 608
AbstractEventExecutor.safeExecute(Runnable) line: 163
NioEventLoop(SingleThreadEventExecutor).runAllTasks(long) line: 404
NioEventLoop.run() line: 495
SingleThreadEventExecutor$5.run() line: 905
Thread.run() line: 748

核心逻辑在:

io.netty.channel.socket.nio.NioSocketChannel.doWrite(ChannelOutboundBuffer)

 case 1: {
// Only one ByteBuf so use non-gathering write
// Zero length buffers are not added to nioBuffers by ChannelOutboundBuffer, so there is no need
// to check if the total size of all the buffers is non-zero.
ByteBuffer buffer = nioBuffers[0];
int attemptedBytes = buffer.remaining();
final int localWrittenBytes = ch.write(buffer);
if (localWrittenBytes <= 0) {
incompleteWrite(true);
return;
}
adjustMaxBytesPerGatheringWrite(attemptedBytes, localWrittenBytes, maxBytesPerGatheringWrite);
in.removeBytes(localWrittenBytes);
--writeSpinCount;
break;
}

writeSpinCount默认是16,也就说说写出线程会尝试写16次,如果ch.write写不出去了再调用incompleteWrite,incompleteWrite里会注册OP_WRITE事件,此处是个优化,不是一上来就注册事件,是先尝试写,写不了再注册。

注册OP_WRITE事件

需要构造较大的响应包,在本地才能观察到。 我构造了将近200万字符,才达能目的。

注册的地方打断点在 SelectionKeyImpl [entry] - interestOps(int) 遍可以观察

堆栈如下:

Thread [worker-3] (Suspended (entry into method interestOps in SelectionKeyImpl))
SelectionKeyImpl.interestOps(int) line: 82
NioSocketChannel(AbstractNioByteChannel).setOpWrite() line: 332
NioSocketChannel(AbstractNioByteChannel).incompleteWrite(boolean) line: 289
NioSocketChannel.doWrite(ChannelOutboundBuffer) line: 407
NioSocketChannel$NioSocketChannelUnsafe(AbstractChannel$AbstractUnsafe).flush0() line: 938
NioSocketChannel$NioSocketChannelUnsafe(AbstractNioChannel$AbstractNioUnsafe).flush0() line: 360
NioSocketChannel$NioSocketChannelUnsafe(AbstractChannel$AbstractUnsafe).flush() line: 905
DefaultChannelPipeline$HeadContext.flush(ChannelHandlerContext) line: 1370
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeFlush0() line: 776
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeFlush() line: 768
DefaultChannelHandlerContext(AbstractChannelHandlerContext).flush() line: 749
ReadTimeoutHandler(ChannelDuplexHandler).flush(ChannelHandlerContext) line: 117
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeFlush0() line: 776
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeFlush() line: 768
DefaultChannelHandlerContext(AbstractChannelHandlerContext).flush() line: 749
HttpResponseEncoder(ChannelOutboundHandlerAdapter).flush(ChannelHandlerContext) line: 115
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeFlush0() line: 776
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeFlush() line: 768
DefaultChannelHandlerContext(AbstractChannelHandlerContext).flush() line: 749
ChunkedWriteHandler.doFlush(ChannelHandlerContext) line: 335
ChunkedWriteHandler.channelWritabilityChanged(ChannelHandlerContext) line: 148
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
HttpContentDecompressor(ChannelInboundHandlerAdapter).channelWritabilityChanged(ChannelHandlerContext) line: 119
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
HttpRequestDecoder(ChannelInboundHandlerAdapter).channelWritabilityChanged(ChannelHandlerContext) line: 119
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
PipelineInitializer$ChannelActiveTester(ChannelInboundHandlerAdapter).channelWritabilityChanged(ChannelHandlerContext) line: 119
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelHandlerContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
ReadTimeoutHandler(ChannelInboundHandlerAdapter).channelWritabilityChanged(ChannelHandlerContext) line: 119
DefaultChannelHandlerContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).fireChannelWritabilityChanged() line: 409
DefaultChannelPipeline$HeadContext.channelWritabilityChanged(ChannelHandlerContext) line: 1431
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeChannelWritabilityChanged() line: 434
AbstractChannelHandlerContext.invokeChannelWritabilityChanged(AbstractChannelHandlerContext) line: 416
DefaultChannelPipeline.fireChannelWritabilityChanged() line: 942
ChannelOutboundBuffer$2.run() line: 608
AbstractEventExecutor.safeExecute(Runnable) line: 163
NioEventLoop(SingleThreadEventExecutor).runAllTasks(long) line: 404
NioEventLoop.run() line: 495
SingleThreadEventExecutor$5.run() line: 905
Thread.run() line: 748

netty里核心代码在:

// io.netty.channel.nio.AbstractNioByteChannel.setOpWrite()
protected final void setOpWrite() {
final SelectionKey key = selectionKey();
// Check first if the key is still valid as it may be canceled as part of the deregistration
// from the EventLoop
// See https://github.com/netty/netty/issues/2104
if (!key.isValid()) {
return;
}
final int interestOps = key.interestOps();
if ((interestOps & SelectionKey.OP_WRITE) == 0) {
key.interestOps(interestOps | SelectionKey.OP_WRITE);
}
}

select出OP_WRITE的处理与buf的写出

注册完之后再select到的写逻辑复用io.netty.channel.socket.nio.NioSocketChannel.doWrite(ChannelOutboundBuffer)

调用栈如下:

Thread [worker-3] (Suspended (entry into method write in SocketChannelImpl))
SocketChannelImpl.write(ByteBuffer) line: 458
NioSocketChannel.doWrite(ChannelOutboundBuffer) line: 405
NioSocketChannel$NioSocketChannelUnsafe(AbstractChannel$AbstractUnsafe).flush0() line: 938
NioSocketChannel$NioSocketChannelUnsafe(AbstractNioChannel$AbstractNioUnsafe).forceFlush() line: 367
NioEventLoop.processSelectedKey(SelectionKey, AbstractNioChannel) line: 671
NioEventLoop.processSelectedKeysOptimized() line: 612
NioEventLoop.processSelectedKeys() line: 529
NioEventLoop.run() line: 491
SingleThreadEventExecutor$5.run() line: 905
Thread.run() line: 748

main线程与boss线程切换,boss线程与worker线程切换

boss与worker的NioEventLoop的spin run是什么时候启动触发的

main启动boss

Thread [main] (Suspended (breakpoint at line 707 in java.lang.Thread))
owns: java.lang.Thread (id=80)
java.lang.Thread.start() line: 707
io.netty.util.concurrent.ThreadPerTaskExecutor.execute(java.lang.Runnable) line: 33
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).doStartThread() line: 894
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).startThread() line: 865
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).execute(java.lang.Runnable) line: 758
io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe(io.netty.channel.AbstractChannel$AbstractUnsafe).register(io.netty.channel.EventLoop, io.netty.channel.ChannelPromise) line: 483
io.netty.channel.nio.NioEventLoop(io.netty.channel.SingleThreadEventLoop).register(io.netty.channel.ChannelPromise) line: 80
io.netty.channel.nio.NioEventLoop(io.netty.channel.SingleThreadEventLoop).register(io.netty.channel.Channel) line: 74
io.netty.channel.nio.NioEventLoopGroup(io.netty.channel.MultithreadEventLoopGroup).register(io.netty.channel.Channel) line: 86
io.netty.bootstrap.ServerBootstrap(io.netty.bootstrap.AbstractBootstrap<B,C>).initAndRegister() line: 333
io.netty.bootstrap.ServerBootstrap(io.netty.bootstrap.AbstractBootstrap<B,C>).doBind(java.net.SocketAddress) line: 282
io.netty.bootstrap.ServerBootstrap(io.netty.bootstrap.AbstractBootstrap<B,C>).bind(java.net.SocketAddress) line: 278
org.restexpress.RestExpress.bind(java.net.InetSocketAddress) line: 709
org.restexpress.RestExpress.bind(java.lang.String, int) line: 686
com.code260.ss.resetexpress.RestExpressApp1.main(java.lang.String[]) line: 26

boss启动worker

Thread [boss-0] (Suspended (breakpoint at line 707 in java.lang.Thread))
owns: java.lang.Thread (id=102)
java.lang.Thread.start() line: 707
io.netty.util.concurrent.ThreadPerTaskExecutor.execute(java.lang.Runnable) line: 33
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).doStartThread() line: 894
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).startThread() line: 865
io.netty.channel.nio.NioEventLoop(io.netty.util.concurrent.SingleThreadEventExecutor).execute(java.lang.Runnable) line: 758
io.netty.channel.socket.nio.NioSocketChannel$NioSocketChannelUnsafe(io.netty.channel.AbstractChannel$AbstractUnsafe).register(io.netty.channel.EventLoop, io.netty.channel.ChannelPromise) line: 483
io.netty.channel.nio.NioEventLoop(io.netty.channel.SingleThreadEventLoop).register(io.netty.channel.ChannelPromise) line: 80
io.netty.channel.nio.NioEventLoop(io.netty.channel.SingleThreadEventLoop).register(io.netty.channel.Channel) line: 74
io.netty.channel.nio.NioEventLoopGroup(io.netty.channel.MultithreadEventLoopGroup).register(io.netty.channel.Channel) line: 86
io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor.channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object) line: 255
io.netty.channel.DefaultChannelHandlerContext(io.netty.channel.AbstractChannelHandlerContext).invokeChannelRead(java.lang.Object) line: 362
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(io.netty.channel.AbstractChannelHandlerContext, java.lang.Object) line: 348
io.netty.channel.DefaultChannelPipeline$HeadContext(io.netty.channel.AbstractChannelHandlerContext).fireChannelRead(java.lang.Object) line: 340
io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object) line: 1408
io.netty.channel.DefaultChannelPipeline$HeadContext(io.netty.channel.AbstractChannelHandlerContext).invokeChannelRead(java.lang.Object) line: 362
io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(io.netty.channel.AbstractChannelHandlerContext, java.lang.Object) line: 348
io.netty.channel.DefaultChannelPipeline.fireChannelRead(java.lang.Object) line: 930
io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe.read() line: 93
io.netty.channel.nio.NioEventLoop.processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel) line: 677
io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized() line: 612
io.netty.channel.nio.NioEventLoop.processSelectedKeys() line: 529
io.netty.channel.nio.NioEventLoop.run() line: 491
io.netty.util.concurrent.SingleThreadEventExecutor$5.run() line: 905
java.lang.Thread.run() line: 748

selector与线程绑定的问题

一个selector对应一个channel,一个selector又对应固定的一个线程,所以一个channel上的数据多次读取不会在不同线程漂移

当然,一个线程可以对应多个channel,但因为一个selector又对应固定的一个线程,所以这些channel用的同一个selector。

多boss问题也即多accept问题

什么时候用多accept?

一般是用在多个端口bind,或者一个端口不同ip网卡平面bind时,多accept(即boss)线程才有意义。否则一个port通常只能bind出一个ServerSocketChannel出来,一个channel又对应一个Selector,一个Selector又对应一个线程在spin的方式去select,所以多个线程对一个bind也没用。如果把一个selector对应到多个线程去用问题在于,selector的方法线程不安全,对应到多个线程会有问题。

当然后来的JDK版本,包括linux开始支持一个port+ip可以被多个进程多次绑定,这样多accept就有意义了。参见 《NIO trick and trap》这个ppt的 题外:SO_REUSEPORT 关键字。 应用场景是 “适合大量短连接的web server”

[netty4][netty-transport]netty之nio传输层的更多相关文章

  1. 【Netty】Netty传输

    一.前言 在简单学习了Netty中的组件后,接着学习Netty中数据的传输细节. 二.传输 2.1 传输示例 Netty中的数据传输都是使用的字节类型,下面通过一个实例进行说明,该实例中服务器接受请求 ...

  2. Netty学习笔记(一)——nio基础

    Netty简单认识: 1) Netty 是由JBOSS 提供的一个Java 开源框架. 2) Netty 是一个异步的.基于事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络I0 程序. 3) ...

  3. netty: marshalling传递对象,传输附件GzipUtils

    netty: marshalling传递对象,传输附件GzipUtils 前端与服务端传输文件时,需要双方需要进行解压缩,也就是Java序列化.可以使用java进行对象序列化,netty去传输,但ja ...

  4. 传输层-Transport Layer(上):传输层的功能、三次握手与四次握手、最大-最小公平、AIMD加法递增乘法递减

    第六章 传输层-Transport Layer(上) 6.1传输层概述 在之前的几章内容中,我们自底向上的描述了计算机网络的各个层次,还描述了一些处于不同层次下的经典网络协议(如以太网.无线局域网.或 ...

  5. 传输层-Transport Layer(下):UDP与TCP报头解析、TCP滑动窗口、TCP拥塞控制详解

    第六章 传输层-Transport Layer(下) 上一篇文章对传输层的寻址方式.功能.以及流量控制方法做了简短的介绍,这一部分将介绍传输层最重要的两个实例:TCP协议和UDP协议,看一看之前描述的 ...

  6. [转帖]技术扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解

    技术扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解    http://www.52im.net/thread-1309-1-1.html   本文来自腾讯资深研发工程师罗成的技术分享, ...

  7. 002——Netty之Netty介绍

    Netty出现背景 Java NIO难用 据说存在bug 业界其他NIO框架不成熟 Netty主要解决两个相应关注领域 (1)异步和事件驱动的实现. (2)一组设计模式,将应用逻辑与网络层解耦. 特性 ...

  8. Netty学习——Netty和Protobuf的整合(一)

    Netty学习——Netty和Protobuf的整合 Protobuf作为序列化的工具,将序列化后的数据,通过Netty来进行在网络上的传输 1.将proto文件里的java包的位置修改一下,然后再执 ...

  9. Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platform)

    catalog . 引言 . Windows 2000网络结构和OSI模型 . NDIS驱动 . NDIS微端口驱动编程实例 . NDIS中间层驱动编程实例 . NDIS协议层驱动编程实例 . TDI ...

随机推荐

  1. 3.OSPF协议及链路状态算法

    OSPF的特点: 1.使用洪泛法向自治系统内所有路由器发送信息,即路由器通过输出端口向所有相邻的路由器发送信息,而每一个相邻路由器又再次将此信息发往其所有的相邻路由器.最终整个区域内所有路由器都得到了 ...

  2. java基础知识--数据类型

    计算机时识别不了我们编写的代码语言,计算机中的数据全部采用二进制表示,即0和1表示的数字,每一个0或者1就是一个位,一个位叫做一个bit(比特).(实际上计算机只能识别高低电平,而不是0和1.) 字节 ...

  3. 04 CMD规范基础使用详解

    CMD模块规范 1.1 CMD规范说明 专门用于浏览器端,并且模块的加载是异步的,而且只有模块使用时才会加载执行: CMD规范的语法类似于Commonjs + AMD --定义模块使用AMD语法,暴露 ...

  4. [spring] -- AOP、IOC、DI篇

    AOP(面向切面编程) 怎么理解这个切面编程的概念及优点? 概念: 横切关注点与业务逻辑相分离,切面能帮助我们模块化横切关注点: 优点: 现在每个关注点都集中于一个地方,而不是分散到多处代码中: 服务 ...

  5. 小白必看,Python入门你要懂那些

    Python作为为数不多的全场景开发语言之一,近年来已经获得了越来越多人的关注,而整个IT行业也释放出了大量的Python就业岗位.因此,当前学习Python语言是非常不错的选择,文泽带你进一步走进P ...

  6. ES6 常用语法知识汇总

    ES6模块化如何使用,开发环境如何打包? 1.模块化的基本语法 /* export 语法 */ // 默认导出 export default { a: '我是默认导出的', } // 单独导出 exp ...

  7. Day13_Thymeleaf简介

    学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"乐优商城"获取视频和教程资料! b站在线视频 1.Th ...

  8. Day01_企业权限管理(SSM整合)

    学于黑马程序员和传智播客联合做的教学项目 感谢 黑马程序员官网 传智播客官网 个人根据教程的每天的工作进度的代码和资料 密码:cti5 b站在线视频 微信搜索"艺术行者",关注并回 ...

  9. JS中Math.random()的使用和扩展

    Math.random()方法返回大于等于 0 小于 1 的一个随机数.对于某些站点来说,这个方法非常实用,因为可以利用它来随机显示一些名人名言和新闻事件. 在连续整数中取得一个随机数 值 = Mat ...

  10. CF 633 div1 1338 B. Edge Weight Assignment 构造

    LINK:Edge Weight Assignment 这场当时没打 看到这个B题吓到我了 还好当时没打. 想了20min才知道怎么做 而且还不能证明. 首先考虑求最小. 可以发现 如果任意两个叶子节 ...