男儿欲遂平生志,五经勤向窗前读。这篇文章主要讲述分布式框架实现基础之IO技术 - Netty框架相关的知识,希望能为你提供帮助。
一、原理Netty 是一个高性能、异步事件驱动的 NIO 框架,基于 java NIO 提供的 API 实现。它提供了对
TCP、UDP 和文件传输的支持,作为一个异步 NIO 框架,Netty 的所有 IO 操作都是异步非阻塞
的,通过 Future-Listener 机制,用户可以方便的主动获取或者通过通知机制获得 IO 操作结果。
二、核心设计在 IO 编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者 IO 多路复用技术进行处理。IO 多路复用技术通过把多个 IO 的阻塞复用到同一个 select 的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程/多进程模型比,I/O 多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源。
与 Socket 类和 ServerSocket 类相对应,NIO 也提供了 SocketChannel 和 ServerSocketChannel两种不同的套接字通道实现。
2.1、多路复用
Netty 架构按照 Reactor 模式设计和实现,它的服务端通信序列图如下:
客户端通信模式
Netty 的 IO 线程 NioEventLoop 由于聚合了多路复用器 Selector,可以同时并发处理成百上千个客户端 Channel,由于读写操作都是非阻塞的,这就可以充分提升 IO 线程的运行效率,避免由于频繁 IO 阻塞导致的线程挂起。
2.2、异步通讯
Netty 采用了异步通信模式,一个 IO 线程可以并发处理 N 个客户端连接和读写操作,这从根本上解决了传统同步阻塞 IO 一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。
2.3、零拷贝技术
使用DIRECT BUFFERS 使用堆外直接内存(需操作系统底层支持),特点是:
2.4、内存池
基于内存池的缓冲区重用机制,虽然随着 JVM 虚拟机和 JIT 即时编译技术的发展,对象的分配和回收是个非常轻量级的工作。但是对于缓
冲区 Buffer,情况却稍有不同,特别是对于堆外直接内存的分配和回收,是一件耗时的操作。为了尽
量重用缓冲区,Netty 提供了基于内存池的缓冲区重用机制。
2.5、Reactor 线程模型
常用的 Reactor 线程模型有三种,Reactor 单线程模型, Reactor 多线程模型, 主从 Reactor 多线程模型。
2.5.1、单线程模型Reactor 单线程模型,指的是所有的 IO 操作都在同一个 NIO 线程上面完成。由于 Reactor 模式使用的是异步非阻塞 IO,所有的 IO 操作都不会导致阻塞,理论上一个线程可以独
立处理所有 IO 相关的操作。从架构层面看,一个 NIO 线程确实可以完成其承担的职责。例如,通过
Acceptor 接收客户端的 TCP 连接请求消息,链路建立成功之后,通过 Dispatch 将对应的 ByteBuffer
派发到指定的 Handler 上进行消息解码。用户 Handler 可以通过 NIO 线程将消息发送给客户端。NIO 线程的职责如下:
2.5.2、多线程模型
Rector 多线程模型与单线程模型最大的区别就是有一组 NIO 线程处理 IO 操作。 有专门一个NIO 线程-Acceptor 线程用于监听服务端,接收客户端的 TCP 连接请求;
网络 IO 操作-读、写
等由一个 NIO 线程池负责,线程池可以采用标准的 JDK 线程池实现,它包含一个任务队列和 N 个可用的线程,由这些 NIO 线程负责消息的读取、解码、编码和发送;
2.5.3、主从多线程模型服务端用于接收客户端连接的不再是个 1 个单独的 NIO 线程,而是一个独立的 NIO 线程池。 Acceptor 接收到客户端 TCP 连接请求处理完成后(可能包含接入认证等),将新创建的 SocketChannel 注册到 IO 线程池(sub reactor 线程池)的某个 IO 线程上,由它负责 SocketChannel 的读写和编解码工作。Acceptor 线程池仅仅只用于客户端的登陆、握手和安全 认证,一旦链路建立成功,就将链路注册到后端 subReactor 线程池的 IO 线程上,由 IO 线程负 责后续的 IO 操作。
2.6、无锁设计&
线程绑定
Netty 采用了串行无锁化设计,在 IO 线程内部进行串行操作,避免多线程竞争导致的性能下降。
表面上看,串行化设计似乎 CPU 利用率不高,并发程度不够。但是通过调整 NIO 线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-
多个工作线程模型性能更优。
Netty 的 NioEventLoop 读取到消息之后,直接调用 ChannelPipeline 的
fireChannelRead(Object msg),只要用户不主动切换线程,一直会由 NioEventLoop 调用到用户的 Handler,期间不进行线程切换,这种串行化处理方式避免了多线程操作导致的锁
的竞争,从性能角度看是最优的。
2.7、序列化框架
Netty 默认提供了对 Google Protobuf 的支持,通过扩展 Netty 的编解码接口,用户可以实现其它的高性能序列化框架,例如 Thrift 的压缩二进制编解码框架。
NAGLE 算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量
小封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法。
开启 RPS 后可以实现软中断,提升网络吞吐量。RPS 根据数据包的源地址,目的地址以
及目的和源端口,计算出一个 hash 值,然后根据这个 hash 值来选择软中断运行的 cpu,从上层
来看,也就是说将每个连接和 cpu 绑定,并通过这个 hash 值,来均衡软中断在多个 cpu 上,提升
网络并行处理性能。
【分布式框架实现基础之IO技术 - Netty框架】
三、NettyRpcgrpc是一个很大的课题,它需要一套系统的支持最基本的包含:服务端、客户端、注册中心、寻址中心和监控平台。类似的如grpc、dobbo等,只是RPC中的通信模块,这里只描述下netty的实现。
3.1、通信过程
如果使用 netty 的话,一般会用 channel.writeAndFlush()方法来发送消息二进制串,这个方法调用后对于整个远程调用(从发出请求到接收到结果)来说是一个异步的,即对于当前线程来说,
将请求发送出来后,线程就可以往后执行了,至于服务端的结果在服务端处理完成后,再以消息的形式发送给客户端的。于是这里出现以下两个问题:
如下图所示,线程 A 和线程 B 同时向 client socket 发送请求 requestA 和 requestB,
socket 先后将 requestB 和 requestA 发送至 server,而 server 可能将 responseB 先返
回,尽管 requestB 请求到达时间更晚。我们需要一种机制保证 responseA 丢给
ThreadA,responseB 丢给 ThreadB。
3.2、通信流程
public Object get()
synchronized (this)// 旋锁 while (true)// 是否有结果了
If(!isDone)
wait();
//没结果释放锁,让当前线程处于等待状态
else//获取数据并处理
private void setDone(Response res)
this.res = res;
isDone = true;
synchronized (this)//获取锁,因为前面 wait()已经释放了 callback 的锁了
notifyAll();
// 唤醒处于等待的线程
四、Thrift简介Apache Thrift 是 Facebook 实现的一种高效的、支持多种编程语言的远程服务调用的框架。本文将从
Java 开发人员角度详细介绍 Apache Thrift 的架构、开发和部署,并且针对不同的传输协议和服务类
型给出相应的 Java 实例,同时详细介绍 Thrift 异步客户端的实现,最后提出使用 Thrift 需要注意的事
项。
目前流行的服务调用方式有很多种,例如基于 SOAP 消息格式的 Web Service,基于 JSON 消息格式
的 RESTful 服务等。其中所用到的数据传输方式包括 XML,JSON 等,然而 XML 相对体积太大,传输
效率低,JSON 体积较小,新颖,但还不够完善。
由 Facebook 开发的远程服务调用框架
Apache Thrift,它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码
生成引擎可以在多种语言中,如 C++, Java, python, php, Ruby, Erlang, Perl, Haskell, C#, Cocoa,
Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小,
对于高并发、大数据量和多语言的环境更有优势。本文将详细介绍 Thrift 的使用,并且提供丰富的实例
代码加以解释说明,帮助使用者快速构建服务。
推荐阅读
- 6.11-6.17博客精彩回顾
- 使用REST discovery(pro)生成SoapUI测试
- 创建函数-----------(向函数传递数组从函数返回数组)
- sedgawk介绍与正则表达式-----------(扩展的正则表达式)
- 创建函数-----------(创建函数定义函数使用函数返回值)
- sedgawk介绍与正则表达式-----------(定义基本正则表达式(BRE)模式)
- 脚本控制------------------------(at命令mail命令batch命令cron表格anacron表格)
- sedgawk介绍与正则表达式-----------(正则表达式定义与类型)
- 脚本控制------------------------(作业控制查看作业jobs命令重新启动停止的作业)