「蘑菇街技术」每个人都想听的技术解析--Netty( 三 )


「蘑菇街技术」每个人都想听的技术解析--Netty文章插图
在Netty中接受和发送ByteBuffer可以采用DirectBuffer , DirectBuffer使用堆外直接内存进行socket读写 , 不需要进行字节缓冲区的二次拷贝 。 它的原理就是会将这个byte数组直接通过调用C/C++方法放在直接内存中 , 这样也就避免了一次从java堆上到堆外内存的拷贝 , 而创建之后如何关联到这块数据呢,是通过address这个变量去做引用的 。
「蘑菇街技术」每个人都想听的技术解析--Netty文章插图
3、减少java堆内之间的内存拷贝:CompositeByteBuf
这个是Netty自己实现的 , 它的作用是聚合多个ByteBuffer对象 , 然后对外提供统一的ByteBuf接口 , 用户可以像操作一个Buffer那样方便的对组合Buffer进行操作 , 避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer 。 但是需要注意的是这种组合是逻辑上的组合 , 不是物理上的给你放在一块连续的内存区域了 。
协议相关
数据协议表示采用什么样的通信协议 , 是HTTP还是内部私有协议 。 协议的选择不同 , 性能模型也就不同 。 而与协议相关的就是序列化框架 , 这部分我们稍后会详细的讲解 。
半包、粘包
假设我们使用tcp发送两条消息 , ABC和DEF , 那么接收方接受到的消息一定是这种格式的嘛?不一定 , 接收方可能一次就接受完了 , 也有可能分成了三四次接受完数据;而前者就是粘包问题 , 如我们在第一次就接受到了ABCDEF , 一般是由于发送方每次写入的数据小于套接字缓冲区大小或者接收方读取套接字缓冲区数据不够及时 , 而后者就是半包问题 , 如可能我们分批接收到AB,CD , EF.一般是由于发送方每次写入的数据 大于套接字缓冲区大小或者发送的数据大于协议的MTU(Maximum Transmission Unit,最大传输单元) , 必须拆包 。
而导致这种现象的本质原因是什么呢?UDP会出现这种情况嘛?其实本质原因就是Tcp是流失协议 , 在tcp中消息时没有边界的 , 而udp就不会出现这种问题 , 因为在udp中数据会像包裹一样被分批打包 , 虽然一次运输多个 , 但是每个包都有界限 , 接收方再一个个签收 , 所以无粘包、半包问题 。 那么当我们使用Tcp时 , 应该如何解决这个问题呢?
半包粘包在Netty中的解决方式
其实解决方式的本质也就是给消息寻找一个边界 , Netty为我们提供了如下三种解决方式:
「蘑菇街技术」每个人都想听的技术解析--Netty文章插图
解码器相关类图如下:
「蘑菇街技术」每个人都想听的技术解析--Netty文章插图
编码与解码
以解码为例 , 在Netty中一般会有两次解码 , 而上述解决粘包、半包问题的解码器 , 我们一般称之为一次解码 , 将原始数据转换为用户数据 , 相关解码器都继承自ByteToMessageDecoder , 但是这个时候我们还是没法进行业务处理 , 我们还需要进行二次编码 , 将Message转为我们可直接使用的Java Object对象 , 相关解码器都继承自MessageToMessageDecoder(编解码的过程通常也称作反序列化/序列化操作) 。
而序列化框架的选择也是十分有考究的 , 一般我们选择序列化框架会从空间占用、编解码速度、可读性、是否支持多语言这几个方面去考虑 , 而令人欣喜的是 Netty对这些高性能的序列化框架也都有所支持 , 如下图所示 。
「蘑菇街技术」每个人都想听的技术解析--Netty文章插图
线程模型
线程模型涉及到如何读取数据包 , 读取之后的编解码在哪个线程中进行 , 编解码后的消息如何派发等方面 。 实际理解就是如何分配干活的人 , 最好让大家各司其职都不能闲着 , 所以线程模型设计得不同 , 对性能也会有很多影响 。
Netty使用了Reactor线程模型 , 包括了单线程模型、多线程模型和主从多线程模型 。
1. Reactor单线程模型
如下图所示,所有的IO操作都由一个NIO线程中进行处理 , 单线程模型中单个线程处理建立连接并将接收到的消息分发到对应的handler中 , 但是单线程模型在处理大量连接的时候性能通常不能满足要求 。
「蘑菇街技术」每个人都想听的技术解析--Netty文章插图
2. Reactor多线程模型
如下图所示 , Rector多线程模型与单线程模型最大的区别就是设计了一个NIO线程池处理I/O操作 , 并且使用一个专门的NIO线程用于监听服务端、接收客户端的TCP连接请求 。 Reactor多线程模型足以应对大多数的场景了;但是当百万客户端并发连接时 , 单个Acceptor线程可能会存在性能不足的问题 , 为了解决这种问题 , 就出现了主从Reactor多线程模型 。