netty(异步非阻塞、实时、高效率)

本文深入探讨Netty框架的优势,如简化通信代码、提高性能及稳定性,适用于多种业务场景。介绍Netty特性,包括灵活的线程模型、高性能的吞吐量、强大的社区支持等。对比NIO与Netty的通信步骤,解析长连接、短连接、心跳检测、文件上传下载的实现,以及粘包、黏包问题的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.为什么使用netty

简单,再也不用编写复杂的代码逻辑去实现通信,再也不用考虑性能问题,不需要考虑编解码问题,半包读写问题。

netty运用于Hadoop的RPC框架Avro,JMS框架RocketMQ,主流的分布式通信框架Dubbox等。

在业务场景中比如导航,建立长连接实时返回数据结果,以及滴滴打车,实时返回车程等。

2.netty特性

Design:各种传输类型,阻塞和非阻塞套接字统一的API使用灵活简单但功能强大的线程模型,无连接的DatagramSocked支持链逻辑,易于重用。

Easy Of Use:提供大量的文档和例子,除了依赖jdk1.6,某些功能依赖jdk1.7,其他特性可能有相关依赖,都是可选的。

Performance:比Java API更好的吞吐量和更低的延迟,因为线程池和重用所有消耗较少的资源尽量减少不必要的内存拷贝。

Robustness:鲁棒性,可以理解成健壮性链接,快或慢或超载不会导致更多的OutOfMemoryError,在高速的网络程序中不会有不公平的read/write。

Security:完整的SSL/TSL和start TLS支持可以在如Applet或OSGI这些受限制的环境中使用。

Community:版本发布频繁,社区活跃

3.NIO通信步骤

(1)创建ServletSocketChannel,为它配置非阻塞模式。

(2)绑定监听,配置TCP参数,录入backlog大小。

(3)创建一个独立的IO线程,用于轮询多路复用器Selector

(4)创建Selector,将之前创建的ServletSocketChannel注册到Selector,并设置监听标识位SelectionKey.ACCEPT

(5)启动IO线程,在循环体中轮询Selector.select()方法,轮询就绪的通道。

(6)当轮询到了处于就绪的通道时,需要进行判断操作位,如果是ACCEPT状态,说明是新的客户端接入,则调用accept方法接受新的客户端。

(7)设置新接入客户端的一些参数,如非阻塞、并将其通道继续注册到Selector之中,设置监听标识位等。

(8)如果轮询通道位的操作是READ,则进行读取,构造Buffer 对象等。

(9)更细节的还有数据没发送完继续发送等问题..............

一个简单的NIO服务器端程序,就是如此的复杂,接下来看Netty需要的几个步骤。

4.Netty通信步骤

(1)创建两个NIO线程组,一个专门用于网络事件处理(接受客户端的请求),另一个则进行网络通信读写。

(2)创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等。

(3)创建一个实际处理数据的类ChannelIntializer,进行初始化的准备工作,比如设置接受传出数据的字符集,格式,已经实际处理数据的接口。

(4)绑定端口,执行同步阻塞方法,等待服务器端启动即可。

5.服务端和客户端

(1) serverBootstap 的SO_BACKLOG配置

 服务器端TCP内核模块维护需要两个队列,我们称之为A队列和B队列。

客户端向服务端发送connect请求的时候,会发送带有SYN标志的包(第一次握手)

服务器端收到客户端的SYN包后会向客户端发送SYN ACK确认(第二次握手)

默认内核模块将客户端加到A队列进行等待,然后当服务器端收到客户端发来的ACK

时,TCP内核模块会将客户端连接从A转移到B中,连接完成,返回accept。也就是

说ACCEPT从B连接中取出完成三次握手的连接。

A和B队列的长度和就是backlog,当A和B连接的长度和大于backlog时,新连接会

被TCP内核拒绝,当backlog过小时,可能会出现accept速度跟不上。

需要注意的是:backlog对程序支持的连接数并无影响,backlog影响的只是还没有

被accept取出的连接。

(2)  服务器端会配置两个EventLoopGroup,客户端只需要配置一个。

(3)  服务端处理类实现ChannelHandlerAdapterSimpleChannelInboundHandler的区别

ChannelHandlerAdapter在处理完消息后需要负责释放资源。在这里将调用ByteBuf.release()来释放资源。

SimpleChannelInboundHandler会在完成channelRead0后释放消息,这是通过Netty处理所有消息的ChannelHandler实现了

ReferenceCounted接口达到的。所以当服务器要返回相同的消息给客户端,用ChannelHandlerAdapter

(4)  netty将接受消息和输出消息封装成了ByteBuf对象

     传统Nio模式接受和传输消息需要进行flip操作(复位指针),ByteBuf不用,因为ByteBuf里面有读写两个指针。

(5)  ByteBuf资源手动去释放资源

  ByteBuf是一个计数对象,处理器的职责是释放所有传递到处理器的计数对象。如果在业务代码中手动执行writeAndflush操作也不用手动释放,因为它会自动去释放。

(6)  netty中所有操作都是异步,所以很多操作都会有一个Future类做为操作通知返回对象,可以避免在消息还没写完或者读完

通道已经关闭了。

6.消息的粘包黏包问题

    问题描述:TCP协议是流协议,服务器端发送数据 ABC  DEF,客户端解析成AB DE F或者其他格式,如果不做处理的话这种

情况很容易出现。

详细原因:

    (1)  应用程序write写入的字节大小大于套接字发送缓冲区的大小。

    (2)  进行MSS大小的TCP分段

    (3)  以太网帧的payload大于MTU进行IP分片

