RPC 通信是大型服务框架的核心
我们经常讨论微服务,首要应该了解的就是微服务的核心到底是什么,这样我们在做技术选型时,才能更准确地把握需求。

就我个人理解,我认为微服务的核心是远程通信和服务治理。远程通信提供了服务之间通信的桥梁,服务治理则提供了服务的后勤保障。所以,我们在做技术选型时,更多要考虑的是这两个核心的需求。

服务的拆分增加了通信的成本,特别是在一些抢购或者促销的业务场景中,如果服务之间存在方法调用,比如,抢购成功之后需要调用订单系统、支付系统、券包系统等,这种远程通信就很容易成为系统的瓶颈。所以,在满足一定的服务治理需求的前提下,对远程通信的性能需求就是技术选型的主要影响因素。

目前,很多微服务框架中的服务通信是基于 RPC 通信实现的,在没有进行组件扩展的前提下,SpringCloud 是基于 Feign 组件实现的 RPC 通信(基于 Http+Json 序列化实现), Dubbo 是基于 SPI 扩展了很多 RPC 通信框架,包括 RMI、Dubbo、Hessian 等 RPC 通信框架(默认是 Dubbo+Hessian 序列化)。不同的业务场景下,RPC 通信的选择和优化标准也不同。

我们部门在选择微服务框架时,选择了 Dubbo。当时的选择标准就是RPC 通信可以支持抢购类的高并发,在这个业务场景中,请求的特点是瞬时高峰、请求量大和传入、传出参数数据包较小。而 Dubbo 中的 Dubbo 协议就很好地支持了这个请求。

无论从响应时间还是吞吐量上来看,单一 TCP 长连接+Protobuf 序列化实现的 RPC 通信框架都有着非常明显的优势。

在高并发场景下,我们选择后端服务框架或者中间件部门自行设计服务框架时,RPC 通信是重点优化的对象。

什么是 RPC 通信
无论是微服务、SOA、还是 RPC 架构,它们都是分布式服务架构,都需要实现服务之间的互相通信,我们通常把这种通信统称为 RPC 通信。

RPC(Remote Process Call),即远程服务调用,是通过网络请求远程计算机程序服务的通信技术。RPC 框架封装好了底层网络通信、序列化等技术,我们只需要在项目中引入各个服务的接口包,就可以实现在代码中调用 RPC 服务同调用本地方法一样。正因为这种方便、透明的远程调用,RPC 被广泛应用于当下企业级以及互联网项目中,是实现分布式系统的核心。

RMI(Remote Method Invocation)是 JDK 中最先实现了 RPC 通信的框架之一,RMI的实现对建立分布式 Java 应用程序至关重要,是 Java 体系非常重要的底层技术,很多开源的 RPC 通信框架也是基于 RMI 实现原理设计出来的,包括 Dubbo 框架中也接入了RMI 框架。接下来我们就一起了解下 RMI 的实现原理,看看它存在哪些性能瓶颈有待优化。

RMI:JDK 自带的 RPC 通信框架
目前 RMI 已经很成熟地应用在了 EJB 以及 Spring 框架中,是纯 Java 网络分布式应用系统的核心解决方案。RMI 实现了一台虚拟机应用对远程方法的调用可以同对本地方法的调用一样,RMI 帮我们封装好了其中关于远程通信的内容。

RMI 的实现原理
RMI 远程代理对象是 RMI 中最核心的组件,除了对象本身所在的虚拟机,其它虚拟机也可以调用此对象的方法。而且这些虚拟机可以不在同一个主机上,通过远程代理对象,远程应用可以用网络协议与服务进行通信。

RMI 在高并发场景下的性能瓶颈
Java 默认序列化
RMI 的序列化采用的是 Java 默认的序列化方式,性能并不是很好,而且其它语言框架也暂时不支持 Java 序列化。

TCP 短连接
由于 RMI 是基于 TCP 短连接实现,在高并发情况下,大量请求会带来大量连接的创建和销毁,这对于系统来说无疑是非常消耗性能的。

