Java 架构师面试之 Netty 面试专题及答案(共 10 题,含详细解答
JSON,是一种轻量级的数据交换格式 优点:兼容性高、数据格式比较简单,易于读写、序列化后数据较小,可扩展性好,兼容性好、与 XML 相比,其协议比较简单,解析速度比较快。 缺点:数据的描述性比 XML 差、不适合性能要求为 ms 级别的情况、额外空间开销比较大。 适用场景(可替代XML):跨防火墙访问、可调式性要求高、基于 Webbrowser 的 Ajax 请求、传输数据量相对小,实时性要求相对低(例如秒级别)的服务。
Fastjson,采用一种“假定有序快速匹配”的算法。 优点:接口简单易用、目前 java 语言中最快的 json 库。 缺点:过于注重快,而偏离了“标准”及功能性、代码质量不高,文档不全。 适用场景:协议交互、 Web 输出、 Android 客户端
Thrift,不仅是序列化协议,还是一个 RPC 框架。 优点:序列化后的体积小, 速度快、支持多种语言和丰富的数据类型、对于数据字段的增删具有较强的兼容性、支持二进制压缩编码。 缺点:使用者较少、跨防火墙访问时,不安全、不具有可读性,调试代码时相对困难、不能与其他传输层协议共同使用(例如 HTTP)、无法支持向持久层直接读写数据,即不适合做数据持久化序列化协议。 适用场景:分布式系统的 RPC 解决方案
Avro, Hadoop 的一个子项目,解决了 JSON 的冗长和没有 IDL 的问题。 优点:支持丰富的数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩的二进制数据形式、可以实现远程过程调用 RPC、支持跨编程语言实现。 缺点:对于习惯于静态类型语言的用户不直观。 适用场景:在 Hadoop 中做 Hive、 Pig 和 MapReduce 的持久化数据格式。
Protobuf,将数据结构以.proto 文件进行描述,通过代码生成工具可以生成对应数据结构的 POJO 对象和 Protobuf 相关的方法和属性。 优点:序列化后码流小,性能高、结构化数据存储格式( XML JSON 等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档更容易管理和维护。 缺点:需要依赖于工具生成代码、支持的语言相对较少,官方只支持 Java 、 C++ 、 python。 适用场景:对性能要求高的 RPC 调用、具有良好的跨防火墙的访问属性、适合应用层对象的持久化
其它 protostuff 基于 protobuf 协议,但不需要配置 proto 文件,直接导包即可 Jboss marshaling 可以直接序列化 java 类, 无须实 java.io.Serializable 接口 Message pack 一个高效的二进制序列化格式 Hessian 采用二进制协议的轻量级 remoting onhttp 工具 kryo 基于 protobuf 协议,只支持 java 语言,需要注册( Registration),然后序列化( Output),反序列化( Input)
7.如何选择序列化协议? 具体场景 对于公司间的系统调用,如果性能要求在 100ms 以上的服务,基于 XML 的 SOAP 协议是一个值得考虑的方案。 基于 Web browser 的 Ajax,以及 Mobile app 与服务端之间的通讯, JSON 协议是首选。对于性能要求不太高,或者以动态类型语言为主,或者传输数据载荷很小的的运用场景, JSON 也是非常不错的选择。 对于调试环境比较恶劣的场景,采用 JSON 或 XML 能够极大的提高调试效率,降低系统开发成本。 当对性能和简洁性有极高要求的场景, Protobuf, Thrift, Avro 之间具有一定的竞争关系。 对于 T 级别的数据的持久化应用场景, Protobuf 和 Avro 是首要选择。如果持久化后的数据存储在 hadoop 子项目里, Avro 会是更好的选择。 对于持久层非 Hadoop 项目,以静态类型语言为主的应用场景, Protobuf 会更符合静态类型语言工程师的开发习惯。 由于 Avro 的设计理念偏向于动态类型语言,对于动态语言为主的应用场景, Avro 是更好的选择。 如果需要提供一个完整的 RPC 解决方案, Thrift 是一个好的选择。 如果序列化之后需要支持不同的传输层协议,或者需要跨防火墙访问的高性能场景,Protobuf 可以优先考虑。 protobuf 的数据类型有多种: bool、 double、 float、 int32、 int64、 string、 bytes、 enum、message。 protobuf 的限定符: required: 必须赋值,不能为空、 optional:字段可以赋值,也可以不赋值、 repeated: 该字段可以重复任意次数(包括 0 次)、枚举;只能用指定的常量集中的一个值作为其值; protobuf 的基本规则:每个消息中必须至少留有一个 required 类型的字段、包含 0 个或多个 optional 类型的字段; repeated 表示的字段可以包含 0 个或多个数据; [1,15]之内的标识号在编码的时候会占用一个字节(常用), [16,2047]之内的标识号则占用 2 个字节,标识号一定不能重复、使用消息类型,也可以将消息嵌套任意多层,可用嵌套消息类型来代替组。 protobuf 的消息升级原则:不要更改任何已有的字段的数值标识;不能移除已经存在的 required 字段, optional 和 repeated 类型的字段可以被移除,但要保留标号不能被重用。新添加的字段必须是 optional 或 repeated。因为旧版本程序无法读取或写入新增的 required 限定符的字段。编译器为每一个消息类型生成了一个.java 文件,以及一个特殊的 Builder 类(该类是用来创建消息类接口的)。如:
UserProto.User.Builder builder =UserProto.User.newBuilder();builder.build();
Netty 中的使用: ProtobufVarint32FrameDecoder 是用于处理半包消息的解码类; ProtobufDecoder(UserProto.User.getDefaultInstance())这是创建的 UserProto.java 文件中的解码类; ProtobufVarint32LengthFieldPrepender 对 protobuf 协议的消息头上加上一个长度为 32 的整形字段,用于标志这个消息的长度的类; ProtobufEncoder 是编码类将 StringBuilder 转换为 ByteBuf 类型: copiedBuffer()方法
8.Netty 的零拷贝实现? Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。堆内存多了一次内存拷贝, JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入 Socket 中。 ByteBuffer 由 ChannelConfig 分配,而 ChannelConfig 创建 ByteBufAllocator 默认使用 Direct BufferCompositeByteBuf 类可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。 add
Components 方法将 header 与 body 合并为一个逻辑上的 ByteBuf, 这两个 ByteBuf 在 CompositeByteBuf 内部都是单独存在的, CompositeByteBuf 只是逻辑上是一个整体通过 FileRegion 包装的 FileChannel.tranferTo 方法 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。通过 wrap 方法, 我们可以将 byte[] 数组、 ByteBuf、 ByteBuffer 等包装成一个 NettyByteBuf 对象, 进而避免了拷贝操作。Selector BUG:若 Selector 的轮询结果为空,也没有 wakeup 或新消息处理,则发生空轮询, CPU 使用率 100%; Netty 的解决办法:对 Selector 的 select 操作周期进行统计,每完成一次空的 select 操作进行一次计数,若在某个周期内连续发生 N 次空轮询,则触发了 epoll 死循环 bug。重建 Selector,判断是否是其他线程发起的重建请求,若不是则将原 SocketChannel 从旧的 Selector 上去除注册,重新注册到新的 Selector 上,并将原来的 Selector 关闭。
9.Netty 的高性能表现在哪些方面? 心跳,对服务端:会定时清除闲置会话 inactive(netty5),对客户端:用来检测会话是否断开,是否重来,检测网络延迟,其中 idleStateHandler 类 用来检测会话状态 串行无锁化设计,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。表面上看,串行化设计似乎 CPU 利用率不高,并发程度不够。但是,通过调整 NIO 线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。 可靠性,链路有效性检测:链路空闲检测机制,读/写空闲超时机制;内存保护机制:通过内存池重用 ByteBuf;ByteBuf 的解码保护;优雅停机:不再接收新消息、退出前的预处理操作、资源的释放操作。 Netty 安全性:支持的安全协议: SSL V2 和 V3, TLS, SSL 单向认证、双向认证和第三方 CA 认证。 高效并发编程的体现: volatile 的大量、正确使用; CAS 和原子类的广泛使用;线程安全容器的使用;通过读写锁提升并发性能。 IO 通信性能三原则:传输( AIO)、协议( Http)、线程(主从多线程) 流量整型的作用(变压器):防止由于上下游网元性能不均衡导致下游网元被压垮,业务流中断;防止由于通信模块接受消息过快,后端业务线程处理不及时导致撑死问题。 TCP 参数配置: SO_RCVBUF 和 SO_SNDBUF:通常建议值为 128K 或者 256K; SO_TCPNODELAY: NAGLE 算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量小封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法;
评论