手写RPC框架系列(六) - 构建RPC通信基础
手写RPC框架系列(六) - 构建RPC通信基础
从本章节开始,我们将逐步切换到RPC框架的核心部分——通信。本章节主要涵盖以下内容:
- 通信协议的选择
- 序列化与反序列化方案
- 常用的网络通信模型
- Java生态中常用的网络编程框架
1. 网络通信协议的选择
RPC 框架的核心是网路通信,这决定了它的性能上限。在选择通信协议时,通常会有以下几个选择:
- TCP 协议: TCP 协议可以提供可靠的、面向连接的服务。 TCP 保证了数据的顺序性以及完整性,目前市面上大部分的 RPC 框架都是基于 TCP 协议。
- UDP 协议: UDP 是一种无连接的协议,通常用于哪些不需要可靠传输的场景。 典型的场景就是在线视频传输、直播流、直播弹幕信息等,这种信息丢失一小部分大多数时候对业务并没有特别大的影响。
- HTTP 协议: 这个协议太常见不过了,目前你所能访问的网站都是大部分基于 HTTP 协议进行通信的。
综合考虑可靠性和性能,我们选择基于TCP协议进行通信。
2. 序列化以及反序列化
数据在网络中进行传输前需要先序列化,接收到后进行反序列化。高效的序列化可以极大的影响 RPC框架的性能。 比较常见的序列化协议包括:
序列化协议 | 二进制/文本 | 主要特点 | 常用于 |
---|---|---|---|
JSON | 文本 | 易于阅读和编辑,跨语言支持 | Web APIs, 配置文件 |
XML | 文本 | 可扩展性强,支持复杂数据结构 | Web服务, 配置管理 |
Protocol Buffers | 二进制 | 高效的编码,跨平台 | 通信协议, 数据存储 |
Thrift | 二进制/文本 | 支持多种编程语言,可以定义数据类型和服务接口 | 跨语言服务开发 |
MessagePack | 二进制 | 小巧,速度快 | 缓存数据, 实时数据传输 |
BSON | 二进制 | 类似JSON但为二进制,支持更多数据类型 | MongoDB文档存储 |
这些序列化协议各有优劣,选择合适的协议需要考虑实际应用场景的需求。如是否需要跨平台兼容、数据传输的效率、是否需要支持复杂的数据结构等因素。
考虑到易用性和通用性,我们选择JSON作为序列化协议。
3. 常用的网络通信模型
3.1 同步 vs 异步
- 同步调用:客户端发起调用后需要等待服务端响应,简化编程模型但降低了整体效率。
- 异步调用:客户端发起调用后可立即执行其他任务,通过回调或Future获取结果,提高并发性能。
在我们的RPC框架中,我们将支持异步通信模型,以提高处理效率和系统扩展性。
3.2 阻塞 vs 非阻塞
- 阻塞调用:调用线程会一直等待操作完成,可能导致资源浪费。
- 非阻塞调用:调用线程可在等待时执行其他任务,更好地利用资源。
由于RPC框架需要高效处理大量并发请求,我们采用非阻塞模型,以提高系统的响应速度和吞吐量。
4. Java 生态的网络编程库
目前 Java 生态的网络编程库比较常用的有下面三个:
4.1 原生 Java NIO
Java NIO(New Input/Output)是Java提供的一种新的输入输出模型,与传统的IO模型相比,它更加灵活,能够更好地处理高并发数据。Java NIO主要基于通道(Channel)和缓冲区(Buffer)的概念,支持非阻塞的IO操作。通道类似于传统IO中的流,但通道可以进行双向数据传输,而且可以异步地读写。
Java NIO的主要组件包括:
- 缓冲区(Buffer):数据的临时存储区,可以读写数据。
- 通道(Channel):连接到数据源如文件或套接字的开放连接,可以进行数据读写操作。
- 选择器(Selector):与非阻塞模式一起使用,可以检测多个注册通道上的事件(如数据到达),从而允许单线程管理多个通道。
尽管Java NIO提供了高性能的数据处理能力,但它的编程复杂度相比传统IO较高,需要处理缓冲区的状态控制、通道的选择等问题。通常是不推荐直接用原生的 Java NIO 去进行生产环境的软件开发。
4.2 Netty
Netty 是基于Java NIO的客户端-服务器框架,提供了易于使用的 API 来创建高性能、高可靠性的网络应用程序。Netty 广泛应用于游戏服务器、通信中间件、微服务架构等多种领域。相比于原生 NIO,Netty 解决了很多底层细节,使得开发者能够更专注于业务逻辑的实现。
Netty 的主要特点包括:
- 异步和事件驱动:Netty 基于事件驱动模型,可以处理数以千计的并发连接。
- 简化的线程模型:Netty 内部实现了线程池,通过有效的资源管理优化了线程的使用。
- 高度可定制:Netty 提供了灵活的编程接口和多种内置的协议支持,如HTTP、WebSocket等。
- 内存管理:通过高效的缓冲区管理机制,减少内存复制次数,提升性能。
Netty 拥有活跃的社区和广泛的文档资料,有助于解决开发中的问题,在生产环境中使用的比例非常高。
4.3 Mina
Apache Mina 是另一种基于Java NIO的网络应用框架,与Netty类似,它也是设计用于开发高性能和高可扩展性的网络应用程序。Mina的设计哲学注重简单性和会话抽象,使得开发网络应用变得更简单。
Mina的核心特点包括:
- 高级的抽象:Mina将网络通信的复杂性抽象成简单的会话和服务概念。
- 可扩展性:提供了一套Filter接口,用户可以通过这些接口来扩展或修改框架的功能。
- 支持多种协议:Mina不仅支持TCP和UDP,还可以通过编写自定义的协议解码器来支持其他协议。
- 灵活的线程模型:允许用户根据需要自定义线程模型,以适应不同的应用场景。
相对于Netty,Mina 在某些高性能场景下可能表现不如 Netty,尤其是在处理大量并发连接时。并且还有个致命的问题,Mina 的社区活跃度和可用资源较少,可能在遇到问题时找到帮助的速度不如 Netty。
这里需要注意的是,虽然 Mina 和 Netty 看起来很强大,但是底层都是基于 Java NIO,本质上还是对 NIO 的 API 进行了封装。 但Mina更侧重于提供简单易用的接口,而Netty提供了更丰富的特性和更高的性能优化可能。
为了更加贴近于生产环境,我们决定选用 Netty 作为网络通信框架。
5. 总结
通过上面章节的介绍,我们应该对通信有了一些基本的了解,包括:
- 通信协议是什么、常用的通信协议有哪些、各种通信协议的优缺点。
- 序列化以及反序列化是什么、常用的序列化协议、各种序列化协议的优缺点。
- 常用的网络通信模型有哪些、各种模型的优缺点。
- 常用的网络编程框架有哪些、各自的优缺点、选型建议等。
这样就可以打通一整个通信链路,即:
- 使用 TCP 作为通信协议
- 使用 JSON 作为通信的数据格式
- 使用 异步 + 非阻塞的通信模式
- 使用 Netty 框架来简化 Java 代码的工作量
6. 参考链接
https://baike.baidu.com/item/TCP/33012 百度百科 - 传输控制协议 - TCP
https://baike.baidu.com/item/UDP/571511 百度百科 - 用户数据报协议 - UDP
https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE 维基 - 传输控制协议 - TCP
https://zh.wikipedia.org/wiki/%E7%94%A8%E6%88%B7%E6%95%B0%E6%8D%AE%E6%8A%A5%E5%8D%8F%E8%AE%AE 维基 - 用户数据报协议 - UDP
https://developer.mozilla.org/zh-CN/docs/Web/HTTP 超文本传输协议 - HTTP
https://en.wikipedia.org/wiki/Comparison_of_data-serialization_formats 维基 - 序列化协议对比
https://tech.meituan.com/2015/02/26/serialization-vs-deserialization.html 美团 - 序列化和反序列化
https://netty.io/ Netty 官网
https://mina.apache.org/ Mina官网
Unix网络编程:卷1 - 5种 I/O 模型