记录使用过程中偶然发现的一些关键逻辑。先做记录,以后netty知识有一定体系再做整理
服务器中有俩group,一个是parentGroup,负责处理链接请求,一个是childGroup,负责业务逻辑。
channelActive是在childGroup中触发的。
新建立链接后会触发channelActive这个事件,parent会将此事件打包成任务放到child的taskqueue中
offerTask:353, SingleThreadEventExecutor (io.netty.util.concurrent)
addTask:344, SingleThreadEventExecutor (io.netty.util.concurrent)
execute:836, SingleThreadEventExecutor (io.netty.util.concurrent)
execute0:827, SingleThreadEventExecutor (io.netty.util.concurrent)
execute:817, SingleThreadEventExecutor (io.netty.util.concurrent)
register:483, AbstractChannel$AbstractUnsafe (io.netty.channel)
register:89, SingleThreadEventLoop (io.netty.channel)
register:83, SingleThreadEventLoop (io.netty.channel)
register:86, MultithreadEventLoopGroup (io.netty.channel)
channelRead:215, ServerBootstrap$ServerBootstrapAcceptor (io.netty.bootstrap)
invokeChannelRead:444, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:420, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:412, AbstractChannelHandlerContext (io.netty.channel)
channelRead:1410, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeChannelRead:440, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:420, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:919, DefaultChannelPipeline (io.netty.channel)
read:97, AbstractNioMessageChannel$NioMessageUnsafe (io.netty.channel.nio)
processSelectedKey:788, NioEventLoop (io.netty.channel.nio)
processSelectedKeysOptimized:724, NioEventLoop (io.netty.channel.nio)
processSelectedKeys:650, NioEventLoop (io.netty.channel.nio)
run:562, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
child执行任务
channelActive:41, NettyHandler (com.example.demo.demos.service)
invokeChannelActive:262, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelActive:238, AbstractChannelHandlerContext (io.netty.channel)
fireChannelActive:231, AbstractChannelHandlerContext (io.netty.channel)
channelActive:1398, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeChannelActive:258, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelActive:238, AbstractChannelHandlerContext (io.netty.channel)
fireChannelActive:895, DefaultChannelPipeline (io.netty.channel)
register0:522, AbstractChannel$AbstractUnsafe (io.netty.channel)
access$200:429, AbstractChannel$AbstractUnsafe (io.netty.channel)
run:486, AbstractChannel$AbstractUnsafe$1 (io.netty.channel)
runTask:174, AbstractEventExecutor (io.netty.util.concurrent)
safeExecute:167, AbstractEventExecutor (io.netty.util.concurrent)
runAllTasks:470, SingleThreadEventExecutor (io.netty.util.concurrent)
run:569, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
从哪获取child?
ServerBootstrapAcceptor中会存储childGroup,通过这个childGroup以轮询这种负载均衡算法将任务设置到group中的一个loop里
ServerBootstrapAcceptor.childGroup何时何地设置?
:186, ServerBootstrap$ServerBootstrapAcceptor (io.netty.bootstrap)
run:154, ServerBootstrap$1$1 (io.netty.bootstrap)
runTask:174, AbstractEventExecutor (io.netty.util.concurrent)
safeExecute:167, AbstractEventExecutor (io.netty.util.concurrent)
runAllTasks:470, SingleThreadEventExecutor (io.netty.util.concurrent)
run:569, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
child和parent为一个loop时,如何更换pipeline?
pipeline跟channel绑定,而不是与loop绑定
bossGroup设置为1就行了?
貌似只有一个channel在处理accept请求(如果服务端只开了一个端口监听服务),bossGroup设置多了也只会用一个loop。
这个观点还有待进一步验证
WEPollSelectorImpl
使用"轮询+同步阻塞"的方式处理channel中发生的事件
底层使用微软的一套API阻塞线程,直到有新的事件到达
doSelect:111, WEPollSelectorImpl (sun.nio.ch)
lockAndDoSelect:129, SelectorImpl (sun.nio.ch)
select:146, SelectorImpl (sun.nio.ch)
select:68, SelectedSelectionKeySetSelector (io.netty.channel.nio)
select:879, NioEventLoop (io.netty.channel.nio)
run:526, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
阻塞状态结束后,io.netty.channel.nio.NioEventLoop#processSelectedKeysOptimized中会遍历io.netty.channel.nio.NioEventLoop#selectedKeys获取需要处理的事件
从NioEventLoop#selectedKeys取出的key的channel是netty中的类?
阻塞结束后用fd找到相应的channel然后进行处理?
loop中的任务如何判断使用哪个channel的pipeline?
github:https://github.com/piscisaureus/wepoll
貌似只有jdk17才会用到这个组件,jdk8不会用。
创建loop时创建handle
create:-1, WEPoll (sun.nio.ch)
:73, WEPollSelectorImpl (sun.nio.ch)
openSelector:33, WEPollSelectorProvider (sun.nio.ch)
openSelector:177, NioEventLoop (io.netty.channel.nio)
:146, NioEventLoop (io.netty.channel.nio)
newChild:183, NioEventLoopGroup (io.netty.channel.nio)
newChild:38, NioEventLoopGroup (io.netty.channel.nio)
:84, MultithreadEventExecutorGroup (io.netty.util.concurrent)
:60, MultithreadEventExecutorGroup (io.netty.util.concurrent)
:52, MultithreadEventLoopGroup (io.netty.channel)
:97, NioEventLoopGroup (io.netty.channel.nio)
:92, NioEventLoopGroup (io.netty.channel.nio)
:73, NioEventLoopGroup (io.netty.channel.nio)
:53, NioEventLoopGroup (io.netty.channel.nio)
init:37, DemoApplicationTests2 (com.example.demo)
main:31, DemoApplicationTests2 (com.example.demo)
向selector注册需要监听的文件
ctl:-1, WEPoll (sun.nio.ch)
processUpdateQueue:143, WEPollSelectorImpl (sun.nio.ch)
doSelect:107, WEPollSelectorImpl (sun.nio.ch)
lockAndDoSelect:129, SelectorImpl (sun.nio.ch)
select:146, SelectorImpl (sun.nio.ch)
select:68, SelectedSelectionKeySetSelector (io.netty.channel.nio)
select:879, NioEventLoop (io.netty.channel.nio)
run:526, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
等待事件发生
wait:-1, WEPoll (sun.nio.ch)
doSelect:111, WEPollSelectorImpl (sun.nio.ch)
lockAndDoSelect:129, SelectorImpl (sun.nio.ch)
select:146, SelectorImpl (sun.nio.ch)
select:68, SelectedSelectionKeySetSelector (io.netty.channel.nio)
select:879, NioEventLoop (io.netty.channel.nio)
run:526, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
WEPoll.wait方法的出参代表捕获了多少事件,有多少待处理事件存储在WEPoll的数组中。所以事件到来后需要轮询WEPoll数组,并将数组中元素转换成文件描述符,最后用文件描述符找到相应的channel。
轮询WEPoll数组,获取文件描述符
processEvents:170, WEPollSelectorImpl (sun.nio.ch)
doSelect:116, WEPollSelectorImpl (sun.nio.ch)
lockAndDoSelect:129, SelectorImpl (sun.nio.ch)
select:146, SelectorImpl (sun.nio.ch)
select:68, SelectedSelectionKeySetSelector (io.netty.channel.nio)
select:879, NioEventLoop (io.netty.channel.nio)
run:526, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
下面截图中红框部分会将待处理的channel所对应的key放到NioEventLoop#selectedKeys中,这样loop就可以拿到待处理的channel
WEPoll只是jdk17在win环境中对网络通讯的一种实现方式,其他平台不会用到WEPoll。所以学习WEPoll的抽象层更有助于理解netty。
create
java.nio.channels.spi.SelectorProvider#openSelector
ctl
调用java.nio.channels.SelectionKey#interestOps(int)将自身注册到sun.nio.ch.SelectionKeyImpl#selector中(win jdk17中会将数据添加到sun.nio.ch.WEPollSelectorImpl#updateKeys),然后java.nio.channels.Selector#select()就可以监听相应的channel了(win jdk17中,每一次触发wait前,都会将updateKeys中的内容补充到WEPoll中,然后再开始wait)。
wait
java.nio.channels.Selector#select()
事件发生
从NioEventLoop#selectedKeys中拿数据
将NioEventLoop#selectedKeys注册到selector
反射方式设置selectedKeys
selector上注册事件
processUpdateQueue:143, WEPollSelectorImpl (sun.nio.ch)
doSelect:107, WEPollSelectorImpl (sun.nio.ch)
lockAndDoSelect:129, SelectorImpl (sun.nio.ch)
select:146, SelectorImpl (sun.nio.ch)
select:68, SelectedSelectionKeySetSelector (io.netty.channel.nio)
select:879, NioEventLoop (io.netty.channel.nio)
run:526, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
添加fdToKey
processUpdateQueue:131, WEPollSelectorImpl (sun.nio.ch)
doSelect:107, WEPollSelectorImpl (sun.nio.ch)
lockAndDoSelect:129, SelectorImpl (sun.nio.ch)
select:146, SelectorImpl (sun.nio.ch)
select:68, SelectedSelectionKeySetSelector (io.netty.channel.nio)
select:879, NioEventLoop (io.netty.channel.nio)
run:526, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
updateKeys
setEventOps:227, WEPollSelectorImpl (sun.nio.ch)
interestOps:109, SelectionKeyImpl (sun.nio.ch)
doBeginRead:414, AbstractNioChannel (io.netty.channel.nio)
beginRead:834, AbstractChannel$AbstractUnsafe (io.netty.channel)
read:1362, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeRead:835, AbstractChannelHandlerContext (io.netty.channel)
read:814, AbstractChannelHandlerContext (io.netty.channel)
read:1004, DefaultChannelPipeline (io.netty.channel)
read:290, AbstractChannel (io.netty.channel)
readIfIsAutoRead:1422, DefaultChannelPipeline$HeadContext (io.netty.channel)
channelActive:1400, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeChannelActive:258, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelActive:238, AbstractChannelHandlerContext (io.netty.channel)
fireChannelActive:895, DefaultChannelPipeline (io.netty.channel)
register0:522, AbstractChannel$AbstractUnsafe (io.netty.channel)
register:480, AbstractChannel$AbstractUnsafe (io.netty.channel)
register:89, SingleThreadEventLoop (io.netty.channel)
register:83, SingleThreadEventLoop (io.netty.channel)
register:86, MultithreadEventLoopGroup (io.netty.channel)
channelRead:215, ServerBootstrap$ServerBootstrapAcceptor (io.netty.bootstrap)
invokeChannelRead:444, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:420, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:412, AbstractChannelHandlerContext (io.netty.channel)
channelRead:1410, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeChannelRead:440, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:420, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:919, DefaultChannelPipeline (io.netty.channel)
read:97, AbstractNioMessageChannel$NioMessageUnsafe (io.netty.channel.nio)
processSelectedKey:788, NioEventLoop (io.netty.channel.nio)
processSelectedKeysOptimized:724, NioEventLoop (io.netty.channel.nio)
processSelectedKeys:650, NioEventLoop (io.netty.channel.nio)
run:562, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
channel
register:236, AbstractSelectableChannel (java.nio.channels.spi)
doRegister:380, AbstractNioChannel (io.netty.channel.nio)
register0:508, AbstractChannel$AbstractUnsafe (io.netty.channel)
register:480, AbstractChannel$AbstractUnsafe (io.netty.channel)
register:89, SingleThreadEventLoop (io.netty.channel)
register:83, SingleThreadEventLoop (io.netty.channel)
register:86, MultithreadEventLoopGroup (io.netty.channel)
channelRead:215, ServerBootstrap$ServerBootstrapAcceptor (io.netty.bootstrap)
invokeChannelRead:444, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:420, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:412, AbstractChannelHandlerContext (io.netty.channel)
channelRead:1410, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeChannelRead:440, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:420, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:919, DefaultChannelPipeline (io.netty.channel)
read:97, AbstractNioMessageChannel$NioMessageUnsafe (io.netty.channel.nio)
processSelectedKey:788, NioEventLoop (io.netty.channel.nio)
processSelectedKeysOptimized:724, NioEventLoop (io.netty.channel.nio)
processSelectedKeys:650, NioEventLoop (io.netty.channel.nio)
run:562, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
accept
accept:382, ServerSocketChannelImpl (sun.nio.ch)
run:119, SocketUtils$5 (io.netty.util.internal)
run:116, SocketUtils$5 (io.netty.util.internal)
executePrivileged:807, AccessController (java.security)
doPrivileged:569, AccessController (java.security)
accept:116, SocketUtils (io.netty.util.internal)
doReadMessages:154, NioServerSocketChannel (io.netty.channel.socket.nio)
read:79, AbstractNioMessageChannel$NioMessageUnsafe (io.netty.channel.nio)
processSelectedKey:788, NioEventLoop (io.netty.channel.nio)
processSelectedKeysOptimized:724, NioEventLoop (io.netty.channel.nio)
processSelectedKeys:650, NioEventLoop (io.netty.channel.nio)
run:562, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
netty的管道将以att的形式注册到selector
attach:458, SelectionKey (java.nio.channels)
register:212, SelectorImpl (sun.nio.ch)
register:236, AbstractSelectableChannel (java.nio.channels.spi)
doRegister:380, AbstractNioChannel (io.netty.channel.nio)
register0:508, AbstractChannel$AbstractUnsafe (io.netty.channel)
access$200:429, AbstractChannel$AbstractUnsafe (io.netty.channel)
run:486, AbstractChannel$AbstractUnsafe$1 (io.netty.channel)
runTask:174, AbstractEventExecutor (io.netty.util.concurrent)
safeExecute:167, AbstractEventExecutor (io.netty.util.concurrent)
runAllTasks:470, SingleThreadEventExecutor (io.netty.util.concurrent)
run:569, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
上个截图中使用javaChannel获取到java自带的channel,然后用这个自带的channel生成了一个key。其中有几点需要注意。
1、原生channel(this)+监听的事件(ops)+nettyChannel(att)+selector(sel)=key,同时这个key也会反过来注册到当前的原生channel上(addKey(k)
)
2、若当前原生channel已经注册到selector中,就不要重复生成了。findKey
中会遍历当前原生channel的keys
这也间接说明一个channel可以绑定在多个selector上,但是同一个selector上不能绑定多个相同的channel,即使ops不同也不行。
processUpdateQueue:131, WEPollSelectorImpl (sun.nio.ch)
doSelect:107, WEPollSelectorImpl (sun.nio.ch)
lockAndDoSelect:129, SelectorImpl (sun.nio.ch)
select:146, SelectorImpl (sun.nio.ch)
select:68, SelectedSelectionKeySetSelector (io.netty.channel.nio)
select:879, NioEventLoop (io.netty.channel.nio)
run:526, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
生成文件描述符
:130, ServerSocketChannelImpl (sun.nio.ch)
:109, ServerSocketChannelImpl (sun.nio.ch)
openServerSocketChannel:72, SelectorProviderImpl (sun.nio.ch)
newChannel:63, NioServerSocketChannel (io.netty.channel.socket.nio)
:89, NioServerSocketChannel (io.netty.channel.socket.nio)
:82, NioServerSocketChannel (io.netty.channel.socket.nio)
:75, NioServerSocketChannel (io.netty.channel.socket.nio)
newInstance0:-1, NativeConstructorAccessorImpl (jdk.internal.reflect)
newInstance:77, NativeConstructorAccessorImpl (jdk.internal.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (jdk.internal.reflect)
newInstanceWithCaller:499, Constructor (java.lang.reflect)
newInstance:480, Constructor (java.lang.reflect)
newChannel:44, ReflectiveChannelFactory (io.netty.channel)
initAndRegister:310, AbstractBootstrap (io.netty.bootstrap)
doBind:272, AbstractBootstrap (io.netty.bootstrap)
bind:268, AbstractBootstrap (io.netty.bootstrap)
bind:253, AbstractBootstrap (io.netty.bootstrap)
init:58, DemoApplicationTests2 (com.example.demo)
main:33, DemoApplicationTests2 (com.example.demo)
io.netty.channel.nio.NioEventLoop#selectedKeys与WEPollSelectorImpl中的selectedKeys是同一个对象。netty中会通过反射的方式将io.netty.channel.nio.NioEventLoop#selectedKeys设置到WEPollSelectorImpl中的selectedKeys。
run:228, NioEventLoop$4 (io.netty.channel.nio)
executePrivileged:776, AccessController (java.security)
doPrivileged:318, AccessController (java.security)
openSelector:213, NioEventLoop (io.netty.channel.nio)
:146, NioEventLoop (io.netty.channel.nio)
newChild:183, NioEventLoopGroup (io.netty.channel.nio)
newChild:38, NioEventLoopGroup (io.netty.channel.nio)
:84, MultithreadEventExecutorGroup (io.netty.util.concurrent)
:60, MultithreadEventExecutorGroup (io.netty.util.concurrent)
:52, MultithreadEventLoopGroup (io.netty.channel)
:97, NioEventLoopGroup (io.netty.channel.nio)
:92, NioEventLoopGroup (io.netty.channel.nio)
:73, NioEventLoopGroup (io.netty.channel.nio)
:53, NioEventLoopGroup (io.netty.channel.nio)
init:39, DemoApplicationTests2 (com.example.demo)
main:33, DemoApplicationTests2 (com.example.demo)
因为io.netty.channel.nio.NioEventLoop#selectedKeys与WEPollSelectorImpl中的selectedKeys是同一个对象,所以loop阻塞结束后,可以直接使用io.netty.channel.nio.NioEventLoop#selectedKeys获取WEPollSelectorImpl的结果
NioEventLoop中会在selector上等待事件,当有任务添加时,会唤醒等待中的selector。
debug客户端发送数据的过程。在这个过程中主线程调用writeAndFlush将"写数据"这个任务添加到channel所属的loop中,添加前loop在selector上等待事件,添加后selector停止等待事件。要研究的就是添加过程中主线程调用哪个方法导致loop中的selector停止等待。
客户端代码
package com.example.demo;
import com.example.demo.demos.service.NettyHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledHeapByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoApplicationTests {
public static void main(String[] args) throws Throwable {
NioEventLoopGroup group = new NioEventLoopGroup(1);
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast("business", new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("receive msg");
super.channelRead(ctx, msg);
}
});
}
});
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9004);
Channel channel = channelFuture.channel();
while (true) {
Thread.sleep(10000);
channel.writeAndFlush(Unpooled.copiedBuffer("aaa".getBytes()));
}
}
}
主线程唤醒在selector上等待事件的loop的堆栈
wakeup:73, SelectedSelectionKeySetSelector (io.netty.channel.nio)
wakeup:853, NioEventLoop (io.netty.channel.nio)
execute:857, SingleThreadEventExecutor (io.netty.util.concurrent)
execute0:827, SingleThreadEventExecutor (io.netty.util.concurrent)
execute:817, SingleThreadEventExecutor (io.netty.util.concurrent)
safeExecute:1165, AbstractChannelHandlerContext (io.netty.channel)
write:972, AbstractChannelHandlerContext (io.netty.channel)
writeAndFlush:934, AbstractChannelHandlerContext (io.netty.channel)
writeAndFlush:984, AbstractChannelHandlerContext (io.netty.channel)
writeAndFlush:1025, DefaultChannelPipeline (io.netty.channel)
writeAndFlush:306, AbstractChannel (io.netty.channel)
main:49, DemoApplicationTests (com.example.demo)
虽然在主线程调试,但我在loop的线程中也添加了断点,这样可以第一时间知道loop是否停止等待
调用"writeAndFlush"会调用wakeup唤醒loop,此时还没调用wakeup,loop还没被唤醒。
有两种常见的触发写操作的方式,一种是通过context,另一种是通过channel。通过context触发的写操作不会遍历责任链中每一个节点;通过channel触发的写操作会遍历责任链中每一个节点。
package com.example.demo;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DemoApplicationTests {
public static void main(String[] args) throws Throwable {
NioEventLoopGroup group = new NioEventLoopGroup(1);
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast("out1", new ChannelOutboundHandlerAdapter() {
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
System.out.println("out1 flush");
super.flush(ctx);
}
})
.addLast("business", new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("receive msg");
super.channelRead(ctx, msg);
}
})
.addLast("out2", new ChannelOutboundHandlerAdapter() {
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
System.out.println("out2 flush");
super.flush(ctx);
}
});
}
});
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9004);
Channel channel = channelFuture.channel();
while (true) {
Thread.sleep(10000);
channel.writeAndFlush(Unpooled.copiedBuffer("aaa".getBytes()));
// ChannelHandlerContext tailContext = channel.pipeline().lastContext();
// tailContext.writeAndFlush(Unpooled.copiedBuffer("aaa".getBytes()));
}
}
}
channel触发写操作,经过了所有outHandler节点
write:524, SocketChannelImpl (sun.nio.ch)
doWrite:415, NioSocketChannel (io.netty.channel.socket.nio)
flush0:931, AbstractChannel$AbstractUnsafe (io.netty.channel)
flush0:354, AbstractNioChannel$AbstractNioUnsafe (io.netty.channel.nio)
flush:895, AbstractChannel$AbstractUnsafe (io.netty.channel)
flush:1372, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeFlush0:921, AbstractChannelHandlerContext (io.netty.channel)
invokeFlush:907, AbstractChannelHandlerContext (io.netty.channel)
flush:893, AbstractChannelHandlerContext (io.netty.channel)
flush:125, ChannelOutboundHandlerAdapter (io.netty.channel)
flush:40, DemoApplicationTests$1$3 (com.example.demo)
invokeFlush0:925, AbstractChannelHandlerContext (io.netty.channel)
invokeFlush:907, AbstractChannelHandlerContext (io.netty.channel)
flush:893, AbstractChannelHandlerContext (io.netty.channel)
flush:125, ChannelOutboundHandlerAdapter (io.netty.channel)
flush:54, DemoApplicationTests$1$1 (com.example.demo)
invokeFlush0:925, AbstractChannelHandlerContext (io.netty.channel)
invokeWriteAndFlush:941, AbstractChannelHandlerContext (io.netty.channel)
run:1247, AbstractChannelHandlerContext$WriteTask (io.netty.channel)
runTask:174, AbstractEventExecutor (io.netty.util.concurrent)
safeExecute:167, AbstractEventExecutor (io.netty.util.concurrent)
runAllTasks:470, SingleThreadEventExecutor (io.netty.util.concurrent)
run:569, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
context触发写操作,只经过了一个节点
write:524, SocketChannelImpl (sun.nio.ch)
doWrite:415, NioSocketChannel (io.netty.channel.socket.nio)
flush0:931, AbstractChannel$AbstractUnsafe (io.netty.channel)
flush0:354, AbstractNioChannel$AbstractNioUnsafe (io.netty.channel.nio)
flush:895, AbstractChannel$AbstractUnsafe (io.netty.channel)
flush:1372, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeFlush0:921, AbstractChannelHandlerContext (io.netty.channel)
invokeFlush:907, AbstractChannelHandlerContext (io.netty.channel)
flush:893, AbstractChannelHandlerContext (io.netty.channel)
flush:125, ChannelOutboundHandlerAdapter (io.netty.channel)
flush:33, DemoApplicationTests$1$3 (com.example.demo)
invokeFlush0:925, AbstractChannelHandlerContext (io.netty.channel)
invokeWriteAndFlush:941, AbstractChannelHandlerContext (io.netty.channel)
run:1247, AbstractChannelHandlerContext$WriteTask (io.netty.channel)
runTask:174, AbstractEventExecutor (io.netty.util.concurrent)
safeExecute:167, AbstractEventExecutor (io.netty.util.concurrent)
runAllTasks:470, SingleThreadEventExecutor (io.netty.util.concurrent)
run:569, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)
数据发送之后需要清理空间
remove:257, ChannelOutboundBuffer (io.netty.channel)
removeBytes:352, ChannelOutboundBuffer (io.netty.channel)
doWrite:421, NioSocketChannel (io.netty.channel.socket.nio)
flush0:931, AbstractChannel$AbstractUnsafe (io.netty.channel)
flush0:354, AbstractNioChannel$AbstractNioUnsafe (io.netty.channel.nio)
flush:895, AbstractChannel$AbstractUnsafe (io.netty.channel)
flush:1372, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeFlush0:921, AbstractChannelHandlerContext (io.netty.channel)
invokeFlush:907, AbstractChannelHandlerContext (io.netty.channel)
flush:893, AbstractChannelHandlerContext (io.netty.channel)
flush:125, ChannelOutboundHandlerAdapter (io.netty.channel)
flush:33, DemoApplicationTests$1$3 (com.example.demo)
invokeFlush0:925, AbstractChannelHandlerContext (io.netty.channel)
invokeWriteAndFlush:941, AbstractChannelHandlerContext (io.netty.channel)
run:1247, AbstractChannelHandlerContext$WriteTask (io.netty.channel)
runTask:174, AbstractEventExecutor (io.netty.util.concurrent)
safeExecute:167, AbstractEventExecutor (io.netty.util.concurrent)
runAllTasks:470, SingleThreadEventExecutor (io.netty.util.concurrent)
run:569, NioEventLoop (io.netty.channel.nio)
run:997, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:842, Thread (java.lang)