阻塞式网络 I/O
网络通信存在 I/O 瓶颈,如果在 Socket 编程中使用传统的 I/O 模型,在高并发场景下基于短连接实现的网络通信就很容易产生 I/O 阻塞,性能将会大打折扣。

一个高并发场景下的 RPC 通信优化路径
SpringCloud 的 RPC 通信和 RMI 通信的性能瓶颈就非常相似。SpringCloud 是基于 Http通信协议(短连接)和 Json 序列化实现的,在高并发场景下并没有优势。

RPC 通信包括了建立通信、实现报文、传输协议以及传输数据编解码等操作,接下来我们就从每一层的优化出发,逐步实现整体的性能优化。

  1. 选择合适的通信协议
    要实现不同机器间的网络通信,我们先要了解计算机系统网络通信的基本原理。网络通信是两台设备之间实现数据流交换的过程,是基于网络传输协议和传输数据的编解码来实现的。其中网络传输协议有 TCP、UDP 协议,这两个协议都是基于 Socket 编程接口之上,为某类应用场景而扩展出的传输协议。

基于 TCP 协议实现的 Socket 通信是有连接的,而传输数据是要通过三次握手来实现数据传输的可靠性,且传输数据是没有边界的,采用的是字节流模式。

基于 UDP 协议实现的 Socket 通信,客户端不需要建立连接,只需要创建一个套接字发送数据报给服务端,这样就不能保证数据报一定会达到服务端,所以在传输数据方面,基于UDP 协议实现的Socket 通信具有不可靠性。UDP 发送的数据采用的是数据报模式,每个UDP 的数据报都有一个长度,该长度将与数据一起发送到服务端。

通过对比,我们可以得出优化方法:为了保证数据传输的可靠性,通常情况下我们会采用TCP 协议。如果在局域网且对数据传输的可靠性没有要求的情况下,我们也可以考虑使用UDP 协议,毕竟这种协议的效率要比 TCP 协议高。

  1. 使用单一长连接
    如果是基于 TCP 协议实现 Socket 通信,我们还能做哪些优化呢?

服务之间的通信不同于客户端与服务端之间的通信。客户端与服务端由于客户端数量多,基于短连接实现请求可以避免长时间地占用连接,导致系统资源浪费。

但服务之间的通信,连接的消费端不会像客户端那么多,但消费端向服务端请求的数量却一样多,我们基于长连接实现,就可以省去大量的 TCP 建立和关闭连接的操作,从而减少系统的性能消耗,节省时间。

  1. 优化 Socket 通信
    建立两台机器的网络通信,我们一般使用 Java 的 Socket 编程实现一个 TCP 连接。传统的 Socket 通信主要存在 I/O 阻塞、线程模型缺陷以及内存拷贝等问题。我们可以使用比较成 熟的通信框架,比如 Netty。Netty4 对 Socket 通信编程做了很多方面的优化,具体见下方。

实现非阻塞 I/O:多路复用器 Selector 实现了非阻塞 I/O 通信。
高效的 Reactor 线程模型:Netty 使用了主从 Reactor 多线程模型,服务端接收客户端请求连接是用了一个主线程,这个主线程用于客户端的连接请求操作,一旦连接建立成功,将会监听 I/O 事件,监听到事件后会创建一个链路请求。

链路请求将会注册到负责 I/O 操作的 I/O 工作线程上,由 I/O 工作线程负责后续的 I/O 操作。利用这种线程模型,可以解决在高负载、高并发的情况下,由于单个 NIO 线程无法监听海量客户端和满足大量 I/O 操作造成的问题。

串行设计:服务端在接收消息之后,存在着编码、解码、读取和发送等链路操作。如果这些操作都是基于并行去实现,无疑会导致严重的锁竞争,进而导致系统的性能下降。为了提升性能,Netty 采用了串行无锁化完成链路操作,Netty 提供了 Pipeline 实现链路的各个操作在运行期间不进行线程切换。

