写点什么

腾讯云 TVP 李智慧:如何用反应式编程提升系统性能与可用性?

  • 2019-10-30
  • 本文字数:3963 字

    阅读完需:约 13 分钟

腾讯云TVP李智慧:如何用反应式编程提升系统性能与可用性?

没有人能够预言未来,也没有人能够断言未来的编程是什么样,但是我们可以通过过往的编程经验去探寻未来的编程趋势,本文是腾讯云 TVP 李智慧教你如何用反应式编程提升系统性能与可用性。


反应式编程这两年愈来愈热,很多人都知道著名的反应式宣言:


即时响应:只要有可能,系统就会及时地做出响应。


弹性:系统在出现失败时依然保持即时响应性。


回弹性:系统在不断变化的工作负载之下依然保持即时响应性。


消息驱动:反应式系统依赖异步的消息传递,从而在确保系统松耦合、 隔离和位置透明。


那么反应式程序究竟在运行层面是怎样的?对软件系统有哪些改进?如何开发一个反应式程序呢?


在最近的一年时间,我们在同程艺龙开发了一个反应式编程框架并应用于一些典型的应用场景,在这些场景中,系统性能和可用性都得到较大提升。

程序是如何运行又是如何崩溃的?

为什么要进行反应式编程的尝试?我们先从传统的编程方法引发的问题说起。


传统的后端程序开发事实上都是多线程开发,但是很多开发工程师并没有感觉到自己是在进行多线程开发,因为自己在程序中并没有创建线程。其实这是因为多线程通常是由 Tomcat 这样的 Web 容器创建的,我们开发的程序由 Web 容器创建的多线程调用执行,后端工程师开发的程序同样要考虑多线程的问题。


这样的应用系统的线程模型通常是这样的。



对于一个高并发的应用系统,同时总是有很多个用户请求到达系统的 Web 容器。Web 容器为每一个请求分配一个用户线程去进行处理,而容器能够启动的用户线程数目是有限的。如果当前所有的容器线程都已经被用完了,这时候还有新的用户请求到达,请求就会被阻塞在应用服务器,等待前面的线程释放,或者直接返回服务器错误。


而线程在运行期可能会遇到各种阻塞情况,导致线程无法执行下去。比较典型的就是访问数据库,一个用户程序,想要访问数据库,必须要获得数据库的连接,而数据库的连接数相对用户线程数是比较少的。当数据库连接用完以后,线程请求获得数据库连接的时候就会被阻塞。而对于得到了数据库连接的线程,去访问数据库的时候,当它将数据库操作请求发送以后,数据库在远程进行数据处理的时候,当前的线程依然会被阻塞。这些被阻塞的线程既无法去响应其他的用户操作,也无法完成自己的工作,只能白白地消耗系统的资源。如果遇到某种情况,比如说数据库因为某个慢查询而响应比较慢,那么大量的用户线程就会堆积阻塞在数据访问这里无法得到释放,响应时间变长。而新的请求又会不断的到达,不断的消耗系统资源,最后可能会导致系统崩溃。

反应式编程框架 Flower 的解决之道

