[笔记]netty随笔

记录使用过程中偶然发现的一些关键逻辑。先做记录,以后netty知识有一定体系再做整理

childGroup

服务器中有俩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里
[笔记]netty随笔_第1张图片

[笔记]netty随笔_第2张图片

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

Selector

使用"轮询+同步阻塞"的方式处理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)

[笔记]netty随笔_第3张图片
阻塞状态结束后,io.netty.channel.nio.NioEventLoop#processSelectedKeysOptimized中会遍历io.netty.channel.nio.NioEventLoop#selectedKeys获取需要处理的事件

从NioEventLoop#selectedKeys取出的key的channel是netty中的类?

阻塞结束后用fd找到相应的channel然后进行处理?

loop中的任务如何判断使用哪个channel的pipeline?

WEPoll

github:https://github.com/piscisaureus/wepoll
貌似只有jdk17才会用到这个组件,jdk8不会用。

create

创建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)

[笔记]netty随笔_第4张图片

ctl

向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)

[笔记]netty随笔_第5张图片

wait

等待事件发生

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
[笔记]netty随笔_第6张图片

WEPoll的抽象层

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

[笔记]netty随笔_第7张图片

parent在哪里将channel移交给child的selector?

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)

设置att

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)

[笔记]netty随笔_第8张图片

上个截图中使用javaChannel获取到java自带的channel,然后用这个自带的channel生成了一个key。其中有几点需要注意。
1、原生channel(this)+监听的事件(ops)+nettyChannel(att)+selector(sel)=key,同时这个key也会反过来注册到当前的原生channel上(addKey(k))
[笔记]netty随笔_第9张图片

2、若当前原生channel已经注册到selector中,就不要重复生成了。findKey中会遍历当前原生channel的keys
[笔记]netty随笔_第10张图片

[笔记]netty随笔_第11张图片
这也间接说明一个channel可以绑定在多个selector上,但是同一个selector上不能绑定多个相同的channel,即使ops不同也不行

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)

fd

生成文件描述符

: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)

设置selectedKeys

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)

[笔记]netty随笔_第12张图片

因为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是否停止等待
[笔记]netty随笔_第13张图片

调用"writeAndFlush"会调用wakeup唤醒loop,此时还没调用wakeup,loop还没被唤醒。
[笔记]netty随笔_第14张图片

主线程调用完wakeup
[笔记]netty随笔_第15张图片

loop线程停止等待
[笔记]netty随笔_第16张图片

写操作

触发方式&责任链

有两种常见的触发写操作的方式,一种是通过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)

你可能感兴趣的:(笔记,java,linux)