微服务全链路异步化实践
1. 背景
随着公司业务的发展,核心服务流量越来越大,使用到的资源也越来越多。在微服务架构体系中,大部分的业务是基于Java 语言实现的,受限于Java 的线程实现,一个Java 线程映射到一个kernel 线程,造成了高并发场景下线程资源的极大浪费,线程成为提高系统并发和吞吐量的瓶颈。
在微服务架构下,使用同步编程模式时不仅造成了资源的极大浪费,并且在流量发生激增波动的时候,受制于系统资源而无法快速的扩容。本文将探索服务异步化在并发、吞吐量方面对系统带来的提升。
2. 如何快速提高服务吞吐量
首先,以微服务架构中的RPC 服务调用举例,测试和探索在微服务架构中,异步架构如何提高服务的吞吐量和并发。ESA Stack 是OPPO 自研的基础框架技术栈,ESA RPC 是自研的RPC 框架。本节测试服务我们使用ESA RPC 搭建。
关于ESA RPC的详情,可以参考我们之前发布的文章《Dubbo协议解析及ESA RPC实践》。
2.1 服务架构
下图所示为测试环境架构。其中Service A 既是服务端也是客户端,它模拟了生产环境中大部分服务的角色。我们对Service A 分别采用同步模型和纯异步模型进行压测,其中纯异步模型包含了客户端、服务端逻辑的异步处理。Service B 模拟一个耗时为N ms 的下游服务,为Service A 的调用提供固定的延时响应。
测试服务器的配置为8 核16G,千兆网卡。
2.2 同步异步模型对比
测试场景1:
并发压测客户端200~8000,服务耗时50ms,分别对同步和异步架构进行压测,对比TPS、服务耗时、CPU 上下文切换;同步模式下,线程数和并发客户端相同;异步模式下,使用框架默认的200 线程。测试数据如下。
测试场景2:
并发压测客户端8000,服务耗时50~500ms,分别对同步和异步模式进行压测,对比TPS、服务耗时、CPU 上下文切换;同步模式下服务端8000 线程;异步模式下,使用框架默认的200 线程。
2.3 服务扩展性对比
并发指服务瞬时同时处理的任务数(包含处于IO 等待状态的任务)。服务端设置业务处理线程200,那么同步模式下能提供的并发为200;纯异步模式下服务并发不受线程限制,IO密集型服务尤其收益。在系统流量突增的情景下,异步模式具有更强的可扩展性(Scalability)。
2.4 结论
根据上面的测试数据可以做出以下对比:
同步模式,线程数与并发成正比,并发越高对线程的消耗越多
异步模式,提高并发不需要线程增加
同步模式,系统Context Switch 次数随并发提高而快速增加
异步模式,系统Context Switch 次数明显小于同步模式
同步模式,并发超过某个临界点后,服务耗时快速上升,系统吞吐量急剧下降
异步模式,吞吐量随着并发增加,服务耗时上升速度明显低于同步模式
从而得出以下结论:
可以通过异步化微服务架构,提高相同资源配置下的服务吞吐量
随着下游平均耗时的增加,异步化带来的吞吐和耗时的提升作用减小
线程资源有限(内核、内存),不能无限增加来提高并发能力,异步化能极大提高系统瞬时并发能力(Scalability)
结论分析:
高并发下同步模型大量线程在内核态度/用户态、不同CPU 核之间进行切换,Context Switch 增加,系统性能下降
下游平均耗时增加时,系统CPU 繁忙程度降低,Context Switch 对性能系统影响下降
3. 异步模型探索
3.1 阻塞与非阻塞
在操作系统中,线程是CPU 调度的基本单位;阻塞调用是指发起调用后,线程进入阻塞状态(让出CPU),直到获得结果或异常返回;非阻塞调用是指不等待结果,调用不阻塞线程直接返回。
3.2 同步与异步
同步和异步关注的是消息通信机制;同步就是在发起调用后就得到返回结果(未必是完整结果),也就是由调用者主动等待结果;异步则是调用在发出之后直接返回,通过信号通知、回调函数处理来通知结果。
3.3 四种IO 模型
非IO 系统调用层面, 阻塞/非阻塞和同步/异步基本是同义词;在IO 系统调用层面,同步/异步和阻塞/非阻塞有以下组合:
同步阻塞调用,线程同步等待阻塞调用结果
同步非阻塞调用,线程通过轮训获取非阻塞调用结果
异步阻塞调用,IO 事件阻塞,IO 操作不阻塞
异步非阻塞调用,调用立即返回,信号/回调处理结果
我们通过一个简单的客户端来介绍四种IO 模型的代码写法:
同步阻塞IO
非同步阻塞IO
多路复用IO
Asynchnorous IO
对四中IO 模型,有以下的对比:
同步阻塞式IO 模型,编程简单但线程阻塞,资源利用率低;
同步非阻塞式IO 模型,需要轮训CPU,浪费资源;
异步非阻塞AIO 模型,不阻塞线程,使用回调方式处理数据,但是编程难度高;
多路复用IO 模型,能够实现异步非阻塞IO,且编程简单,方便实现同步和异步调用,因此成为RPC 框架的首选。
4. 全链路异步编程指南
4.1 全链路组成及现状
微服务架构下的全链路包含了网关层、WEB 服务、RPC 服务、数据层等。目前公司的网关层已经实现了纯异步架构,Web 框架和RPC 框架支持纯异步编程,数据存储层目前异步方案还不成熟。
4.2 网关异步化
网关层由于其特殊性,不需要访问业务数据库只做协议转换和流量转发,目前已经使用了纯异步的架构;其IO 密集型的特点,特别适合纯异步的架构,可以极大的节省资源。
4.3 Web 服务异步化
Web 服务作为微服务体系内的重要组成,服务节点众多,传统的Web 服务框架SpringMVC 不支持纯异步化编程,OPPO 自研Web 框架Restlight 支持纯异步编程,且性能远超SpringMVC。下面是性能对比及Restlight 异步实践。
Restlight 框架异步编程实践:通过Controller 方法返回值区分同步和异步调用,且支持三种异步调用方式,CompletableFuture、ListenableFuture(Guava)、Future(Netty)。
4.4 RPC 调用异步化
RPC 调用等待下游response 返回时,线程不应处于block 状态;作为微服务架构中数据流量最大的一部分,RPC 调用异步化的收益巨大;目前ESA RPC 已经具备了纯异步化的能力,提供RPC 调用的服务一般既是客户端也是服务端,因此包含了客户端异步调用能力和服务端异步处理能力;为了兼容存量接口,ESA RPC 既支持CompletableFuture 也支持普通返回值的接口。
客户端异步化实践:底层使用异步非阻塞IO 收发网路数据包,使用CompletableFUture传递IO 事件以实现响应式编程,客户端不被RPC 调用阻塞,可继续调用其他服务。
接口返回CompletableFuture 来实现异步调用:
普通接口使用ESARpcContext::asyncCall 实现异步调用:
服务端异步化实践:通过服务端异步功能返回CompletableFuture 给框架以释放Biz 线程,自定义线程池或者IO 线程池收到下游response 后,完成返回给框架的Future。
接口定义返回CompletableFuture 来实现异步调用:
普通接口通过ESARpcContext::startAsync 开启服务端异步:
4.5 存储层异步化
数据操作是每个请求调用链的终点,纯异步的架构必须使用异步存储层客户端,目前OPPO 没有自研的存储层异步客户端,但业界开源方案欣欣向荣:
数据库:Vert.x JDBC 客户端
Redis:Redisson、Lettuce
Queue:基本都支持异步调用
4.6 纯异步与伪异步
异步调用目的在于防止当前业务线程被阻塞。伪异步将任务包装为Runnable 放入另一个线程执行并等待,当前Biz 线程不阻塞;纯异步为响应式编程模型,通过IO 实践驱动任务完成。他们的区别不在于是否将请求放入另一个线程池执行,而在于是否有线程阻塞等待Response。
5. 异步化未来发展
5.1 异步化带来的问题
相比于同步模型,异步模型存在以下问题:
代码可读性和可维护性较差,可能出现Callback Hell
框架SDK 变得复杂,使用门槛增加
业务可能不清楚代码逻辑执行线程
大量的ThreadLocal 需要手动export/import
简单来说,异步编程就是以编程的简单性(simplity)来交换性能(performance)。
5.2 使用协程实现异步非阻塞
目前在其他语言中,Erlang、Go、Kotlin 等都支持了协程,使用协程的好处是在语言层面支持了异步调用,业务代码可以使用同步的写法达到异步的效果,线程不被阻塞,避免大量的CPU 上下文切换,提升系统的性能。
目前Java 对协程的支持也在进行中, Project Loom 就是Java 的协程项目:http://openjdk.java.net/projects/loom/。
主要有以下几个概念:
Fiber,轻量级线程(用户态线程),基于Continuation 实现
Continuation,指令执行单元, 阻塞时调用Continuation::yield , 恢复时调用Continuation::run
Scheduler,用户态Fiber 调度器(ForkJoinPool),使用有限Workers 线程执行任意数量Fibers
开发者可以使用 Fiber 来执行业务代码块,当遇到LockSupport::park、socket io 等阻塞调用时,Fiber 中的代码单元执行会被阻塞,但是底层的线程并不会被阻塞。由此达到了开发同步模式代码,运行时达到异步执行的目的。
未来,ESAStack服务框架会支持协程。目前 Restlight框架已经支持协程并在内部开始试用,ESARPC也有支持协程的计划。框架提供的服务线程使用 Fiber 执行业务逻辑,业务实现中数据库请求、下游服务调用均在 Fiber 之中执行, 其包含的 IO 等阻塞调用只挂起 Fiber 而不阻塞所在线程,从而避免了过多的上下文切换提升 了吞吐量,达到了和异步模式一样的效果。
☆ END ☆
OPPO互联网基础技术团队招聘一大波岗位,涵盖C++、Go、OpenJDK、Java、DevOps、Android、ElasticSearch等多个方向,请点击这里查看详细信息及JD。
更多技术干货
扫码关注
OPPO互联网技术
本文分享自微信公众号 - OPPO互联网技术(OPPO_tech)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
打造数字化服务能力,中国联通如何借助云原生技术实现增长突围?
8 月 13 日,中国联通发布《2020 年半年度报告》,公司营业收入同比增长 3.8% 至 1503.97 亿元,其中,主营业务收入 1383.35 亿元,同比增长 4%,高于行业平均的 3.2%。归属于上市公司股东的净利润同比增长 10.9% 至 33.44 亿元,扣非净利润 35.41 亿元,同比增幅为 25.5%。 可以看到,随着 2G 到 4G,乃至如今 5G 技术的普及以及中国联通积极发挥资源禀赋优势,深度推进产业合作。5G+ 垂直行业应用的培育和融合创新发展的加快,中国联通在数字化转型新时代迎来了更快速的增长。 而这背后,业务量的迅猛发展与数据量的不断增长,持续挑战运营商业务支撑系统的开发运维和服务能力。一直以来,作为国内三大运营商之一的中国联通始终以新理念新模式促进信息基础设施升级,推动网络资源优化演进升级,持续提升网络竞争力。 此前,中国联通采用的是传统 IOE 架构。当面对大量集中的全国业务,系统的应用部署、硬件部署和网络部署会面临极大的挑战,传统的架构无法适应互联网的突增和突减情况。中国联通 BSS 核心系统随着用户规模增长,个性化业务复杂性日趋提升、竞争压力不断...
- 下一篇
ServiceMesh Edge设计
入站和出站流量的一般注意事项 首先查看Istio的路由: 当连connection访问Istio的某个成员时(假设一个入口网关,但它对每个成员都起作用),所有路由选择决定均基于主机名(host header字段)。 Ingress gateways为侦听某些端口上的连接,以及基于网关对象的某些主机名。网关配置会根据标签选择器选择要应用网关的Pod。网关对象应在网关容器所在的相同namespace中定义。 默认情况下,Ingress gateway不知道Istio的服务。为了使入口网关了解网格中的服务,必须定义VirtualService并将其应用于网关对象。当VirtualService与网关对象不在同一个命名空间中时(在大多数情况下是这种情况),应以NamespacedName格式(<namespace> / <gateway>)引用网关对象。 VirtualService可以与DestinationRule结合使用,以进行细粒度的流量管理。 然后,VirtualService将路由到Kubernetes服务的活动成员(自动发现)或ServiceEntry。S...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8编译安装MySQL8.0.19
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Mario游戏-低调大师作品
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2更换Tomcat为Jetty,小型站点的福音