• 《Netty权威指南》笔记


    第1章 Java的I/O演进之路
    1.1 Linux网络I/O模型
    fd:file descriptor,文件描述符。linux内核将所有外部设备都看作一个文件来操作,对文件的读写会调用内核提供的命令,返回一个文件描述符。对一个socket的读写也会有相应的socket fd。描述符就是一个指向内核中结构体的数字。
    Unix I/O模型分为5类:
    ①阻塞IO模型
    文件操作的默认模型。进程空间调用recvfrom函数,直到数据包到达且被复制到应用进程的缓冲区中或者发生错误时才返回,在此期间,进程会被阻塞一直等待。
    ②非阻塞IO模型
    recvfrom函数当发现内核缓冲区没有数据时,直接返回一个EWOULDBLOCK错误,一般通过轮询检查这个状态,看是否有数据到来。
    ③IO复用模型
    linux提供select/poll,进程通过将一个或多个fd传递给select或poll系统调用,阻塞在select操作上,这样select/poll可以通过顺序扫描多个fd帮我们侦测是否处于就绪状态。但支持的fd数量有限。
    linux还提供了epoll系统调用,基于事件驱动方式代替顺序扫描,性能更高。当有fd就绪时,立即回掉函数rollback。
    ④信号驱动IO模型
    首先开启套接口信号驱动IO功能,并通过系统调用sigaction执行一个非阻塞信号处理函数。当数据准备就绪时,就为该进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,并通知主循环函数处理数据。
    ⑤异步IO模型
    告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。
    这种模型与信号驱动模型的主要区别是:信号驱动IO由内核通知我们何时可以开始一个IO操作;而异步IO模型由内核通知我们IO操作何时已经完成。
     
    1.2 I/O多路复用技术
    java NIO 的核心类库多路复用器Selector就是基于epoll的多路复用技术实现。
    当需要同时处理多个客户端接入请求时,可以利用多线程或IO多路复用技术实现。
    IO多路复用:通过把多个IO的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下也可以同时处理多个客户端请求。
    IO多路复用最大优势:系统开销小。即系统不需要创建新的额外进程或线程,也不需要维护这些这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源。
    IO多路复用主要应用场景:①服务器需要同时处理多个处于监听或连接状态的套接字。②服务器需要同时处理多种网络协议的套接字。
     
    支持IO多路复用的系统调用有select、pselect、poll、epoll。在linux网络编程中,很长时间使用select,但最终选择epoll。用来克服select/poll缺点的方法不只有epoll,epoll只是一种linux实现方案,在freeBSD下有kqueue。
     
    epoll改进的select缺点如下:
    ①支持一个进程打开的socket描述符(FD)不受限制(仅受限于操作系统的最大文件句柄数)。
    select最大缺陷是单个进程打开的FD是有限制的,默认值1024个,太多网络效率会下降。而epoll在1G内存大概支持10万个。
    ②IO效率不会随着FD数目的增加而线性下降。
    因为select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。而epoll只有活跃的socket才会主动调用callback函数,epoll实现了一个伪AIO。极端情况当全部socket都活跃时,epoll和select性能差不多。
    ③使用mmap加速内核与用户空间的消息传递。
    内核需要把FD消息通知给用户空间,epoll通过内核和用户空间mmap同一块内存来避免不必要的内存复制。
    ④epoll的API更加简单。
    包括创建一个epoll描述符、添加监听事件、阻塞等待所监听的事件发生、关闭epoll描述符等。
     
    1.3 Java的I/O演进
    在JDK 1.4推出Java NIO之前,java都是使用同步阻塞模式(BIO),而这一时期C和C++语言的大型应用都直接使用操作系统提供的异步IO或AIO能力。
    JDK 1.4新增了个java.nio包,提供了进行异步IO开发的API和类库,主要类和接口如下:进行异步IO操作的缓冲区ByteBuffer、管道Pipe、各种异步或同步的通道Channel、实现非阻塞IO操作的多路复用器Selector等。但NIO 1.0版仍然存在不足,主要问题为:没有统一的文件属性、API能力比较弱、底层存储系统的一些高级API无法使用、所有文件操作都是同步阻塞调用,不支持异步文件读写操作。
    JDK1.7 对NIO做了升级,被称为NIO 2.0版,主要改进三个方面如下:
    ①提供批量获取文件属性API。
    ②提供AIO功能,支持基于文件的异步IO操作和针对网络套接字的异步操作。
    ③完善通道功能,包括对配置和多播数据报的支持等。
     
     
    第2章 NIO入门
    2.1 传统的BIO编程
    ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。经过三次握手,连接成功之后,Client和Server双方通过输入流和输出流进行同步阻塞式通信。
    同步阻塞IO服务端通信模型:称做一个客户端连接一个线程。通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到连接请求后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。
    该模型最大问题:缺乏弹性伸缩能力,服务端线程数和客户端访问数呈1:1的正比关系,当线程数膨胀,性能急剧下降,导致宕机和僵死。
     
    2.2 伪异步IO编程
    是对传统BIO一个连接一个线程的简单优化,服务端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大线程数N的比例关系,M可以远远大于N。由于底层依然使用同步阻塞IO,所以被称为“伪异步”。
    优点:资源占用是可控的,不会导致耗尽和宕机。
    缺点:只是简单优化,无法从根本上解决同步IO导致的通信线程阻塞问题。
     
    2.3 NIO编程
    官方叫法是New I/O。而被大多数人接受的更准确叫法是非阻塞IO(Non-block I/O)。
    (1)NIO类库简介
    ①缓冲区Buffer
    缓冲区实质是一个数组,提供对数据的结构化访问以及维护读写位置等。所有数据都是用缓冲区处理,任何时候访问NIO中的数据,都是通过缓冲区进行操作。常见缓冲区类有7个:ByteBuffer(最常用)、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。而java bio是面向流操作的。
    ②通道Channel
    Channel是一个全双工的双向通道,可以读写操作同时进行,能更好的映射底层操作系统的API,因为Unix底层操作系统通道都是全双工的。而java bio 流是单向的,一个流必须是InputStream或OutputString的子类。
    Channel可以分为两大类:用于网络多写的SelectableChannel和用于文件操作的FileChannel。ServerSocketChannel和SocketChannel都是SelectableChannel的子类。
    ③多路复用器Selector
    多路复用器提供选择以及就绪的任务的能力。Selector会不断地轮询注册在其上地Channel,如果某个Channel上面发生读或写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的IO操作。JDK使用epoll实现。
     
    (2)NIO编程优缺点
    缺点:NIO编程难度比BIO大很大,编码复杂。
    优点:①客户端发起的连接操作都是异步的,通过在多路复用器注册OP_CONNECT等待后续结果。
    ②SocketChannel的读写操作都是异步的。
    ③线程模型的优化,一个Selector线程可以同时处理成千上万个客户端连接。
     
    2.4 AIO编程
    JDK1.7(NIO 2.0)引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现,是真正的异步IO(因此NIO2.0也称作异步非阻塞IO,而NIO 1.0称作非阻塞IO)。其中异步套接字通道是真正的异步非阻塞IO,对应于Unix网络编程中的事件驱动IO(AIO)。它不需要通过多路复用器Selector对注册的通道进行轮询操作即可实现异步读写,从而简化了NIO的编程模型。主要类有AsynchronousSocketChannel和CompletionHandler(异步操作回调通知接口)。
     
    2.5 四种IO模型对比
     
     
    第3章 TCP粘包和拆包
    3.1 TCP粘包/拆包
    TCP是个“流”协议,所谓流,就是没有界限的一串数据。就像河里的流水,它们连成一片,其间并没有分界线。
    粘包:上层业务的多个小包被封装成一个大的数据包发送。
    拆包:上层业务的一个完整的包被拆分成多个数据包发送。
    3.2 TCP粘包/拆包解决方案
    主要有四种解决方案:
    ①消息定长。例如每个报文的大小为固定长度200字节,如果不够,空位补空格。
    ②在包尾增加回车换行符进行分割。例如FTP协议。
    ③将消息分为消息头和消息体,消息头中包含表示消息总长度的字段。
    ④更复杂的应用层协议。
     
    为了解决TCP粘包/拆包导致的半包读写问题,Netty默认提供了多种编解码器用于处理半包。
     
    第4章 编解码技术
    4.1 Java序列化缺点
    java序列化缺点如下:
    ①无法跨语言
    ②序列化后的码流太大
    ③序列化性能太低
     
    4.2 业界主流编解码框架
    Protobuf:google开源,高效的编解码性能,语言无关和平台无关,文本化的数据结构描述语言。
    Thrift:facebook开源给apache,支持压缩和可选优化等二进制编解码。
     
     
    第5章 HTTP协议
    5.1 HTTP请求消息
    http请求由三部分组成:
    ①HTTP请求行
    请求行以一个方法夫开头,以空格分开,后面跟着URI和协议版本,格式为:Method Request-URI HTTP-Version CRLF(回车和换行)。
    ②HTTP消息头
    ③HTTP请求正文
     
    5.2 HTTP响应消息
    http响应由三部分组成:
    ①状态行
    状态行格式:HTTP-Version Status-Code(状态码) Reason-Phrase CRLF。
    ②消息报头
    ③响应正文
     
    5.3 HTTP协议缺点
    http协议缺点如下:
    ①HTTP协议为半双工协议。
    半双工协议指数据可以在客户端和服务器两个方向传输,但是不能同时传输。即同一时刻,只有一个方向上的数据传送。
    ②HTTP消息冗长而繁琐。
    采用文本方式传输,比二进制通信协议冗长繁琐。
    ③开销大,不适用于低延迟应用。
    容易针对服务器长连接推送的黑客攻击。例如长时间轮询,消耗大量服务器带宽。
     
     
    第6章 WebSocket协议
    6.1 WebSocket入门
    WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通信的网路技术,浏览器和服务器只需要做一个握手动作,然后,浏览器和服务器之间就形成了一条快速通道,两者就可以直接互相传送数据了。
    WebSocket特点如下:
    ①单一的TCP连接,基于TCP的全双工模式通信。
    ②对代理、防火墙和路由器透明。
    ③无头部信息、Cookie和身份验证。
    ④无安全开销。
    ⑤通过“ping/pong”帧保持链路激活。
    ⑥服务器可以主动传递消息给客户端,不再需要客户端轮询。
     
     
    第7章 私有协议栈开发
    7.1 私有协议介绍
    私有协议也称非标准协议,就是未经国际和国家标准化组织采纳和批准,由某个企业自己制定。
    7.2 Netty协议栈开发
    Netty私有协议栈开发主要有如下步骤:
    ①数据结构定义
    ②消息编解码
    ③握手和安全认证
    ④心跳检测机制
    ⑤断连重连
    ⑥客户端代码
    ⑦服务端代码
     
     
    第8章 Reactor线程模型
    8.1 Reactor单线程模型
    是指所有的IO操作都在同一个NIO线程上完成,这个唯一NIO线程的职责如下:
    ①做为NIO服务器,接收客户端的TCP连接。
    ②做为NIO客户端,向服务器发起TCP连接。
    ③读取通信对端的请求或者应答消息。
    ④向通信对端发送消息或者应答消息。
     
    通过Acceptor类接收客户端的TCP连接请求消息,当链路建立成功之后,通过Dispatch将对应的ByteBuffer派发到指定的Handler上,进行消息解码,用户线程消息编码后通过NIO线程将消息发送给客户端。
    单线程模型只适用于小容量应用场景,对于高负载、大并发应用场景不适用,原因如下:
    ①一个NIO线程同时处理成百上千的链路,性能上无法支撑,无法满足海量消息的编码、解码、读取和发送。
    ②当NIO线程负载过重时,处理速度变慢,会导致大量客户端连接超时,而超时重发会进一步加重负载。
    ③可靠性问题,一旦NIO线程崩溃或死循环,会导致整个系统通信模块不可用。
     
    8.2 Reactor多线程模型
    用一组NIO线程来处理IO操作。多线程模型特点如下:
    ①有专门一个NIO线程Acceptor线程用于监听服务端,接收客户端的TCP连接请求。
    ②网路IO操作读写等由一个NIO线程池负责。
     
    不足:单独一个Acceptor线程可能会存在性能不足问题。
    8.3 主从Reactor多线程模型
    用一个Acceptor线程池来处理客户端TCP连接请求,握手,安全认证等。一旦链路建立成功,就将链路注册到后端Reactor线程池的IO线程上,由IO线程负责后续IO操作。
     
     
    第9章 高性能之道
    9.1 传统RPC调用性能分析
    传统RPC调用性能差的三宗罪:
    ①网络传输方式采用同步阻塞I/O。
    ②序列化性能差。
    ③线程模型问题。即1:1占用服务器线程资源。
     
    9.2 IO通信性能三原则
    从架构层面,影响IO通信性能主要有三个要素:传输方式、通信协议、线程模型。
     
    9.3 Netty高性能之道
    netty高性能之道如下:
    ①异步非阻塞通信。
    ②高效的Reactor线程模型。
    ③无锁化的串行设计。
    避免锁竞争带来的性能损耗。
    ④高效的并发编程。
    ⑤高性能的序列框架。
    netty默认提供了对google Protobuf的支持。
    ⑥零拷贝。
    netty的接收和发送byteBuffer采用Direct Buffer,使用堆外直接内存进行socket读写,不需要进行缓冲区二次拷贝。
    ⑦内存池。
    netty提供了基于内存池的缓冲区重用机制,来进行对象的分配和回收。
    ⑧灵活的TCP参数配置能力。
  • 相关阅读:
    结构-行为-样式-有趣的函数
    结构-行为-样式-angularJs笔记
    Js-Html 前端系列--页面撑开头尾
    Java 实现下载
    Js-Html 前端系列--Ajax
    Js-Html 前端系列--checkbox
    Nutz中过滤特殊字符
    NUTZ中处理系统未捕获异常
    Thymeleaf 笔记
    Json 使用小结
  • 原文地址:https://www.cnblogs.com/fhwup/p/8587801.html
Copyright © 2020-2023  润新知