零拷贝:一个数据从内存发送到网络中,存在着两次拷贝动作,先是从用户空间拷贝到内核空间,再是从内核空间拷贝到网络 I/O 中。而 NIO 提供的ByteBuffer 可以使用 Direct Buffers 模式,直接开辟一个非堆物理内存,不需要进行字节缓冲区的二次拷贝,可以直接将数据写入到内核空间。

除了以上这些优化,我们还可以针对套接字编程提供的一些 TCP 参数配置项,提高网络吞吐量,Netty 可以基于 ChannelOption 来设置这些参数。
TCP_NODELAY:TCP_NODELAY 选项是用来控制是否开启 Nagle 算法。Nagle 算法通过缓存的方式将小的数据包组成一个大的数据包,从而避免大量的小数据包发送阻塞网络,提高网络传输的效率。我们可以关闭该算法,优化对于时延敏感的应用场景。

SO_RCVBUF 和 SO_SNDBUF:可以根据场景调整套接字发送缓冲区和接收缓冲区的大小。

SO_BACKLOG:backlog 参数指定了客户端连接请求缓冲队列的大小。服务端处理客户端连接请求是按顺序处理的,所以同一时间只能处理一个客户端连接,当有多个客户端进来的时候,服务端就会将不能处理的客户端连接请求放在队列中等待处理。

SO_KEEPALIVE:当设置该选项以后,连接会检查长时间没有发送数据的客户端的连接状态,检测到客户端断开连接后,服务端将回收该连接。我们可以将该时间设置得短一些,来提高回收连接的效率。

  1. 量身定做报文格式
    接下来就是实现报文,我们需要设计一套报文,用于描述具体的校验、操作、传输数据等内容。为了提高传输的效率,我们可以根据自己的业务和架构来考虑设计,尽量实现报体小、满足功能、易解析等特性。

  2. 编码、解码
    序列化编码和解码的过程,对于实现一个好的网络通信协议来说, 兼容优秀的序列化框架是非常重要的。如果只是单纯的数据对象传输,我们可以选择性能相对较好的 Protobuf 序列化,有利于提高网络通信的性能。

  3. 调整 Linux 的 TCP 参数设置选项
    如果 RPC 是基于 TCP 短连接实现的,我们可以通过修改 Linux TCP 配置项来优化网络通信。
    我们可以通过 sysctl -a | grep net.xxx 命令运行查看 Linux 系统默认的的 TCP 参数设置, 如果需要修改某项配置,可以通过编辑 vim/etc/sysctl.conf,加入需要修改的配置项, 并通过 sysctl -p 命令运行生效修改后的配置项设置。通常我们会通过修改以下几个配置项来提高网络吞吐量和降低延时。

