ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Netty基本概念

2022-01-09 22:32:40  阅读:180  来源: 互联网

标签:Netty 触发 socket epoll IO 就绪 基本概念


Netty是什么?

是一个网络通信框架

能做什么?

绝大多数的网络通信Netty都能做,BIO的服务端与客户端通信,NIO的服务端网络通信

为什么要用Netty?

一般对于NIO来说,jdk提供的NIO实现是真的有点复杂,很原生,还有点bug,其中最难顶的就是空selector导致的cpu过高问题,Netty是指在减少NIO的开始技术难度,即使不太熟悉的NIO的开发者,只要按照Netty的规范去写代码也能写出很棒的并发通信应用。

Netty的几个基本组件:

ByteBuf:对标JDK的ByteBuffer,是一个操作序列,可以说是缓冲区,零拷贝实现的一个重要组件

Channel:对标JDK的Channel,通道,通信的核心类包装了一个unsafe,操作IO的工具或方法需要注意的是IO不是写进channel的,而是缓冲区

ChannelHandler:用户自定义的IO数据处理实现,有channelRead、register等IO通知方法,方便我们在各种IO事件后做相应的业务操作。

如:

1. 注册事件 fireChannelRegistered。

2. 连接建立事件 fireChannelActive。

3. 读事件和读完成事件 fireChannelRead、fireChannelReadComplete。

4. 异常通知事件 fireExceptionCaught。

5. 用户自定义事件 fireUserEventTriggered。

6. Channel 可写状态变化事件 fireChannelWritabilityChanged。

7. 连接关闭事件 fireChannelInactive。

EventLoop:还有另一个概念EventLoopGroup,在初始化netty时我们会定义两个线程组,一个负责IO事件的分发,另一个负责IO的事的处理,但要注意的是每个Channel都会绑定一个EventLoop。确保在单线程中完成一次IO事件

bootstrap:netty的引导器,负责引导Netty程序的启动

还有一个概念引自一位大佬的知乎:面试系列 深入理解NIO select&epoll - 知乎

为什么会出现epoll,它的背景是什么

select和poll函数的缺陷是:

  1. 函数调用参数拷贝问题:这两系统函数每次调用都需要我们提供给他所有需要监听的socket文件描述符集合,而且我们主线程是死循环调用select/poll函数,这涉及到用户空间数据到内核空间的拷贝过程,这个操作比较耗费性能。其实我们的fd集合是比较稳定的,可能它每次只有1-2个socket_fd需要更改,但是我们每次都需要传整个。因为select/poll没有在kernel层面保留任何数据信息,所以说每次调用都需要进行数据拷贝。
  2. 系统调用返回后不知道哪些socket就绪问题:select和poll函数它的返回值是个int整型,只能代表有几个socket就绪了,导致我们程序被唤醒之后,还需要新一轮的系统调用,去检查哪个socket是就绪状态的

怎么解决,epoll怎么设计的呢?

需要epoll函数在内核空间内,它有一个对应的数据结构去存储一些数据。这个数据结构实际上就是eventpoll对象,eventpoll的结构,主要是三块重要的区域,一块是检查列表,存放需要监听的socket_fd监听符,红黑树,因为这个socket集合信息经常会有增删改查的需求,保持了时间复杂度为O(logN)

另一块就是就绪列表,存放就绪状态的socket信息,双向链表。 另一块是等待队列,把调用epollwait函数的进程放到这里面去。

它主要提供了两个函数,epoll_ctl函数,它负责维护检查列表,可以根据eventpoll-id去增加删除改动需要检查的socket文件描述符。 epoll_wait函数它主要参数是eventpoll-id,表示此次系统调用需要监测的socket_fd集合是eventpoll里面的信息。默认情况下它会阻塞调用线程,直到eventpoll中关联的某个socket就绪以后,epoll_wait函数才会返回。

怎么维护就绪队列

靠的是中断程序,对等待队列和就绪队列进行操作

epoll_wait返回0表示没有就绪socket,大于0表示有几个就绪socket,它怎么解决第二个识别就绪socket问题的呢。

epollwait函数调用的时候,会传入一个epoll_event事件数组指针,epoll_wait函数正常返回之前会把就绪的socket事件信息拷贝到这个数组指针里面。上层程序就可以通过这个数组拿到就绪的socket列表。所以它的时间复杂度是O(1)

epoll_wait函数可以设置成非阻塞的 吗?

默认是阻塞的,但可以设置阻塞时间,如果设置为0表示非阻塞,每次调用都会去检查就绪列表

检查队列采用的数据结构是什么?

红黑树,因为这个socket集合信息经常会有增删改查的需求

epoll_wait阻塞期间另一个线程执行epoll_ctl是否安全

  • 添加:没问题,另一个线程增加fd进监听列表,epoll_wait是可以监听到的
  • 删除:官方描述的话,是部分unix系统关闭socket会被认为是就绪,从而读写失败报错,部分是没问题
  • 删除再打开:如果关闭了socket就相当于把它从红黑树监听列表移除了,必须再次添加才能被监听

边缘触发和水平触发的区别

  • 水平触发:如果文件描述符已经就绪可以非阻塞的执行IO操作了,此时会触发通知.允许在任意时刻重复检测IO的状态.select,poll就属于水平触发.
    • 简单来说只要缓冲区还有内容,就会返回读就绪。
  • 边缘触发:如果文件描述符自上次状态改变后有新的IO活动到来,此时才会触发通知.在收到一个IO事件通知后要尽可能多的执行IO操作,因为如果在一次通知中没有执行完IO那么就需要等到下一次新的IO活动到来才能获取到就绪的描述符.
    • 简单来说,只有当缓冲区内容发生变化时,才会返回读就绪

使用场景

epoll默认是水平触发,优点是保证数据完整性,会一直通知你,缺点是这种通知涉及内核态到用户态的切换,会浪费一定性能。

边缘触发,优点是每次内核只会通知一次,提高效率,缺点是不能保证数据的完整性,不一定能及时取出所有数据。一般来说如果处理大数据的情况下会使用边缘触发。

一个管道收到了1kb的数据,epoll会立即返回,此时读了512字节数据,然后再次调用epoll.这时如果是水平触发的,epoll会立即返回,因为有数据准备好了.如果是边缘触发的不会立即返回,因为此时虽然有数据可读但是已经触发了一次通知,在这次通知到现在还没有新的数据到来,直到有新的数据到来epoll才会返回,此时老的数据和新的数据都可以读取到(当然是需要这次你尽可能的多读取).

标签:Netty,触发,socket,epoll,IO,就绪,基本概念
来源: https://blog.csdn.net/q568213372/article/details/122400248

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有