Netty
2024-02-22 17:24:05 3 举报
AI智能生成
登录查看完整内容
Netty是一个高性能、异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。它提供了一套丰富的API,支持多种传输协议,如TCP、UDP等。Netty的核心是基于NIO(Non-blocking I/O)实现的,通过零拷贝、内存池等技术提高了数据传输的效率。此外,Netty还提供了强大的编解码器支持,可以轻松实现各种协议的编解码。总之,Netty是一个功能强大、易于使用的网络编程框架,适用于构建高性能、高并发的网络应用。
作者其他创作
大纲/内容
Bootstrap是Netty框架的启动类和主入口类,分为客户端类Bootstrap和服务器类ServerBootstrap两种。网络编程里,\"服务器\"和\"客户端\"实际上表示了不同的网络行为:换句话说,是监听传入的连接还是建立到一个或者多个进程的连接。因此,有两种类型的引导:一种用于客户端(简单地称为Bootstrap),而另一种(ServerBootstrap)用于服务器。无论应用程序使用哪种协议或者处理哪种类型地数据,唯一决定它使用哪种引导类的是它是作为一个客户端还是作为一个服务器
Bootstrap
NIO:io.netty.channel.socket.nio 使用java.nio.channels包作为基础--基于选择器的方式
OIO:io.netty.channel.socket.oio使用java.net包作为基础--使用阻塞流
Local:io.netty.channel.local可以在VM内部通过管道进行通信的本地传输
Embedded:io.netty.channel.embedded Embedded传输,允许使用ChannelHandler而又不需要一个真正的基于网络的传输,在测试ChannelHandler实现时非常有用
内置通信传输模式
Channel是JavaNIO的一个基本构造,它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的IO操作的程序组件)的开发连接,如读操作和写操作
目前,可以把Channel看作是传入(入站)或者传出(出站)数据的载体。因此,它可以被打开或者被关闭,连接或者断开连接
Netty网络抽象的代表:Channel--SocketEventLoop--控制流、多线程处理、并发ChannelFuture--异步通知Channel和EventLoop关系如图:我们可以看出Channel需要被注册到某个EventLoop上,在Channel整个声明周期内部都由这个EventLoop处理IO事件,也就是说一个Channel和一个EventLoop进行了绑定,但是一个EventLoop可以同时被多个Channel绑定。
Channel、EventLoopGroup和ChannelFuture
Channel的生命周期状态ChannelUnregistered:Channel已经被创建,但是还未注册到EventLoopChannelRegistered:Channel已经被注册到了EventLoopChannelActiveLChannel处于活动状态(已经连接到它的远程节点)。它现在可以接收和发送数据了ChannelInactive:Channel没有连接到远程节点当这些状态发生改变时,将会生成对应的事件。这些事件将会被转发给ChannelPipeline中的ChannelHandler,其可以随后对它们做出响应。在日常编程中,关注ChannelActive和ChannelInactive会更多一些
Channel
EventLoop继承关系图
线程管理。在内部,当提交任务到如果(当前)调用线程正是支撑EventLoop的线程,那么所提交的代码块将会被(直接)执行,否则,EventLoop将调度该任务以便稍后执行,并将它放入到内部队列中。当EventLoop下次处理它的事件时,它会执行队列中的那些任务/事件
为什么要设计成一个Channel中的事件处理只能交给一个EventLoop?在以前的版本中所使用的线程模型只保证了入站(之前称为上游)事件会在所谓的IO线程(对应Netty4中的EventLoop)中执行。所有的出站(下游)事件都由调用线程处理,其可能是I/O线程也可能是别的线程。开始看起来这似乎是个好主意,但是已经被发现是有问题的,因为需要在ChannelHandler中对出站事件进行仔细的同步。简而言之,不可能保证多个线程在同一时刻尝试访问出站事件。例如你通过在不同的线程中调用Channel.write()方法,针对同一个Channel同时触发出站的事件,就会发生这种情况。当出站事件触发了入站事件时,将会导致另一个负面影响。当Channel.write()方法导致异常时,需要在调用线程中执行代码,然后将事件移交给I/O线程去执行(在出站事件中,因为出站的数据是要发往对端的,所以当异常触发时要把它当作入站,交由发送端来处理)然而这将带来额外的上下文切换。Netty4中所采用的线程模型,通过在同一个线程中处理某个给定的EventLoop中所产生的所有事件,解决了这个问题,这提供了一个更加简单的执行体系架构,并且消除了在多个ChannelHandler中进行同步的需要,也解决了线程安全和同步的问题
EventLoop/EventLoopGroup
Netty使用不同的事件来通知我们状态的改变或者是操作的状态。这使得我们能够基于已经发生的事件来触发适当的动作
Netty事件是按照它们与入站或出站数据流的相关性进行分类的。可能由入站数据或者相关的状态更改而触发的事件包括:连接已被激活或者连接失活;数据读取;用户事件;错误事件;
出站事件是未来将会触发的某个动作的操作结果,这些动作包括:打开或者关闭到远程节点的连接;将数据写到或者冲刷到套接字
事件
提供提供了大量预定义的可以开箱即用的ChannelHandler实现,包括用于各种协议(如HTTP和SSL/TLS)的ChannelHandler
ChannelHandler的生命周期。在ChannelHandler被添加到ChannelPipeline中移除时会调用下面这些方法。这些方法中的每一个都接受一个ChannelHandlerContext参数。handlerAdded:当把ChannelHandler添加到ChannelPipeline中被调用handlerRemoved:当从ChannelPipeline中移除ChannelHandler时被调用exceptionCaught:当处理过程中在ChannelPipeline中有错误产生时被调用
入站处理流程
出站处理流程
匹配入站/出站处理器
入站/出站事件掩码
Handler的顺序可以被打乱,但是相对顺序不能轻易改变
ChannelPipeline中的ChannelHandler.入站和出站ChannelHandler被安装到同一个ChannelPipeline中,ChannelPipeline以双向链表的形式进行维护管理。如上图,在网络上传递的数据,要求加密,但是加密后密文比较大,需要压缩后再传输,而且按照业务要求,需要检查报文中携带的用户信息是否合法,于是图中实现了,解压(入)Handler、压缩(出)Handler、解密(入)Handler、加密(出)Handler\\授权(入)Handler.如果一个消息或者任何其他入站事件被读取,那么它会从ChannelPipeline的头部开始流动,但是只被处理入站事件的Handler处理,也就是解压(入)Handler、解密(入)Handler、授权(入)Handler,最终,数据将会到达ChannelPipeline的尾端,届时,所有的处理就都结束了数据的出站运动(即正在被写的数据)在概念上也是一样的。在这种情况下,数据将从链的尾端开始流动,但是制备处理出站的Handler处理,也就是加密(出)Handler、压缩(出)Handler,直到它到达链的头部为止。在这之后,出站数据将会到达网络传输层,也就是SocketNetty能区分入站事件的Handler和出站的Handler,并确保数据只会在具有相同定向的两个ChannelHandler之间传递在编写Netty应用程序时要注意,分属出站和入站不同的Handler,在业务没有特殊要求的情况下是无所谓顺序的,比如压缩(出)Handler可以放在解压(入)Handler和解密(入)Handler中间,也可以放在解密(入)Handler和授权之间而同属一个方向的Handler则是有顺序的,因为上一个Handler处理的结果往往是下一个Handler的要求的输入。比如入站处理,对于收到的数据,只有先解压才能得到密文,才能解密,只有解密后才能拿到明文中的用户信息进行授权检查,所以解压-解密-授权这个三个入站Handler的顺序就不能乱
从应用程序开发人员的角度来看,Netty的主要组件是ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器。ChannelHandler的方法是由网络事件触发的。事实上,ChannelHandler可专门用于几乎任何类型的动作,例如将数据从一种格式转换为另一种格式,例如各种编解码,或者处理转换过程中所抛出的异常.
ChannelInboundHandler的生命周期方法。这些方法将会在数据被接收时或者与其对应的Channel状态发生改变时被调用。和Channel的生命周期密切相关channelRegistered:当Channel已经注册到它的EventLoop并且能够处理I/O时被调用channelUnRegistered:当Channel从它的EventLoop注销并且无法处理任务I/O时被调用channelActive:当Channel处于活动状态时被调用;Channel已经连接/绑定并且已经就绪channelInactive:当Channel离开活动状态并且不再连接它的远程节点被调用chabnelReadComplete: 当Channel上的一个读操作完成时被调用channelRead:当从Channel读取数据时被调用channelWritabilityChanged:当Channel的可写状态发生改变时被调用,可以通过调用Channel的isWritable()方法来检测Channel的可写性。与可写性相关的阈值可以通过Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWaterMark()方法来设置,userEventTriggered:当ChannelInboundHanadler.fireUserEventTriggered()方法被调用时被调用
SimpleChannelInboundHandler
ChannelInboundHandler该接口将会是经常实现的子接口。这种类型的ChannelHandler接收入站事件和数据,这些数据随后将会被应用程序的业务逻辑所处理。当你要给连接的客户端发送响应时,也可以从ChannelInboundHandler直接虫偶刚刷数据然后输出到对端。应用程序的业务逻辑通常实现在一个或者多个ChannelInboundHandler中。这种类型的ChannelHandler接收入站事件和数据,这些数据随后将会被应用程序的业务逻辑所处理
ChannelOutboundHandler出站操作和数据将由ChannelOutboundHandler处理。它的方法将被Channel、ChannelPipeline以及ChannelHandlerContext调用,所有由ChanneloutboundHandler本身所定义的方法如下biind():当请求将Channel绑定到本地地址时调用connect():当请求将Channel连接到远程节点时被调用disconnect():当请求将Channel从远程节点断开时被调用close():当请求关闭Channel时被调用deregister():当请求将Channel从它的EventLoop注销时被调用read()当请求从Channel读取更多的数据时被调用flush():当请求通过Channel将入队数据冲刷到远程节点时被调用write()当请求通过Channel将数据写到远程节点时被调用
ChannelHandler的适配器。有一些适配器类可以将编写自定义的ChannelHandler所需要的工作降到最低限度,因为它们提供了定义在对应接口中的所有方法的默认实现。因为有时会忽略那些不感兴趣的事件,所以Netty提供了抽象积累ChannelInboundHandlerAdapter(处理入站)和ChannelOutboundHandlerAdapter(处理出站)的基本实现,通过扩展抽象类ChannelHandlerAdapter,它们获得了它们共同的超接口ChannelHandler的方法.不过ChannelOutboundHandler有个非常让人迷惑的read()方法,ChannelOutboundHandler不是处理出站事件的吗?怎么会有read()方法呢?其实这个read方法不是表示读数据,而是表示业务发出了读(read)数据的要求,这个要求也会封装为一个事件进行传播,这个事件因为时业务发出到网络的,自然就是个出站事件,而且这个事件触发的就是ChannelOutboundHandler中read()方法。如果Handler纪要处理入站又要处理出站怎么办呢?这个时候就可以使用类ChannelDuplexHandler,当然也可以同时实现ChannelOutboundHandler,ChannelInboundHandler这两个接口
ChannelHandler
ChannelInitializer
Channel、ChannelPipeline和ChannelhandlerContext上的事件传播。ChannelHandlerContext有很多的方法,其中一些方法也存在于Channel和ChannelPipeline本身上,但是有一点重要的不同,如果调用Channel或者ChannelPipeline上的这些方法,它们将沿着整个ChannelPipeline进行传播。而调用位于ChannelHandlerContext上的相同的方法,则将从当前所关联的ChannelHandler开始,并且只会传播给位于该ChannelPipeline中的下一个(入站下一个,出站上一个)能够处理该事件的ChannelHandler
eg:比如服务器收到对端发过来的报文,解压后需要进行机密,结果解密失败,要给对端一个应答。如果发现解密失败 原因时服务器和对端的加密算法不一致,应答报文只能以明文的压缩格式发送,就可以在解密Handler中直接使用ctx.write给对端应答,这样应答报文就只会经过压缩Handler就发往了对端
ChannelHandlerContext的APIalloc返回和这个示例相关联的Channel所配置的ByteBufAllocatorbind绑定到给定的SocketAddress,并返回ChannelFuturechannel返回绑定到这个实例的Channelclose关闭Channel,并返回ChannelFutureconnect连接给定的SocketAddress,并返回ChannelFuturederegister从之前分配的EventExecutor注销,并返回ChannelFuturedisconnect从远程节点断开,并返回ChannelFutureexecutor返回调度时间的EventExecturofireChannelActive触发对下一个ChannelInboundHandler上的channelActive()方法(已连接)的调用fireChannelInactive触发对下一个ChannelInboundHandler上的ChannelInactive()(已关闭)方法fireChannelRead触发对下一个ChannelInboundHandler上的channelRead()方法(已接收的消息)调用fireChannelReadComplete触发对下一个ChannelInboundHandler上的channelReadComplete()方法的调用fireChannelRegistered触发对下一个ChannelinboundHandler上的fireChannelRegistered()方法的调用fireChannelUnRegistered()触发对下一个ChannelInboundHandler上的fireChannelUnRegistered()方法的调用fireChannelWritablityChanged触发对下一个ChannelInboundHandler上的fireChannelWritabilityChanged()方法的调用fireChannelExceptionCaught()触发对下一个ChannelInboundHandler上的fireExceptionCaught()方法的调用fireUserEventTriggered触发对下一个ChannelInboundHandler上的fireUserEventTriggered(Object evet)方法的调用handler返回绑定到这个实例的ChannelHandlerisRemoved如果所关联的ChannelHandler已经被从ChannelPipeline中移除则返回truename返回这个实例的唯一名称pipeline返回这个实例所关联的ChannelPipelineread将数据从Channel读取到第一个入站缓冲区,如果读取成功则触发一个channelRead事件,并(在最后一个消息被读取完成后)通知ChannelInboundHandler的channelReadComplte(ctx)方法write通过这个实例写入消息并经过ChannelPipelinewriteAndFlush通过这个实例写入并冲刷消息并经过ChannelPipeline当使用ChannelHandlerContext的API的时候,有以下两点1.ChannelHandlerContext和ChannelHandler之间的关联(绑定)是永远不会改变的,所以缓存对它的引用是安全的2.相对于其他类的同名方法,ChannelHandlerContext的方法将产生更短的事件流,应该尽可能地利用这个特性来获得最大地性能
ChannelHandlerContext
基于Netty的网路应用程序中根据业务需求会使用Netty已经提供的Channelhandler或者自行开发ChannelHandler,这些ChannelHandler都放在ChannelPipeline中统一管理,事件就会在ChannelPipeline中流动,并被其中一个或者多个ChannelHandler处理
ChannelPipeline上的方法。既然ChannelPipeline以双向链表的形式进行维护管理Handler,自然也提供了对应的方法在ChannelPipeline中增加或者删除、替换handler.addFist、addBefore、addAfter、addLast将一个ChannelHandler添加到ChannelPipeline中。remove将一个ChannelHandler从ChannelPipeline中移除replace将ChannelPipeline中的一个ChannelHandler替换为另一个ChannelHandlerget通过类型或者名称返回ChannelHandlercontext返回和ChannelHandler绑定的ChannelHandlerContextnames返回ChannelPipeline中所有ChannelHandler的名称ChannelPipeline的API公开了用于调用入站和出站操作的附加方法
ChannelPipeline
Netty中所有的IO操作都是异步的,我们知道\"异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等\",也就是说至少我们需要一种获得异步执行结果的手段
JDK原生Future提供的方法
ChannelFuture
ChannelOption.SOBACKLOG
ChannelOption.SO_REUSEADDR
ChannelOption.SO_KEEPALIVE
ChannelOption.SO_SNDBUF/ChannelOption.SO_RCVBUF
ChannelOption.SO_LINGER
ChannelOption.TCP_NODELAY
ChannelOption
PooledByteBuf是池化的ByteBuf,提高了内存分配与释放的速度,它本身是一个抽象泛型类,有三个子类:PooledDirectByteBuf、PooledHeapByteBuf、PooledUnsafeDirectByteBuf.
分配过程如下:4号节点被完全分配,将高度值设置为12表示不可用。4号节点的父亲节点即2号节点,将高度值更新为两个子节点的较小值;其他祖先节点亦然,直到高度值更新到根节点,可推知,memoryMap数组的值有如下三种情况:memoryMap[id]=depthMap[id] -- 该节点没有被分配memoryMap[id] > depthMap[id] -- 至少有一个子节点被分配,不能再分配该高度满足的内存,但可以根据实际分配较小一些的内存,比如,上图中分配了4号子节点的2号节点,值从1更新为2,表示该节点不能再分配8MB的只能最大分配4MB内存,因为分配了4号节点后只剩下5号节点可用。memoryMap[id] = 最大高度 +1 (本例中12) -- 该节点及其子节点已被完全分配,没有剩余空间。
PooledByteBuf
假如你的系统要支撑高并发的用户场景,你可能会进行搜索如何搭建高性能的Java网络编程,你可能会看到Netty是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器和客户端
接着你的下一步多半是阅读一些博客,看一些例子,然后写点代码试试,如果掌握了网络通信编程的技术,遇到的问题可能会少点,否则你可能看不懂在说些什么
高性能系统不仅要求超一流的编程技巧,还需要几个复杂领域(网络编程、多线程处理和并发)的专业知识,Netty优雅地处理了这些领域的知识,使得我们可以将精力放在业务处理上,而不是关注网络编程方便的非业务逻辑
Netty的架构方法和设计原则是:每个小点都和它的技术性内容一样重要,穷其精妙。1.关注点分离——业务和网络逻辑解耦2.模块化和可复用性3.可测试性作为首要的要求
1.在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费。
2.需要为每个线程的调用栈都分配内存,其默认值大小区间为64kb-1mb
3.即使JVM可以在物理上支持非常大的数量,但是远在到达该极限之前,上下文切换所带来的开销就会带来麻烦
这种并发方案对于支撑中小数量的客户端来说还算可以接收,但是为了支撑100 0000或者更多的并发连接所需要的资源会使得它很不理想
为什么会有Netty?
为什么不用Netty5? Netty5已经停止开发了
AIO还有个缺点是接收数据需要预先分配缓存,而不是NIO那种需要接收时才需要分配缓存,所以对连接数量非常大但流量小的情况,内存浪费很多
而且Linux上AIO不够成熟,处理回调结果速度跟不上处理需求,作者原话:Not faster than NIO(epoll) on unix systems(which is true)There is no daragram supportUnnecessary threading model(too much abstraction without usage)
为什么Netty使用NIIO而不是AIO?
简单来说,Mina机会不再更新了,Netty本来就是因为Mina不够好所以开发出来的
为什么不用Mina?
常见的为什么
1.API使用简单,开发门槛低
2.功能强大,预置了多种编解码功能,支持多种主流协议
3.定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展
4.性能高,通过与其他业界主流地NIO框架对比,Netty的综合性能最优
5.成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务人员不需要再为NIO的bug而烦恼
6.社区活跃,版本迭代周期短,发现的bug可以被及时修复,同事,更多的新功能会加入
7.经历了大规模的商业应用考研,直到得到验证
优势
功能描述。通信框架承载了业务内部各模块之间的消息交互和服务调用,它的主要功能如下:基于Netty的NIO通信框架,提供高性能的异步通信能力提供消息的编解码框架,可以实现POJO的序列化和反序列化消息内容的防篡改机制提供基于IP地址的白名单接入认证机制链路的有效性校验机制链路的断连重连机制
功能描述
通信模型1.客户端发送应用握手请求,携带节点ID等有效身份认证信息2.服务端对应应用握手请求消息进行合法性校验,包括节点ID有效性校验、节点重复登录校验和IP地址合法性校验,校验通过后,返回登录成功的应用握手应答消息3.链路建立成功之后,客户端发送业务消息4.链路成功之后,服务端发送心跳消息5.链路建立成功之后,客户端发送心跳消息6.链路建立成功之后,服务端发送业务消息7.服务端退出时,服务端关闭连接,客户端感知对方关闭连接后,被动关闭客户端连接备注:需要指出的是,协议通信双方链路建立成功之后,双方可以进行全双工通信,无论是客户端还是服务端,都可以主动发送请求消息给对方,通信方式可以是TWO WAY或者ONE WAY.双方之间的心跳采用Ping-Pong机制,当链路处于空闲状态时,客户端主动发送Ping消息给服务端,服务端接收到Ping消息后发送应答消息Pong给客户端,如果客户端连续发送N条Ping消息都没有接收到服务器返回的Pong消息,说明链路已经挂死或者对方处于异常状态,客户端主动关闭连接,间隔周期T后发起重连操作,直到重连成功
通信模型
Netty消息定义表
消息头定义(Header)
消息定义
链路的建立
心跳机制。在凌晨等业务低谷时段,如果发生网络闪断、连接被Hang住等问题时,由于没有业务消息,应用程序很难发现。到了白天业务高峰期时,会发生大量的网络通信失败,严重的会导致一段时间进程内无法处理消息。为了解决这个问题,在网络空闲时采用心跳机制来检测链路的互通性,一旦发现网络故障,立即关闭链路,主动重连。当读或者写心跳消息发生I/O异常的时候,说明已经中断,此时需要立即关闭连接,如果时客户端,则需要重新发起连接,如果是服务端,需要清空缓存的半包信息,等到客户端重连
空闲的连接和超时。检测空闲连接以及超时对于及时释放资源来说是至关重要的。由于这是一项常见的任务,Netty特地为它提供了几个ChannelHandler实现。IdleStateHandler当连接空闲时间太长时,将会触发一个IdleStateEvent时间。然后,可以通过在ChannelInboundHandler中重写userEventTriggered()方法来处理该IdleStateEvent时间ReadTimeoutHandler如果在指定的时间间隔内没有收到任何的入站出局,则抛出一个ReadTimeoutException并关闭对应的Channel.可以通过重写ChannelHandler中的exceptionCaught()方法来检测该Read-TImeoutException
重连机制。如果链路中断,等到INTERVAL时间后,由客户端发起重连操作,如果重连失败,间隔周期INTERVAL后再次发起重连,直到重连成功。为了保证服务端能够有重组的时间释放句柄资源,在首次断连时客户端需要等待INTERVAL时间之后再发起重连,而不是失败后立即重连。为了保证句柄资源能够及时释放,无论什么场景下重连失败,客户端必须保证自身的资源被及时释放,包括但不限制SocketChannel、Socket等重连失败后,可以打印异常堆栈信息,方便后续的问题定位
可靠性设计
模型
前期准备。定义好消息有关的实体类,为了防篡改,消息体需要进行摘要,可以使用MD5、SHA-1和SHA-256.还可以对MD5进行额外的加盐摘要,序列化框架可以使用Kryo
测试。1.正常情况2.客户端宕机,服务器应能清楚客户端的缓存信息,允许客户端重新登录3.服务器宕机,客户端应能发起重连4.在LoginAuthRespHandler中进行注释,可以模拟当服务器不处理客户端的请求时,客户端在超时后重新进行登录
功能的增强。作为一个通信框架,支持诊断也是很重要的,所以我们可以在服务端单独引入一个MetricsHandler,可以提供,目前在线Channel数、发送队列积压消息数、读取速率、写出速率相关数据,以方便应用方对自己的应用的性能和繁忙程度进行检查和调整。当然对于一个通信框架还可以提供SSL安全访问、流控、I/O线程和业务线程分离、参数的可配置化等等功能
源码:netty-adv
实现设计
通信框架功能设计
Netty是如何解决JDK中的Selector BUG的?
首先就是要突破操作系统的限制。在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统为每个TCP连接都要创建一个Socket句柄,每个socket句柄也是一个文件句柄)。可使用ulimit命令查看系统允许当前用户进程打开的句柄数限制:ulimit -n1024这表示当前用户的每个进程最多允许同时打开1024个句柄,这1024个句柄中还得出去每个进程必然打开的标准输入,标准输出,标准错误,服务器监听socket,进程间通讯的unix域socket等文件,那么剩下的可用于客户端socket连接的文件数只有大概1024-10=1014个左右。也就是说缺省情况下,基于Linux的通讯程序最多允许同时1014个TCP并发连接对于想支持更高数量的TCP并发连接的通讯处理程序,就必须修改Linux对当前用户进程同时打开的文件数量修改单个进程打开最大文件数限制的最简单的办法就是使用ulimit命令:ulimit -n 1000000如果系统回显类似于\"Operation not permitted\
操作系统
font color=\"#e74f4c\
接收和发送缓冲区调优在一些场景下,端侧设备会周期性地上报数据和发送心跳,单个链路的消息收发量并不大,针对此类场景,可以通过调小TCP的接收和发送缓冲区来降低单个TCP连接的资源占用率。当然对于不同的应用场景,收发缓冲区的最优值可能不同,用户需要根据实际场景,结合性能测试数据进行针对性的调优
IO线程和业务线程分离如果服务端不做复杂的业务逻辑操作,仅仅时简单的内存操作和消息转发,则可以通过调大NioEventLoop工作线程池的方式,直接在IO线程中执行业务ChannelHandler,这样便减少了一次线程上下文切换,性能反而更高。如果有复杂的业务逻辑操作,则将以IO线程和业务线程分离,对于IO线程,由于互相之间不存在锁竞争,可以创建一个大的NioEventLoopGroup线程组,所有Channel都共享同一个线程池。对于后端的业务线程池,则加你创建多个小的业务线程池,线程池可以与IO线程绑定,这样既减少了锁竞争,又提升了后端的处理性能
针对端侧并发连接数的流控无论服务端的性能优化到多少,都需要考虑流控功能。当资源称为瓶颈,或者遇到端侧设备的大量接入,需要通过流控对系统做保护。流控的策略有很多种,比如针对端侧连接数的流控;在Netty中,可以非常方便地实现流控功能:新增一个FlowCOntrolChannelHandler,然后添加到ChannelPipeline靠前的位置,覆盖channelActive()方法,创建TCP链路后,执行流控逻辑,如果达到流控阈值,则拒绝该连接,调用ChannelHandlerContext的close()方法关闭连接
JVM层面相关性能优化。font color=\"#000000\
Netty调优
如何让单机下Netty支持百万长连接?如果要支持百万长连接,需要有很多的工作要做
什么是水平触发(LT)和边缘触发(ET)?
常见的问题
Netty
0 条评论
回复 删除
下一页