六、Java NIO 深入解读--编解码技术

编解码技术

1.编码和解码

编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码。codec(编解码器) 的组成部分有两个:decoder(解码器)和 encoder(编码器)。encoder 负责把业务数据转换成字节码数据,decoder 负责把字节码数据转换成业务数据。

2.Java序列化的缺点

  • 无法跨语言

  • 序列化后的码流太大

  • 序列化性能太低

3.Netty本身的编码解码的机制和问题分析

Netty 本身自带的 ObjectDecoder 和 ObjectEncoder 可以用来实现 POJO 对象或各种业务对象的编码和解码,底层使用的仍是 Java 序列化技术 , 而 Java 序列化技术本身效率就不高,存在如下问题无法跨语言序列化后的体积太大,是二进制编码的 5 倍多。序列化性能太低。

4. 业界主流的编解码框架

4.1 Google的Protobuf

Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC[远程过程调用 remote procedure call ] 数据交换格式 。

它的特点如下:

  • 结构化数据存储格式(XML,JSON等)

  • 高效的编解码性能

  • 语言无关、平台无关、扩展性好

  • 官方支持Java、C++和Python三种语言。

Protobuf另一个比较吸引人的地方就是它的数据描述文件和代码生成机制,利用数据描述文件对数据结构进行说明的优点如下。

  • 文本化的数据结构描述语言,可以实现语言和平台无关,特别适合异构系统间的集成

  • 通过标识字段的顺序,可以实现协议的前向兼容

  • 自动代码生成,不需要手工编写同样数据结构的C++和Java版本

  • 方便后续的管理和维护。相比于代码,结构化的文档更容易管理和维护

4.2 Facebook的Thrift

Thrift源于Facebook,在2007年Facebook将Thrift作为一个开源项目提交给了Apache基金会。对于当时的Facebook来说,创造Thrift是为了解决Facebook各系统间大数据量的传输通信以及系统之间语言环境不同需要跨平台的特性,因此Thrif可以支持多种程序语言,如C++、C#、Cocoa、Erlang、Haskell、Java、Ocami、Perl、PHP、Python、Ruby。在多种不同的语言之间通信,Thrift可以作为高性能的通信中间件使用,它支持数据(对象)序列化和多种类型的RPC服务。Thrift适用于静态的数据交换,需要先确定好它的数据结构,当数据结构发生变化时,必须重新编辑IDL文件,生成代码和编译,这一点跟其他IDL工具相比可以视为是Thrift的弱项。Thrift适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输,相对于JSON和XML在性能和传输大小上都有明显的优势。

Thrift支持三种比较典型的编解码方式

  • 通用的二进制编解码;

  • 压缩二进制编解码;

  • 优化的可选字段压缩编解码

4.3 JBoss Marshalling

JBossMarshalling是一个Java对象的序列化API包,修正了JDK自带的序列化包的很多问题,但又保持跟java.io.Serializable接口的兼容。同时,增加了一些可调的参数和附加的特性,并且这些参数和特性可通过工厂类进行配置。

相比于传统的Java序列化机制,它的优点如下。

  • 可插拔的类解析器,提供更加便捷的类加载定制策略,通过一个接口即可实现定制

  • 可插拔的对象替换技术,不需要通过继承的方式

  • 可插拔的预定义类缓存表,可以减小序列化的字节数组长度,提升常用类型的对象序列化性能

  • 无须实现java.io.Serializable接口,即可实现Java序列化;

  • 通过缓存技术提升对象的序列化性能。

相比于前面介绍的两种编解码框架,JBossMarshalling更多是在JBoss内部使用,应用范围有限。

Netty架构

  • Reactor通信调度层

  • 职责链ChannelPipeline

  • 业务逻辑编排层(ServiceChannelHandler)

  • 关键架构质量属性

1 .关键架构质量属性

1.1 高性能

“性能是设计出来的,而不是测试出来的”。Netty的架构设计是如何实现高性能的。

(1)采用异步非阻塞的I/O类库,基于Reactor模式实现,解决了传统同步阻塞IO模式下一个服务端无法平滑地处理线性增长的客户端的问题。

(2)TCP接收和发送缓冲区使用直接内存代替堆内存,避免了内存复制,提升了I/O读取和写入的性能。

(3)支持通过内存池的方式循环利用ByteBuf,避免了频繁创建和销毁ByteBuf带来的性能损耗。

(4)可配置的IO线程数、TCP参数等,为不同的用户场景提供定制化的调优参数,满足不同的性能场景。

(5)采用环形数组缓冲区实现无锁化并发编程,代替传统的线程安全容器或者锁

(6)合理地使用线程安全容器、原子类等,提升系统的并发处理能力。

(7)关键资源的处理使用单线程串行化的方式,避免多线程并发访问带来的锁竞争和额外的CPU资源消耗问题

(8)通过引用计数器及时地申请释放不再被引用的对象,细粒度的内存管理降低了GC的频率,减少了频繁GC带来的时延增大和CPU损耗。

1.2 可靠性

1.2.1 链路有效性检测

为了支持心跳,Netty提供了如下两种链路空闲检测机制。

  • 读空闲超时机制:当连续周期T没有消息可读时,触发超时Handler,用户可以基于读空闲超时发送心跳消息,进行链路检测:如果连续N个周期仍然没有读取到心跳消息,可以主动关闭链路。

  • 写空闲超时机制:当连续周期T没有消息要发送时,触发超时Handler,用户可以基于写空闲超时发送心跳消息,进行链路检测;如果连续N个周期仍然没有接收到对方的心跳消息,可以主动关闭链路。

为了满足不同用户场景的心跳定制,Netty提供了空闲状态检测事件通知机制,用户可以订阅空闲超时事件、写空闲超时事件、读或者写超时事件,在接收到对应的空闲事件之后,灵活地进行定制。

1.2.2 内存保护机制

Netty提供多种机制对内存进行保护,包括以下方面:

  • 通过对象引用计数器对Netty的ByteBuf等内置对象进行细粒度的内存申请和释放,对非法的对象引用进行检测和保护。

  • 通过内存池来重用ByteBuf,节省内存。

  • 可设置的内存容量上限,包括ByteBuf、线程池线程数等。

1.2.3 可定制性

Netty的可定制性主要体现在以下几点。

  • 责任链模式:ChannelPipeline基于责任链模式开发,便于业务逻辑的拦截、定制和扩展。

  • 基于接口的开发:关键的类库都提供了接口或者抽象类,如果Netty自身的实现无法满足用户的需求,可以由用户自定义实现相关接口。

  • 提供了大量工厂类,通过重载这些工厂类可以按需创建出用户实现的对象。

  • 提供了大量的系统参数供用户按需设置,增强系统的场景定制性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值