本文来自OPPO互联网基础技术团队,转载请注名做者。同时欢迎关注咱们的公众号:OPPO_tech,与你分享OPPO前沿互联网技术及活动。
随着公司业务的发展,核心服务流量愈来愈大,使用到的资源也愈来愈多。在微服务架构体系中,大部分的业务是基于Java 语言实现的,受限于Java 的线程实现,一个Java 线程映射到一个kernel 线程,形成了高并发场景下线程资源的极大浪费,线程成为提升系统并发和吞吐量的瓶颈。java
在微服务架构下,使用同步编程模式时不只形成了资源的极大浪费,而且在流量发生激增波动的时候,受制于系统资源而没法快速的扩容。本文将探索服务异步化在并发、吞吐量方面对系统带来的提高。数据库
首先,以微服务架构中的RPC 服务调用举例,测试和探索在微服务架构中,异步架构如何提升服务的吞吐量和并发。ESA Stack 是OPPO 自研的基础框架技术栈,ESA RPC 是自研的RPC 框架。本节测试服务咱们使用ESA RPC 搭建。编程
关于ESA RPC的详情,能够参考咱们以前发布的文章《Dubbo协议解析及ESA RPC实践》。服务器
下图所示为测试环境架构。其中Service A 既是服务端也是客户端,它模拟了生产环境中大部分服务的角色。咱们对Service A 分别采用同步模型和纯异步模型进行压测,其中纯异步模型包含了客户端、服务端逻辑的异步处理。Service B 模拟一个耗时为N ms 的下游服务,为Service A 的调用提供固定的延时响应。架构
测试服务器的配置为8 核16G,千兆网卡。并发
测试场景1:框架
并发压测客户端200~8000,服务耗时50ms,分别对同步和异步架构进行压测,对比TPS、服务耗时、CPU 上下文切换;同步模式下,线程数和并发客户端相同;异步模式下,使用框架默认的200 线程。测试数据以下。异步
测试场景2:socket
并发压测客户端8000,服务耗时50~500ms,分别对同步和异步模式进行压测,对比TPS、服务耗时、CPU 上下文切换;同步模式下服务端8000 线程;异步模式下,使用框架默认的200 线程。async
并发指服务瞬时同时处理的任务数(包含处于IO 等待状态的任务)。服务端设置业务处理线程200,那么同步模式下能提供的并发为200;纯异步模式下服务并发不受线程限制,IO密集型服务尤为收益。在系统流量突增的情景下,异步模式具备更强的可扩展性(Scalability)。
根据上面的测试数据能够作出如下对比:
从而得出如下结论:
结论分析:
在操做系统中,线程是CPU 调度的基本单位;阻塞调用是指发起调用后,线程进入阻
塞状态(让出CPU),直到得到结果或异常返回;非阻塞调用是指不等待结果,调用不阻塞线程直接返回。
同步和异步关注的是消息通讯机制;同步就是在发起调用后就获得返回结果(未必是完整结果),也就是由调用者主动等待结果;异步则是调用在发出以后直接返回,经过信号通知、回调函数处理来通知结果。
非IO 系统调用层面, 阻塞/非阻塞和同步/异步基本是同义词;在IO 系统调用层面,同步/异步和阻塞/非阻塞有如下组合:
咱们经过一个简单的客户端来介绍四种IO 模型的代码写法:
同步阻塞IO
非同步阻塞IO
多路复用IO
Asynchnorous IO
对四中IO 模型,有如下的对比:
微服务架构下的全链路包含了网关层、WEB 服务、RPC 服务、数据层等。目前公司的网关层已经实现了纯异步架构,Web 框架和RPC 框架支持纯异步编程,数据存储层目前异步方案还不成熟。
网关层因为其特殊性,不须要访问业务数据库只作协议转换和流量转发,目前已经使用了纯异步的架构;其IO 密集型的特色,特别适合纯异步的架构,能够极大的节省资源。
Web 服务做为微服务体系内的重要组成,服务节点众多,传统的Web 服务框架SpringMVC 不支持纯异步化编程,OPPO 自研Web 框架Restlight 支持纯异步编程,且性能远超SpringMVC。下面是性能对比及Restlight 异步实践。
Restlight 框架异步编程实践:经过Controller 方法返回值区分同步和异步调用,且支持三种异步调用方式,CompletableFuture、ListenableFuture(Guava)、Future(Netty)。
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 开启服务端异步:
数据操做是每一个请求调用链的终点,纯异步的架构必须使用异步存储层客户端,目前OPPO 没有自研的存储层异步客户端,但业界开源方案欣欣向荣:
异步调用目的在于防止当前业务线程被阻塞。伪异步将任务包装为Runnable 放入另外一个线程执行并等待,当前Biz 线程不阻塞;纯异步为响应式编程模型,经过IO 实践驱动任务完成。他们的区别不在因而否将请求放入另外一个线程池执行,而在因而否有线程阻塞等待Response。
相比于同步模型,异步模型存在如下问题:
简单来讲,异步编程就是以编程的简单性(simplity)来交换性能(performance)。
目前在其余语言中,Erlang、Go、Kotlin 等都支持了协程,使用携程的好处是在语言层面支持了异步调用,业务代码可使用同步的写法达到异步的效果,线程不被阻塞,避免大量的CPU 上下文切换,提高系统的性能。
目前Java 对协程的支持也在进行中, Project Loom 就是Java 的协程项目:http://openjdk.java.net/proje...。
主要有如下几个概念:
开发者可使用Fiber 来执行业务代码块,当遇到LockSupport::park、socket io 等阻塞调用时,Fiber 中的代码单元执行会被阻塞,可是底层的线程并不会被阻塞。由此达到了开发同步模式代码,运行时达到异步执行的目的。
将来,ESAStack服务框架会支持协程。目前 Restlight框架已经支持协程并在内部开始试用,ESARPC也有支持协程的计划。框架提供的服务线程使用 Fiber 执行业务逻辑,业务实现中数据库请求、下游服务调用均在 Fiber 之中执行, 其包含的 IO 等阻塞调用只挂起 Fiber 而不阻塞所在线程,从而避免了过多的上下文切换提高 了吞吐量,达到了和异步模式同样的效果。