https://github.com/wuyinxian124/nettybook2.git 使用com.phei.netty.frame.delimiter.EchoServer做实验
使用这个工程时要处理一下pom的包冲突,否则调试的时候回显"alternative"之类让你选择代码的操作
重点参考文章:

一、主要对象

一般来说,server有boss线程和work线程,client只有work线程。boss和work之间的区别在于监听的事件不同。boss用于监听远程发起的新连接,一旦检测到有新连接就会向work线程池中注册新的需要监听的事件(其实就是channel);work线程用于处理read事件,一接收到read事件分两步走:1、将远程数据读取到buff中 2、将buff传递到pipeline的调用链中。

grpc框架和netty哪个性能好_数据


上图中是一些比较关键的对象,任意两个有颜色的对象都可以相互调用(直接或间接)。下面简要说明一下这些对象。

名称

说明

NioEventLoop

每一个boss或者work线程的死循环对象,负责selector的轮询 IO独写 taskQueue中任务的处理

selector

每个NioEventLoop中都有各自的selector,用于实现多路选择器

selectionKeyImpl

selector中保存有三个比较重要的数组:keys selectedKeys cancelKeys,数组中每个对象就是这个类

NioServerSocketChannel

每个selectionKeyImpl有一个通道信息。ps:监听8080是一个通道,监听12345->8080也是一个通道

DefaultChannelPipeline

每个通道都有一个pipeline

DefaultChannelHandlerContext

实现链式调用的关键

有关链式调用,可参考此文章 服务端启动流程可参考此文章

二、比较关键的调试断点

线程创建
1、io.netty.util.concurrent.ThreadPerTaskExecutor#execute创建新线程
selector中key的变化

名称

说明

keys创建

SelectorImpl(WindowsSelectorImpl的父类)中初始化

keys添加

sun.nio.ch.WindowsSelectorImpl#implRegister

selectedKeys创建

SelectorImpl中初始化(最初初始化时是hashset,之后会被替换为其他类)

selectedKeys添加

sun.nio.ch.WindowsSelectorImpl$SubSelector#processFDSet

若想比较boss和work线程最初keys初始化流程,可参考此文章

work线程读取远程数据
io.netty.channel.socket.nio.NioSocketChannel#doReadBytes

@Override
    protected int doReadBytes(ByteBuf byteBuf) throws Exception {
        return byteBuf.writeBytes(javaChannel(), byteBuf.writableBytes());
    }

work线程读取远程数据的详细过程可参考此文章

work线程写数据到远程
io.netty.channel.socket.nio.NioSocketChannel#doWriteBytes

@Override
    protected int doWriteBytes(ByteBuf buf) throws Exception {
        final int expectedWrittenBytes = buf.readableBytes();
        final int writtenBytes = buf.readBytes(javaChannel(), expectedWrittenBytes);
        return writtenBytes;
    }

不同事件处理
NioEventLoop#processSelectedKey

//已省略大部分代码
private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final NioUnsafe unsafe = ch.unsafe();
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                ch.unsafe().forceFlush();
            }
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);
                unsafe.finishConnect();
            }
    }

任务(taskQueue)的生产与消费

名称

说明

生产

SingleThreadEventExecutor#fetchFromDelayedQueue,addTask,wakeup

消费

SingleThreadEventExecutor#pollTask,takeTask

用途:
1、boss和work线程运行之初,都会被创建其线程的父线程添加AbstractChannel$AbstractUnsafe#register中的一个任务(这个任务中会添加新的key)。boss线程还会被主线程添加DefaultPromise#notifyListener中的一个任务(这个任务中会继续添加一些其他任务)。

主线程向boss线程添加AbstractChannel$AbstractUnsafe#register的详细过程可参考此文章

三、重要的底层调用

selector

基本概念及使用方法

参考:

1 通过 Selector.open() 打开一个 Selector.
2 将 Channel 注册到 Selector 中, 并设置需要监听的事件(interest set)
3 不断重复:
3.1 调用 select() 方法
3.2 调用 selector.selectedKeys() 获取 selected keys
3.3 迭代每个 selected key:
3.4 从 selected key 中获取 对应的 Channel 和附加信息(如果有的话)
3.5 判断是哪些 IO 事件已经就绪了, 然后处理它们. 如果是 OP_ACCEPT 事件, 则调用 “SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept()” 获取 SocketChannel, 并将它设置为 非阻塞的, 然后将这个 Channel 注册到 Selector 中.
3.6 根据需要更改 selected key 的监听事件.
3.7 将已经处理过的 key 从 selected keys 集合中删除.

在netty中的应用
1、windows下使用WindowsSelectorImpl实现selector
2、NioEventLoop在NioEventLoop#openSelector中创建了selector,并将原有的HashSet类型的selectedKeys更换为SelectedSelectionKeySet类型

ByteBuf读写数据

待续
此处应该讲一下缓冲池机制

四、待解决问题

链式调用这篇文章应该重写 done
收发数据这篇文章应该重写
服务端启动流程这篇文章应该重写
netty boss和work的keys初始化过程比较 重写