针对上述传统的阻塞式编程的缺点,我们基于 Akka(https://akka.io/)开发了一个全异步的反应式编程框架Flowerhttps://github.com/zhihuili/Flower)



使用 Flower 开发的 Web 应用,只需要有限的几个线程,就可以完成全部的用户请求操作。当并发用户到达应用服务器的时候,Flower 只需要极少的容器线程就可以处理所有的并发用户请求。这个线程并不会执行真正的业务操作,它只是将用户的请求变为请求对象以后,将请求对象异步交给 Flower 的 Service 去处理,自身立刻就返回。因为容器线程不做太多的工作,所以极少的容器线程就可以满足高并发的用户请求,用户的请求不会被阻塞,不会因为容器线程不够而无法处理。


用户请求交给 Flower 的 Service 对象以后,Service 之间依然是使用异步的消息通讯的方式进行调用,Service 之间也不会直接进行阻塞式的调用。一个 Service 完成业务逻辑处理计算以后,会返回一个处理结果,这个结果以消息的方式异步地发送给他的下一个 Service,Service 之间使用了 Akka Actor 进行消息通信,也是只需要有限的几个线程就可以完成大量的 Service 处理和消息传输。


我们刚才提到 Web 应用主要的线程阻塞,是因为数据库的访问导致的线程阻塞。Flower 支持异步数据库驱动,用户请求数据库的时候,将请求提交给异步数据库驱动,立刻就返回,不会阻塞当前线程,异步数据库访问连接远程的数据库,进行真正的数据库操作,得到结果以后,将结果以异步回调的方式发送给 Flower 的 Service 进行进一步的处理,这个时候依然不会有线程被阻塞。也就是说使用 Flower 开发的系统,在一个典型的 Web 应用中,几乎没有任何地方会被阻塞,所有的线程都可以被不断的复用,有限的线程就可以完成大量的并发用户请求,从而大大地提高了系统的吞吐能力和响应时间,同时,由于线程不会被阻塞,应用就不会因为并发量太大或者数据库处理缓慢而宕机,从而提高了系统的可用性。


基于 Flower 框架开发一个异步反应式系统的时候,只需要实现 Flower 的 Service 接口。


public class ServiceA implements Service<Message2> {  @Override  public Object process(Message2 message) {    return message.getAge() + 1;  }}
复制代码


然后将这些 Service 按照处理流程,进行流程编排。就可以得到一个异步的反应式系统。


getServiceFlow().buildFlow("ServiceA", "ServiceB");
复制代码


通过流程编排的 Service 之间没有任何耦合,Service 之间的调用不需要任何依赖,只需要将这些 Service 编排在一个流程中就可以了。


Flower 的 Service 可以异步通信,主要是基于 AKKA Actor 进行异步通信的,那么 AKKA Actor 又是如何实现异步的消息通信的呢?



一个 Actor 向另一个 Actor 进行通讯的时候,当前 Actor 就是一个消息的发送者 sender,当他想要向另一个 Actor 进行通讯的时候,他需要获得另一个 Actor 的 ActorRef,也就是一个引用,通过引用进行消息通信。而 ActorRef 收到消息以后,会将这个消息放入到 Actor 的 Mailbox 里面去,然后就立即返回了。也就是说一个 Actor 向另一个 Actor 发送消息的时候,不需要另一个 Actor 去真正的去处理这个消息,只需要将消息发送到目标 Actor 的 Mailbox 里面就可以了。自己不会被阻塞,可以继续执行自己的操作。而目标 Actor 检查自己的 Mailbox 中是否有消息,如果有消息,Actor 则会在从 Mailbox 里面去获取消息,对消息进行异步的处理,而所有的 Actor 会共享线程,这些线程不会有任何的阻塞。

反应式编程性能和可用性改善效果

我们在同程艺龙的一些典型产品中进行了 Flower 应用落地,实践表明,Flower 在提升系统性能和可用性方面都有非常大的改进。



这是在某产品核心查询接口利用 Flower 重构前后的性能对比,左边这张图是 TPS 的对比,右边这张图是响应时间的对比,我们可以看到,重构后,TPS 吞吐能力提升一倍,而平均响应时间则降低了一倍,性能提升显著。


除了性能提升外,使用 Flower 开发的 Web 应用的可用性也会得到提升。



这是一个典型的互联网微服务应用架构,并发用户请求进入系统网关后,调用相关微服务完成具体业务处理。但是如果某个微服务出现故障,比如服务 1,响应延迟比较厉害,可能会导致服务 2 也无法正常访问,事实上,可能会导致整个网关失效,系统整体宕机。


如我们前面分析,网关作为一个 Web 应用,使用传统的同步阻塞式方式调用服务 1 和服务 2,那么当服务 1 响应延迟的时候,就会阻塞网关的线程,而网关的线程是有限的,严重情况,可能会阻塞所有调用服务 1 的网关线程,导致网关假死。这时,即使服务 2 是正常的,也无法正常访问了。通常网关是集群部署的,如果所有的网关都调用了服务 1,那么整个系统都会不可用。


我们用 Flower 框架对网关进行了重构,并使用异步 HTTP Client 调用服务 1 和服务 2,这样对服务的调用不会占用网关的线程,当服务 1 响应延迟的时候,服务 2 的访问是正常的,系统虽然部分功能失效,但是整个系统是可用的。

小结

没有人能够预言未来,也没有人能够断言未来的编程是什么样。但是我们纵观编程的历史就会发现,编程技术的进步驱动力主要来自两个方面。


一是使程序具有更低的耦合性。 编程语言的进步,从汇编语言到面向过程的编程语言,再到面向对象的编程语言,在编程语言层面就使得程序的耦合性越来越低。而我们在开发过程中使用的各种编程框架,MVC、ORM 等等,也使代码之间的关系变得更加清晰,耦合变得更低。


二是使程序运行速度更快, 在编程层面,从多线程技术,到对象池、连接池各种池化复用技术,再到各种异步 IO 技术,目的都是使程序占用尽量少的资源、更多的并行执行,从而使执行速度更快。在架构层面,各种分布式集群技术、分布式缓存技术、分布式消息队列技术,主要目标也都是为了提高性能。


从这个角度看,未来的编程技术也一定是在这两个方面进行创新性的改进。


反应式编程框架 Flower 在低耦合方面使得服务之间的调用不再直接依赖,而是通过流程编排的方式将多个服务关联起来,完成一个业务逻辑处理。在性能方面,Flower 将整个计算处理过程全部异步化,更少线程切换、避免线程阻塞,从而获得更好的执行性能。从某种程度讲,Flower 遵循编程技术的进步趋势并进行了一定的创新。


事实上,Flower 对反应式的支持并不止文中提到的这些特性。如果你对反应式编程以及 Flower 框架感兴趣,欢迎体验 Flower 并加入 Flower 的开发,Flower 开源地址:https://github.com/zhihuili/Flower

关于 TVP

TVP,即腾讯云最具价值专家(Tencent Cloud Valuable Professional),是腾讯云实现数字化转型、建设智慧生态的重要战略计划,旨在通过建立与行业技术专家的交流平台,促进腾讯云与技术专家和用户之间的有效沟通,从而提升腾讯云产品能力,打造云计算技术生态,实现“用科技影响世界”的美好愿景。


作者介绍:


李智慧,同程艺龙交通首席架构师 ,腾讯云 TVP。长期从事大数据、大型网站架构的研发工作,曾担任阿里巴巴技术专家、Intel 亚太研发中心架构师、宅米和 WiFi 万能钥匙 CTO。Apache Spark 源代码贡献者,著有畅销书《大型网站技术架构:核心原理与案例分析》,极客时间《从零开始学大数据》专栏作者。


本文转载自公众号云加社区(ID:QcloudCommunity)。


原文链接:


https://mp.weixin.qq.com/s/G5zHvfH6UiR2uZ28QadN7A


2019-10-30 18:171141

评论

发布
暂无评论
发现更多内容

PIP的报错Could not fetch URL https://pypi.org/

陈磊@Criss

Git删除仓库中的文件和文件夹

陈磊@Criss

Docker的Image

陈磊@Criss

人人都可以掌握的正交试验设计测试用例方法

陈磊@Criss

如何选择一个性能测试工具(LoadRunner和Locust的一次对比)

陈磊@Criss

告别下载速度慢!Docker配置阿里云镜像仓库

程序员的时光

Docker 阿里云

优质单元测试的十大标准,你有遵循吗?

禅道项目管理

项目管理 单元测试 自动化测试

华章25周年活动——《迁移学习》限量5折!

华章IT

Nginx的容器部署

陈磊@Criss

分布式定时任务调度框架实践

vivo互联网技术

大数据 分布式 框架

企业微信群消息机器人发送开源项目

陈磊@Criss

Docker 容器连接

陈磊@Criss

好玩又好用,一款轻松就可以实现音视频的Demo

anyRTC开发者

音视频 移动互联网 RTC anyRTC Demo

你还应该知道的哈希冲突解决策略

vivo互联网技术

哈希冲突

该了解一波了!零基础入门Nginx

程序员的时光

nginx Docker

pipreqs:生成python项目的requirements

陈磊@Criss

微信小程序的自动化测试框架

陈磊@Criss

DockerFile 详解

陈磊@Criss

国内程序员最容易发音错误的单词集合

程序员生活志

程序员 经验总结

聊聊微前端的原理和实践

vivo互联网技术

大前端

jmeter 执行python脚本

陈磊@Criss

最受欢迎的男友职业排行榜Top10

程序员生活志

程序员

快速掌握的测试用例优先级划分方法

陈磊@Criss

Git使用教程:最详细、最傻瓜、最浅显、真正手把手教!

程序员生活志

git

一文道尽“表驱动法”

架构精进之路

编码 表驱动法

Docker的Image

陈磊@Criss

Java的Override和Overload

陈磊@Criss

Kafka实战宝典:如何跨机房传输数据

数据社

大数据 kafka 跨机房

Kafka实战宝典:一文带解决Kafka常见故障处理

数据社

kafka 监控

国家央行数字货币的优势与挑战

CECBC

数字货币 央行 商业银行

Python的Twisted事件驱动的网络引擎框架

陈磊@Criss

腾讯云TVP李智慧:如何用反应式编程提升系统性能与可用性?_文化 & 方法_李智慧_InfoQ精选文章