欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Netty

程序员文章站 2022-07-14 12:17:46
...

Java 的AIO

重要的三个类:AsynchronousServerSocketChannel(服务端)、AsynchronousSocketChannel(客户端)和CompletionHandler(用户处理器)

CompletionHandler接口实现应用程序向操作系统发起IO操作

Buffer的基本用法就是:
初始化(allocate)–> 写入数据(read / put)–> 转换为写出模式(flip)–> 写出数据(get)–> 转换为写入模式(compact)–> 写入数据(read / put)…

五种IO通信模型:

  1. 阻塞IO

  2. 非阻塞IO

  3. 多路复用IO(selector 复用器)重点

    ​ 本质和阻塞IO类似,相较于阻塞IO更适合用于处理多连接高并发的情形,常见于:Java NIO、Nginx;

  4. 信号驱动IO

​ 伪异步

  1. 异步IO

​ 适合用于高性能高并发应用

同步和异步

​ 同步和异步是指CPU时间片的利用,主要看请求发起方对结果的获取是主动还是被动的;

​ 发起结果获取请求后,

​ 同步阻塞:一直等待应答

​ 同步非阻塞:可以先处理其他事情,但要不断轮询结果

​ 异步阻塞:等待系统通知结果,但是同时自己也一直在等通知

​ 异步非阻塞:处理其他事情,系统处理完成后通知自己

BIO (Blocking IO)

NIO (Non-Blocking IO)

NIO其实也是阻塞的:只不过他阻塞的时间段只在于数据从内核态到用户态的拷贝过程,是面向缓存区来设计的

BIO是面向流的,NIO是面向缓冲区的;

面向流:数据在一个流上,直来直去的,中间不间断顺序也不能乱,从头到尾都必须一直等待;

面向缓冲区:数据将被装到一个缓冲区里,就像先翻一个桶在哪里,当水龙头里面的水来了之后,就会自动装到这个桶里,这里就有些复 杂的情况了:1.当水太少,装不满桶,不够用;2.对自己需要的水量预估不足或者额外多了一些水进来,最后我们都不能得到我们想要的结果,最终我们只能不都拿的去检查水桶里的水是否足够,这其实花费了更多的开销

Java 1.7 的NIO2 才真正实现了异步IO,即AIO

Mina & Netty

都是韩国大神Trustin Lee的作品

对比:两者有很多特性都一样

Mina依赖于Apache

Netty依赖于JBoss

一般使用中两者性能相差无几;

概念

Netty三大核心组件:缓冲区Buffer、选择器Selector、通道Channel

  • Buffer

position:指定下一个将要读取或者写入的位置,初始值为0,又put()或get()自动维护

limit:指定还有多少数据需要读取或者还有多少空间能够被写入

capacity:缓冲区的容量,即底层数组的大小;

flip():重设缓冲区,将当前位置设为限制值,然后将当前位置设为0;

hasRemaining():查看当前位置和限制值之间是否有元素;

remaining():返回剩余元素个数;

alloctate():初始化一个指定大小的缓冲区;

wrap():将现有数组包装为缓冲区;

slice():分片,截取缓冲区的一个窗口,方便操作,大小为从postion到limit;

asReadOnlyBuffer():只读缓冲区;

allocateDirect():直接缓冲区,尽量避免中间copy操作,使得IO速度更快;

MappedByteBuffer:内存映射

  • Selector

selector是注册各种IO事件的地方,当这些事件发生之后,它就会告知我们发生的所有事件;

每个事件有对应的selectionKey ;

selectionKey 对应了事件发生的通道(Channel);

通过selectionKey 拿到Channel我们就可以进行数据读写等操作;

NIO编程基本就是分三步:

  1. 向Slector注册感兴趣的事件;

  2. 从Selector中读取感兴趣的事件;

  3. 处理事件

  • Channel

它是一个对象,通过它可以读取或者写入数据;当然所有数据都是通过Buffer对象来处理,我们永远不会将字节直接写进Channel,而是先写进缓冲区,读取也是同理;

使用NIO读取分为三步:

  1. 从Stream中获取Channel;
  2. 创建Buffer;
  3. 将数据从Channel读取到Buffer;

类似的,写入也是三步:

  1. 从Stream中获取Channel;
  2. 创建Buffer;
  3. 将数据从Buffer写入到Channel;
  • 多路复用

主要包括四种:select、poll、epoll、kqueue

对于java而言,windows下仅支持select一种,这种技术特别适合高并发场景(1ms上千请求);其他场景这一设计模式并不能发挥优势;

文件描述:FileDescriptor,IO通过这个列来操作文件

IOUtil.makePipe()

Netty和NIO

Netty采用NIO而非AIO的理由

  1. Netty不看重windows上的使用,在Linux上,AIO底层仍使用epoll,没有很好的实现AIO, 性能上么有明显优势;
  2. Netty采用Reactor模型,而AIO采用Proactor;
  3. AIO有个缺点就是:接收数据需要预先分配缓存,而NIO是需要接收时才分配缓存,因此对于对连接数量非常大但流量小的情况,AIO浪费了很多内存;

PipeLine是Netty的职责链,用来存放各种handler;

context 用于封装hander,方便操作;

每个hander都在单独线程执行;

handelr主要分为inbound(入站)和outbound(出站),执行顺序遵循:inbound先add的先执行,而outbound先add后执行。

Bootstrap

Bootstrap是一个用来初始化Netty的工厂类;

  1. handler()和childHandler()的主要区别是,handler()是发生在初始化的时候,childHandler()是发生在客户端连接之后

    也就是说,如果需要在客户端连接前的请求进行handler处理,则需要配置handler(),如果是处理客户端连接之后的handler,则需要配置在childHandler()。

  2. 对于ChannelOption.SO_BACKLOG的解释

服务器端TCP内核模块维护有2个队列,我们称之为A,B吧
客户端向服务端connect的时候,发送带有SYN标志的包(第一次握手)
服务端收到客户端发来的SYN时,向客户端发送SYN ACK 确认(第二次握手)
此时TCP内核模块把客户端连接加入到A队列中,然后服务器收到客户端发来的ACK时(第三次握手)
TCP没和模块把客户端连接从A队列移到B队列,连接完成,应用程序的accept会返回
也就是说accept从B队列中取出完成三次握手的连接
 
A队列和B队列的长度之和是backlog,当A,B队列的长之和大于backlog时,新连接将会被TCP内核拒绝
所以,如果backlog过小,可能会出现accept速度跟不上,A.B 队列满了,导致新客户端无法连接,
要注意的是,backlog对程序支持的连接数并无影响,backlog影响的只是还没有被accept 取出的连接