netty4核心源码分析第一篇一前置篇

epoll模型原理图

  • 调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
  • 调用epoll_ctl向event_epoll对象的红黑树添加相关节点,并指明对哪些事件感兴趣[比如netty层面的accept事件,读写事件等]
  • 红黑树中的每个节点会与网卡建立回调机制,当网卡发生相应感兴趣事件,则将相关事件对应的节点加入下图的list_head双向链表中
  • 调用epoll_wait获取双向链表中[事件集合][netty层面的selector,select]

优点: 无需遍历红黑树,从双向链表中立刻获取到网卡所有被激活的channel,通过空间换时间

在这里插入图片描述

水平触发[LT]与边缘触发[ET]

  • 水平触发.发生网络事件,不处理之后会继续触发
  • 边缘触发 发生网络事件,不处理之后不会继续触发

在这里插入图片描述

reactor线程模型

单线程模型

在这里插入图片描述

多线程模型

在这里插入图片描述
在这里插入图片描述

主从线程模型

在这里插入图片描述在这里插入图片描述

reactor在netty配置双eventLoopGroup的思考

参考笔者在其他博客下的留言
netty学习之旅系列文章
根据netty权威指南中对reactor线程模型的定义,我更倾向于netty使用了多线程模型而非主从模型,当然这只是理论性的知识点,实际还要是掌握netty自身的运行机制,只是理解不同,并无对错之分

NIO编程实战

关键代码作用
serverSocketChannel.configureBlocking(false)IO多路复用必须是非阻塞模式
selectedKey.channel或read处理IO事件或者连接等感兴趣事件,由于底层模式为水平触发,不处理事件会导致事件每次select都会获取到
selectedKeys.remove()原因如下

因为 select 在事件发生后,就会将相关的 key 放入 selectedKeys 集合,但不会在处理完后从 selectedKeys 集合中移除,需要我们自己编码删除。例如

  • 第一次触发了 ssckey 上的 accept 事件,没有移除 ssckey
  • 第二次触发了 sckey 上的 read 事件,但这时 selectedKeys 中还有上次的 ssckey ,在处理时因为没有真正的 serverSocket 连上了,此时accept返回null在这里插入图片描述
public static void startServer() throws IOException {
    1、获取Selector选择器
    Selector selector = Selector.open();
    2、获取通道
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    3.设置为非阻塞 想要绑定到选择器上面的通道必须为非阻塞模式 否则会报错
    serverSocketChannel.configureBlocking(false);
    4、绑定连接
    serverSocketChannel.bind(new InetSocketAddress(NioDemoConfig.SOCKET_SERVER_PORT));
    Logger.info("服务器启动成功");

    5、将通道注册到选择器上,并注册的IO事件为:“接收新连接”
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

    6、轮询感兴趣的I/O就绪事件(选择键集合)
    while (selector.select() > 0) {
        7、获取选择键集合
        Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
        while (selectedKeys.hasNext()) {
            8、获取单个的选择键,并处理
            SelectionKey selectedKey = selectedKeys.next();

            9、判断key是具体的什么事件
            if (selectedKey.isAcceptable()) {
                10、若选择键的IO事件是“连接就绪”事件,就获取客户端连接
                由于是水平触发这里必须处理,否则下次还需获取到
                SocketChannel socketChannel = /* selectedKey.channel() */serverSocketChannel.accept();
                11、切换为非阻塞模式
                socketChannel.configureBlocking(false);
                12、将该通道注册到selector选择器上
                socketChannel.register(selector, SelectionKey.OP_READ);
            } else if (selectedKey.isReadable()) {
                13、若选择键的IO事件是“可读”事件,读取数据
                SocketChannel socketChannel = (SocketChannel) selectedKey.channel();

                14、读取数据
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                int length = 0;
                while ((length = socketChannel.read(byteBuffer)) >0) {
                    byteBuffer.flip();
                    Logger.info(new String(byteBuffer.array(), 0, length));
                    byteBuffer.clear();
                }
                socketChannel.close();
            }
            15、移除选择键
            selectedKeys.remove();
        }
    }

    关闭连接
    serverSocketChannel.close();
}

总结

  • 本文介于netty源码分析之前,主要使读者了解selectorIO复用的底层模型epoll[省略select和poll模型]
  • 简介了水平触发和边缘触发,netty[epoll]基于水平触发,发生的事件必须处理,否则会一直触发
  • 通过介绍epoll的底层数据结构,阐述空间换时间后epoll高性能之密
  • 介绍reactor线程模型,netty本身可以通过参数配置在单线程,多线程,主从之前切换
  • 介绍NIO编程易错点
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值