BIO通信
一请求一应答,对于后端创建的线程太多,不好
BIO 阻塞同步
伪异步IO通信
由线程池来统一处理客户端的接入,当大量请求时会出现线程池阻塞的问题
伪异步IO 阻塞同步
NIO通信
NIO 非阻塞同步
缓冲区Buffer
通道Channel
多路复用Selector
AIO通信
AIO 非阻塞异步
连续注册读写事件和回调函数
读写方法异步
主动通知程序
Netty入门
原生NIO的缺陷
类库和API繁杂
入门门槛高
工作量和难度大
JDK NIO 存在Bug
Netty的优势
API简单
入门门槛低
性能高
成熟、稳定
WebSocket入门
什么是WebSocket?
H5协议规范
握手机制
解决客户端与服务端实时通信而产生的技术
WebSocket的优点
节省通信开销
服务器主动传送数据给客户端
实时通信
WebSocket建立连接
客户端发起握手请求
服务端响应请求
连接建立
WebSocket生命周期
打开事件
消息事件
错误事件
关闭事件
WebSocket关闭连接
服务器关闭底层TCP连接
客户端发起TCP Close
NIO
NIO的三个重要组成部分:Channel(通道),Buffer(缓冲区),Selector(选择器)
Channel ( 通道 ),顾名思义,就是通向什么的道路,为某个提供了渠道。
传统IO中,Stream是单向的,比如InputStream只能进行读取操作,OutputStream只能进行写操作。
而Channel是双向的,既可用来进行读操作,又可用来进行写操作。
具体的常见实现通道有FileChannel,SocketChanel,ServerSocketChannel,DatagramChannel等
Buffer(缓冲区),是NIO中非常重要的一个东西,实际上就是一个容器,是一个连续数组。在NIO中所有数据的读和写都离不开Buffer。在NIO中,读取的数据只能放在Buffer中。同样地,写入数据也是先写入到Buffer中。
要想使用Channel(通道)传递数据,必须先把数据丢进Buffer(缓冲区,容器)里。
在NIO中,Buffer是一个顶层父类,它是一个抽象类,常用的Buffer的子类有:
ByteBuffer,IntBuffer,CharBuffer,LongBuffer,DoubleBuffer,FloatBuffer,ShortBuffer等
Selector , 可以说它是NIO中最关键的一个部分,Selector的作用就是用来轮询每个注册的Channel,一旦发现Channel有注册的事件发生,便获取事件然后进行处理。
以前传统socket编程时,accept方法会一直阻塞,直到有客户端请求的到来,并返回socket进行相应的处理。
NIO则为我们提供了更好的解决方案,Selector选择器能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。并且是按顺序处理,基于通道(Channel)和缓冲区(Buffer)来传输和保存数据。
与Selector有关的一个关键类是SelectionKey,一个SelectionKey表示一个到达的事件,这2个类构成了服务端处理业务的关键逻辑。
Buffer:与Channel进行交互,数据是从Channel读入缓冲区,从缓冲区写入Channel中的
flip方法 : 反转此缓冲区,将position给limit,然后将position置为0,其实就是切换读写模式
clear方法 :清除此缓冲区,将position置为0,把capacity的值给limit。
rewind方法 : 重绕此缓冲区,将position置为0
Netty的特点?
一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持
使用更高效的socket底层,对epoll空轮询引起的cpu占用飙升在内部进行了处理,避免了直接使用NIO的陷阱,简化了NIO的处理方式。
采用多种decoder/encoder 支持,对TCP粘包/分包进行自动化处理
可使用接受/处理线程池,提高连接效率,对重连、心跳检测的简单支持
可配置IO线程数、TCP参数, TCP接收和发送缓冲区使用直接内存代替堆内存,通过内存池的方式循环利用ByteBuf
通过引用计数器及时申请释放不再引用的对象,降低了GC频率
使用单线程串行化的方式,高效的Reactor线程模型
大量使用了volitale、使用了CAS和原子类、线程安全类的使用、读写锁的使用
Netty的线程模型?
Netty通过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件,由对应的Handler处理。
Selector BUG:若Selector的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%,Netty的解决办法:对Selector的select操作周期进行统计,每完成一次空的select操作进行一次计数,若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug。重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,重新注册到新的Selector上,并将原来的Selector关闭。
Handlerhandler和child
在服务端的ServerBootstrap中增加了一个方法childHandler,它的目的是添加handler,用来监听已经连接的客户端的Channel的动作和状态。handler在初始化时就会执行,而childHandler会在客户端成功connect后才执行,这是两者的区别。