网络通信优化之通信协议:如何优化RPC网络通信?相关推荐

  1. 10 | 网络通信优化之通信协议:如何优化RPC网络通信?

    RPC 通信是大型服务框架的核心 微服务的核心是远程通信和服务治理.远程通信提供了服务之间通信的桥梁,服务治理则提供了服务的后勤保障.所以,我们在做技术选型时,更多要考虑的是这两个核心的需求. 目前, ...

  2. 基于量桨搭建AI量子通信模拟平台,优化量子通信协议

    点击左上方蓝字关注我们 项目背景 现阶段量子通讯协议的设计和优化依然停留在人工处理的阶段,距离迈向系统化工程处理依然有着不小的距离.导致目前量子通讯协议从研发到实验验证这一整套流程面临着周期长.成本高 ...

  3. 【前端优化】雅虎前端优化的35条军规(存档备用)

    目录 内容部分 css部分 js部分 javascript, css 图片 cookie 移动端 服务器 摘要:无论是在工作中,还是在面试中,web前端性能的优化都是很重要的,那么我们进行优化需要从哪 ...

  4. AI System 人工智能系统 TVM深度学习编译器 DSL IR优化 计算图 编译 优化 内存内核调度优化 DAG 图优化 DFS TaiChi 函数注册机 Registry

    DSL 领域专用语言 TVM深度学习编译器 AI System 人工智能系统 参考项目 TaiChi 三维动画渲染物理仿真引擎DSL TVM 深度学习DSL 密集计算DSL LLVM 模块化编译器 编 ...

  5. Android 网络性能优化-概述和DNS优化

    1. 移动App网络优化背景 对于Android来说,开发者可以轻松的打造一套 MVP + Retrofit + RxJava 的框架来处理所有的网络请求.因为 Retrofit下层封装的OkHttp ...

  6. oracle 优化器 失效,oracle 优化器 不走索引原因

    SQL优化器简介 基于规则的优化器 .总是使用索引 .总是从驱动表开始(from子句最右边的表) .只有在不可避免的情况下,才使用全表扫描 .任何索引都可以 基于成本的优化器 .需要表.索引的统计资料 ...

  7. mysql 优化_常用MySQL优化

    1.大批量插入数据优化 (1)对于MyISAM存储引擎的表,可以使用:DISABLE KEYS 和 ENABLE KEYS 用来打开或者关闭 MyISAM 表非唯一索引的更新. ALTER TABLE ...

  8. 机器学习+优化问题的种类、如何优化、凸优化、非凸优化、对偶问题、KKT条件

    机器学习+优化问题的种类.如何优化.凸优化.非凸优化.对偶问题.KKT条件 目录

  9. 性能优化指南:性能优化的一般性原则与方法

    作为一个程序员,性能优化是常有的事情,不管是桌面应用还是web应用,不管是前端还是后端,不管是单点应用还是分布式系统.本文从以下几个方面来思考这个问题:性能优化的一般性原则,性能优化的层次,性能优化的 ...

  10. 双目标帕累托优化_结构力学中的优化分析(3) —— 结构优化分析

    引言 上文中,我们主要介绍了优化分析的基本类型. 蒙特遇见卡罗:结构力学中的优化分析(1) -- 优化方法基本概念​zhuanlan.zhihu.com 蒙特遇见卡罗:结构力学中的优化分析(2) -- ...

最新文章

  1. linux与windows互传文件、用户与用户组管理、密码配置文件
  2. 发送接收图片_国际空间站将在8月4-5号发送SSTV图片,普通对讲机可接收
  3. 十荟团关停全国业务 社区电商开启“降本增效”大突围
  4. linux设置环境变量_Linux怎么设置系统环境变量之export命令详解
  5. C++新特性探究(9.1):functor仿函数探究
  6. 循环输出26个字母C语言,菜鸟求助,写一个随机输出26个英文字母的程序
  7. 27. Minimize casting
  8. latex做ppt_用Markdown可以做什么
  9. Python爬虫入门教程21:文档的爬取
  10. 技嘉 linux设置u盘启动项,技嘉主板bios设置u盘启动教程
  11. ResHacker使用小解
  12. 2018年sfdc工作总结_前端绑数据 前端定义勿用驼峰命名法
  13. 如何更改VUE项目运行名称以及Logo图标
  14. python 将变量保存到文件里
  15. 基本数据类型 int操作 bool布尔操作 str字符串操作 for in 循环
  16. 中国汽水制造商市场趋势报告、技术动态创新及市场预测
  17. IT小白如何从职场新人到行业翘楚,看一个IT高管的发展路线
  18. 气动调节阀运转、查看及维护保养
  19. npm run dev关闭终端后如何停止?退出vscode不行, 杀掉进程node.exe就行  Port 3030 is already in use [nodemon] app crashed
  20. 「完整版」小说《倾心倾情倾了所有》在线阅读

热门文章

  1. 装了linux的u盘格式化,u盘格式化容量变小了u盘安装linuxcentos
  2. java线程栅栏_用栅栏(CyclicBarrier)实现高并发测试
  3. 【观察】重塑质量管理体系,筑起车企数字化转型“护城河”
  4. DSPE-PEG-TAT,磷脂-聚乙二醇-靶向穿膜肽TAT,一种磷脂PEG肽
  5. 【2023秋招】10月8日美团校招两道题
  6. 9个最适合Elementor的免费主题【官方推荐】
  7. Redux-Router
  8. 小球弹跳及MATLAB实现
  9. 置换和轮换(新姿势,摘自黑书)
  10. HDOJ-----5773The All-purpose Zero(LIS)