解决方案:

    (1)  发送消息协议好固定长度,如果不够长度的话自己空位补空格。接受数据长度不够固定长度时保留在tcp流中,够固定长度

的时候再进行解析。

    (2)  在包尾部增加特殊字符进行分割,例如加回车换行符。

Netty封装了DelimiterBasedFramerDecoder

    (3)  推荐的主流,将消息分成消息头和消息体,在消息头中包含消息长度的字段,然后进行业务逻辑的处理。

7.netty的长连接和短连接

默认netty连接都是长连接,如何切换成短连接呢?

(1)在server端每次发完消息后关闭和客户端的连接,即在操作结尾加上addListener 关闭对客户端的通道,但是客户端发送过

来的消息还是会接收到。

    (注:writeAndFlush操作会把数据写到tcp流里面 并发送到通道里面,单纯的write 并不会被服务端读取数据

8.netty的数据通信

   通信方式大致分为三种:

(1)长连接不间断的进行连接,也就是服务端和客户端的通道一直处于开启状态,推荐场景:服务器性能比较好,客户端连接数量比较少。

(2)一次性批量提交数据,采用短连接方式,也就是我们将数据保存在本地临时缓冲区或者临时表中,界当到达临时一次性批量提交,或者根据定时任务轮询提交,推荐场景:对实时性要求不高。

(3)使用一种特殊的长连接,在指定的某一个时间,服务器没有和客户端通信时断开连接,当客户端向服务端发送请求时再次建立连接。这种场景需要考虑到

       (a)如何在超时后关闭通道,关闭通道后如何再创立连接?

               注:通信是双向的,所以不是说一边关闭连接通道,另一边就不可以朝这边传递数据了。

               关闭通道:服务端和客户端在通道处理器上加上new ReadTimeoutHandler(5) 5分钟无通信关闭。

               创立连接:每次在通信之前判断连接是否为空或者未激活状态,如果不是就重新获取连接。

        (b)客户端在宕机时无需考虑,在机器重启后与服务器建立连接,服务器宕机时客户端怎么和服务端进行连接呢?

                一般客户端会定时向服务端发送监听包监听服务端在线情况,时长一般是服务端休眠时间的两倍。

9.netty的心跳检测

   使用场景:维护服务器集群,主机对下面各服务器进行实时监控,采用netty模拟心跳机制。

    用sigar 获取 机器内存,cpu,网络,存储登情况。需要将下载的sigar的sigar-amd64-winnt.dll(windows64位)文件放到 

JAVE_HOME的bin目录下。

    示例代码逻辑:发送心跳之前 客户端和服务端进行2次握手,进行身份验证,身份验证成功后,服务端起一个定时线程,发送

请求包(心跳包),服务端接收请求包(心跳包)后记录请求包内容,包括服务端的CPU、内存、网络、存储等状态(这些内容

通过sigar的api获取)

   心跳包模拟代码见示例;

10.netty做文件上传下载

netty做文件操作的基础是基于Http协议,所以先了解一下http协议

Http协议是建立在Tcp协议传输协议上的应用层协议,目前针对于WEB开发,在netty中使用Http协议也是异步非阻塞的。

Http协议特点:

  (1)简单:客户端请求服务器时只需要指定URL和携带必要的参数就可以了。

  (2)灵活:Http协议允许传输任何类型的数据对象,传输内容由Http协议头部的Content-Type加以标记。

  (3)无状态:Http协议是无状态的,无状态指的是协议对事物处理没有记忆能力,这意味着如果后续处理需要之前的信息,则它必须要重新获取,体现出Http的设计是为了使网络传输更加的轻量级,敏捷,负载较轻。

Http协议组成部分:

    请求行

    请求头

    请求正文(实体部分)

Http协议请求方式:

     GET(获取Request-URI所标识的资源)、POST(在Request-URI所标识的资源附加新的请求信息)、HEAD(请求获取

Request-URI所标识的资源响应头)、PUT(请求服务器存储一个资源,并用Request-URI作位标识)、DELETE(请求服务器删

除Request-URI所标识的资源)、TRACE(请求服务器回送收到的请求信息,主要用来测试和诊断)、CONNECT(保留将来使

用)、OPTIONS(请求查询服务器的性能和相关资源的选项和需求)

Http协议的响应信息:

    1xx:提示信息,表示请求已经接收,继续处理。

    2xx:成功,表示请求已经接收成功。

    3xx:重定向,要完成的操作必须进行更进一步的操作。

    4xx:客户端错误。可能是请求语法错误或者请求无法实现。

    5xx:服务器端错误,服务器未能处理请求(可能内部异常)。

netty做文件处理示例代码逻辑:

服务端下载:

    在文件处理之前对请求进行http解码码、http消息聚合(将多个消息转换成单一的FullHttpRequest)、http编码、有需要的话加

入chunked处理器(支持大文件传输,底层是分段传输)。

    上续操作直接把请求封装成FullHttpRequest,然后基本的几重判断,如请求方式、请求编码结果是否存在、请求资源是否存在

等。

    如果请求遍历目录,手动构造html见面,遍历请求内容,然后封装一个DefaultFullHttpResponse做返回对象,设置头部信息为

text/html,再将httpResponse写入处理器上下文。

    如果请求下载资源,用RandomAccessFile去获取资源,构建一个ChunkedFileRandomAccessFile写入,声明传输的内容

区间和每次传输的大小,添加传输监听以获取传输进度。如果使用chunked的话最后要再发一个空消息体,表示所有消息体已成

功发送。

    附录:菜鸟一枚,可能写的不是很好,啊哈哈哈,代码我没有上传githup,所以需要代码的码友留个qq吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值