Netty百万级高并发支持
百万并发下netty的使用
1. 百万级并发场景
实际场景下,百万级并发请求是较多的,比如电商的促销、12306抢票、健康码查询等等,这些场景要求应用服务稳定,能最大化资源利用。所以实际应用程序设计时,会对QPS并发做预估处理,通过QPS选择合适的设计实现和对应的服务器资源数。一般会做如下要求:
- 选择占用尽可能少的服务器资源,减少成本投入;
- 服务器数量少,运维和维护难度降低,减少人力成本投入;
- 优秀的应用设计会降低实现复杂度,提升可用性。
2. 为什么选择netty做高并发
面临高并发场景诉求,解决办法有三个主题:
- I/O传输模型:用什么样的通道将数据发送给对方,是BIO、NIO还是AIO,I/O传输模型在很大程度上决定了框架的性能。
- 数据协议:采用什么样的通信协议,协议的选择不同,性能模型也就不同。
- 线程模型:线程模型涉及读取数据包,读包之后的编解码,编解码后消息如何派发等,线程模型设计得不同,对性能也会产生非常大的影响。
2.1 IO传输模型
IO在计算机中指Input/Output,也就是输入和输出。计算机运行时数据是在内存中驻留,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。
LINUX中进程无法直接操作I/O设备,必须通过系统调用请求kernel来协助完成I/O动作。内核会为每个I/O设备维护一个缓冲区。IO输入时,应用进程请求内核,内核会先看缓冲区中有没有相应的缓存数据,有数据则直接复制到进程空间,没有的话再到设备中读取。通常用户进程中的一个完整IO分为两阶段:用户进程空间<-->内核空间、内核空间<- ->设备空间。
由于CPU和内存的速度远远高于外设的速度,所以在IO编程中,就存在速度严重不匹配的问题,所以有了同步/异步,阻塞和非阻塞IO之分。
IO模型分为:BIO、NIO、IO多路复用、信号驱动IO和AIO。
- BIO:进程发起IO系统调用后,进程被阻塞,转到内核空间处理,整个IO处理完毕后返回进程,操作成功则进程获取到数据。
- NIO:非阻塞IO模型在内核数据没准备好,需要进程阻塞的时候,就返回一个错误,以使得进程不被阻塞;
- IO多路复用:多个的进程的IO可以注册到一个复用器(selector)上,然后用一个进程调用该select,,select会监听所有注册进来的IO。
- 信号驱动IO:当进程发起一个IO操作,会向内核注册一个信号处理函数,然后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用IO读取数据。
- AIO:当进程发起一个IO操作,进程返回(不阻塞),但也不能返回结果。内核把整个IO处理完后,会通知进程结果,如果IO操作成功则进程直接获取到数据。
异步IO是「内核数据准备好」和「数据从内核态拷贝到用户态」这两个过程都不用等待,所以只有AIon才是真正的一步IO。
2.2 线程模型
先了解一下Reactor模式,
在Reactor模式中,有5种角色:
- Initiation Dispatcher: 初始分发器,一旦事件被触发后,Initiation Dispatcher首先会分离出每一个事件,然后调用事件处理器,最后调用相关的回调方法来处理这些事件。
- Synchronous Event Demultiplexer: 同步事件分发器,调用方在调用它的时候会被阻塞,一直阻塞到同步事件分离器上有事件产生为止。对于Linux来说,同步事件分离器指的就是常用的I/O多路复用机制,比如说select、poll、epoll等。
- Event Handler:事件处理器,本身由多个回调方法构成,这些回调方法构成了与应用相关的对于某个事件的反馈机制。
- Concrete Event Handler:具体事件处理器,是事件处理器的实现。它本身实现了事件处理器所提供的各种回调方法,从而实现了特定于业务的逻辑。
- Handle:句柄,本质上表示一种资源(比如说文件描述符,或是针对网络编程中的socket描述符),是由操作系统提供的;该资源用于表示一个个的事件,事件既可以来自于外部,也可以来自于内部;外部事件比如说客户端的连接请求,客户端发送过来的数据等;内部事件比如说操作系统产生的定时事件等。它本质上就是一个文件描述符,Handle是事件产生的发源地。
Reactor 单线程模型:
优点:模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成
缺点:性能问题,单线程无法完全发挥多核 CPU 的性能。Handler 在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈 。
多线程Reactor
优点:可以充分的利用多核cpu 的处理能力
缺点:多线程数据共享和访问比较复杂,但是reactor处理所有的事件的监听和响应,在单线程运行, 在高并发场景容易出现性能瓶颈。
主从Reactor
优点:父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理。
缺点:编程复杂度较高
2.3 零拷贝
- Netty接收和发送ByteBuffer采用DirectBuffer,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。
- Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便地对组合Buffer进行操作,避免了传统的通过内存拷贝的方式将几个小Buffer合并成一个大Buffer的烦琐操作。
- Netty中文件传输采用了transferTo()方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write()方式导致的内存拷贝问题。
2.4 内存池
对于缓冲区来说,尤其是对于堆外直接内存的分配和回收,是一种耗时的操作。为了尽量重复利用缓冲区内存,Netty设计了一套基于内存池的缓冲区重用机制。
2.5 无锁化的串行设计理念
大多数应用场景下,并行多线程处理可以提升系统的并发性能。但如果共享资源竞争激烈,就会造成严重的锁竞争,导致系统性能的下降。为了尽可能避免锁竞争带来的性能损耗,可以通过串行化设计来避免多线程竞争和同步锁,即消息的处理尽可能在同一个线程内完成,不进行线程切换。
2.6 高效的并发编程
Netty的高效并发编程主要体现在如下几点:
-
volatile关键字的大量且正确的使用。
-
CAS和原子类的广泛使用。
-
线程安全容器的使用。
-
灵活的TCP参数配置能力。
2.7 对高性能的序列化框架的支持
影响序列化性能的关键因素有:序列化后的码流大小(网络带宽的占用);序列化/反序列化的性能(CPU资源占用)和是否支持跨语言。Netty默认提供了对Google Protobuf的支持,用户也可以通过扩展Netty的编解码接口接入其他高性能的序列化框架进行编解码,例如Thrift的压缩二进制编解码框架。
3. netty的使用姿势
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.option(ChannelOption.SO_REUSEADDR, true).option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_RCVBUF, 1024 * 128)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
bootstrap.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childOption(ChannelOption.SO_KEEPALIVE, true);
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
EventExecutorGroup logicGroup = new DefaultEventExecutorGroup(16);
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(logicGroup,new EchoServerWithExecutorHandler());
}
});
ChannelFuture f = bootstrap.bind().sync();
System.out.println(App.class.getName() + " started and listen on " + f.channel().localAddress());
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully().sync();
workerGroup.shutdownGracefully().sync();
}
更多推荐
所有评论(0)