Java之netty高性能
Posted 技术修养之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java之netty高性能相关的知识,希望对你有一定的参考价值。
netty介绍
netty应用场景
netty原理
同原理应用场景介绍
netty介绍
netty是一个高性能、异步事件驱动的NIO框架,基于JAVA NIO提供的API实现。它提供了对TCP、UDP和文件传输的支持
netty应用场景
互联网行业。在分布式系统中,各个节点之间需要远程服务调用,高性能的 RPC 框架必不可少,dubbo 协议默认使用 netty 作为基础通信组件
游戏行业。游戏服务可能大多都是c来编写socket服务,或者开发私有协议栈,netty的出现使得Java在此表现也不弱,市面游戏公司有采用netty
netty原理
1 先聊聊socket编程
基于TCP/IP协议的编程抽象就是socket
2 BIO和NIO区别
Blocking I/O是程序会等待I/O请求直到结果返回,每次从流中读一个或多个字节,直至完成,数据没有被缓存在任何地方
每个请求都需要独立的线程完成数据 Read,业务处理,数据 Write 的完整操作。
当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。
连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费。
相对于BIO的弊端和痛点,NIO的核心思路是将多线程处理的I/O转换为单线程处理
3 java的NIO实现
Buffer,Channel,Selector
NIO操作面向缓冲区,数据从Channel读取到Buffer缓冲区,随后在Buffer中处理数据。
4 netty的非阻塞I/O
netty中NioEventLoop是聚合SelectorProvider,Selector,所以可以使用单独线程来非阻塞的处理N个客户端连接和读写操作
相对于传统的同步阻塞IO,一连接一线程处理模式,性能和扩展能力大大提升
5 netty的线程模型
轮询方式,线程不断轮询访问相关事件发生源有没有发生事件,有发生事件就调用事件处理逻辑(空轮寻消耗cpu)
事件驱动方式,发生事件,主线程把事件放入事件队列,在另外线程不断循环消费事件列表中的事件,调用事件对应的处理逻辑处理事件
Reactor模式
The reactor design pattern is an event handling pattern for handling service requests delivered concurrently by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to associated request handlers
Reactor,Reactor 在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对 IO 事件做出反应
Handlers,处理程序执行 I/O 事件要完成的实际事件,Reactor 通过调度适当的处理程序来响应 I/O 事件,处理程序执行非阻塞操作
取决于Reactor的数量和 Hanndler线程数量的不同,Reactor模型有3种实现程度:
单 Reactor 单线程
对于一些小容量应用场景,可以使用到单线程模型。但对于高负载,大并发的应用却不合适,主要原因如下:
当一个NIO线程同时处理成百上千的链路,性能上无法支撑,即使NIO线程的CPU负荷达到100%,也无法完全处理消息
当NIO线程负载过重后,处理速度会变慢,会导致大量客户端连接超时,超时之后往往会重发,更加重了NIO线程的负载。
可靠性低,一个线程意外死循环,会导致整个通信系统不可用
单 Reactor 多线程
相比上一种模式,该模型在处理链部分采用了多线程(线程池)。
在绝大多数场景下,该模型都能满足性能需求。但是,在一些特殊的应用场景下,如服务器会对客户端的握手消息进行安全认证。这类场景下,单独的一个Acceptor线程可能会存在性能不足的问题。为了解决这些问题,产生了第三种Reactor线程模型
主从 Reactor 多线程
该模型相比第二种模型,是将Reactor分成两部分,mainReactor负责监听server socket,accept新连接;并将建立的socket分派给subReactor。subReactor负责多路分离已连接的socket,读写网络数据,对业务处理功能,其扔给worker线程池完成。通常,subReactor个数上可与CPU个数等同。
netty线程模型
Netty 主要基于主从Reactors多线程模型做了一定的修改,其中主从 Reactor 多线程模型有多个 Reactor:
MainReactor 负责客户端的连接请求,并将请求转交给 SubReactor。
SubReactor 负责相应通道的 IO 读写请求。
非 IO 请求(具体逻辑处理)的任务则会直接写入队列,等待 worker threads 进行处理。
netty采用了串行化设计理念,从消息的读取、编码以及后续Handler的执行,始终都由IO线程EventLoop负责,这就意外着整个流程不会进行线程上下文的切换,数据也不会面临被并发修改的风险。这也解释了为什么Netty线程模型去掉了Reactor主从模型中线程池
ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝
server启动类
handler处理类
SimpleChannelInboundHandler
同原理应用场景介绍
node.js让单线程的js可以处理海量并发需求的服务端应用,核心点在于reactor pattern
I/O多路复用有很多种实现。在linux上,2.4内核前有select和poll,自Linux 2.6内核正式引入了epoll
以上是关于Java之netty高性能的主要内容,如果未能解决你的问题,请参考以下文章