您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

时间:2020-07-29 10:30:15  来源:  作者:

在之前的文章中就讨论过为什么在高并发的情况下,程序会崩溃。主要原因是,在高并发的情况下,有大量用户请求需要程序计算处理,而目前的处理方式是,为每个用户请求分配一个线程,当程序内部因为访问数据库等原因造成线程阻塞时,线程无法释放去处理其他请求,这样就会造成请求堆积,不断消耗资源,最终导致程序崩溃。

反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

 

这是传统的 Web 应用程序运行期的线程特性。对于一个高并发的应用系统来说,总是同时有很多个用户请求到达系统的 Web 容器。Web 容器为每个请求分配一个线程进行处理,线程在处理过程中,如果遇到访问数据库或者远程服务等操作,就会进入阻塞状态,这个时候,如果数据库或者远程服务响应延迟,就会出现程序内的线程无法释放的情况,而外部的请求不断进来,导致计算机资源被快速消耗,最终程序崩溃。

那么有没有不阻塞线程的编程方法呢?

一、反应式编程

答案就是反应式编程。反应式编程本质上是一种异步编程方案,在多线程(协程)、异步方法调用、异步 I/O 访问等技术基础之上,提供了一整套与异步调用相匹配的编程模型,从而实现程序调用非阻塞、即时响应等特性,即开发出一个反应式的系统,以应对编程领域越来越高的并发处理需求。

人们还提出了一个反应式宣言,认为反应式系统应该具备如下特质:

  • 即时响应,应用的调用者可以即时得到响应,无需等到整个应用程序执行完毕。也就是说应用调用是非阻塞的。
  • 回弹性,当应用程序部分功能失效的时候,应用系统本身能够进行自我修复,保证正常运行,保证响应,不会出现系统崩溃和宕机的情况。
  • 弹性,系统能够对应用负载压力做出响应,能够自动伸缩以适应应用负载压力,根据压力自动调整自身的处理能力,或者根据自身的处理能力,调整进入系统中的访问请求数量。
  • 消息驱动,功能模块之间,服务之间,通过消息进行驱动,完成服务的流程。

目前主流的反应式编程框架有 RxJAVA、Reactor 等,它们的主要特点是基于观察者设计模式的异步编程方案,编程模型采用函数式编程。

观察者模式和函数式编程有自己的优势,但是反应式编程并不是必须用观察者模式和函数式编程。Flower 就是一个纯消息驱动,完全异步,支持命令式编程的反应式编程框架。

下面我们就看看 Flower 如何实现异步无阻塞的调用,以及 Flower 这个框架设计使用了什么样的设计原则与模式。

二、反应式编程框架Flower的基本原理

一个使用 Flower 框架开发的典型 Web 应用的线程特性如下图所示:

反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

 

当并发用户到达应用服务器的时候,Web 容器线程不需要执行应用程序代码,它只是将用户的 HTTP 请求变为请求对象,将请求对象异步交给 Flower 框架的 Service 去处理,自身立刻就返回。因为容器线程不做太多的工作,所以只需极少的容器线程就可以满足高并发的用户请求,用户的请求不会被阻塞,不会因为容器线程不够而无法处理。相比传统的阻塞式编程,Web 容器线程要完成全部的请求处理操作,直到返回响应结果才能释放线程;使用Flower 框架只需要极少的容器线程就可以处理较多的并发用户请求,而且容器线程不会阻塞

用户请求交给基于 Flower 框架开发的业务 Service 对象以后,Service 之间依然是使用异步消息通讯的方式进行调用,不会直接进行阻塞式的调用。一个 Service 完成业务逻辑处理计算以后,会返回一个处理结果,这个结果以消息的方式异步发送给它的下一个Service。

传统编程模型的 Service 之间如果进行调用,被调用的Service 在返回之前,调用的 Service 方法只能阻塞等待。而 Flower 的 Service 之间使用了 AKKA Actor 进行消息通信,调用者的 Service 发送调用消息后,不需要等待被调用者返回结果,就可以处理自己的下一个消息了。事实上,这些 Service 可以复用同一个线程去处理自己的消息,也就是说,只需要有限的几个线程就可以完成大量的 Service 处理和消息传输,这些线程不会阻塞等待

我们刚才提到,通常 Web 应用主要的线程阻塞,是因为数据库的访问导致的线程阻塞。Flower 支持异步数据库驱动,用户请求数据库的时候,将请求提交给异步数据库驱动,立刻就返回,不会阻塞当前线程,异步数据库访问连接远程的数据库,进行真正的数据库操作,得到结果以后,将结果以异步回调的方式发送给 Flower 的 Service 进行进一步的处理,这个时候依然不会有线程被阻塞

也就是说,使用 Flower 开发的系统,在一个典型的 Web 应用中,几乎没有任何地方会被阻塞,所有的线程都可以被不断地复用,有限的线程就可以完成大量的并发用户请求,从而大大地提高了系统的吞吐能力和响应时间,同时,由于线程不会被阻塞,应用就不会因为并发量太大或者数据库处理缓慢而宕机,从而提高了系统的可用性

Flower 框架实现异步无阻塞,一方面是利用了 Web 容器的异步特性,主要是 Servlet3.0以后提供的 AsyncContext,快速释放容器线程;另一方面是利用了异步的数据库驱动以及异步的网络通信,主要是 HttpAsyncClient 等异步通信组件。而 Flower 框架内,核心的应用代码之间的异步无阻塞调用,则是利用了 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的设计方法

但是直接使用 Actor 进行编程有很多不便,Flower 框架对 Actor 进行了封装,开发者只需要编写一些细粒度的 Service,这些 Service 会被包装在 Actor 里面,进行异步通信。Flower Service 例子如下:

publicclassServiceAimplementsService<Message2>{
    @Override
    publicObjectprocess(Message2message){
        returnmessage.getAge()+1;
    }
}

每个 Service 都需要实现框架的 Service 接口的 process 方法,process 方法的输入参数就是前一个 Service process 方法的返回值,这样只需要将 Service 编排成一个流程,Service 的返回值就会变成 Actor 的一个消息,被发送给下一个 Service,从而实现Service 的异步通信。

Service 的流程编排有两种方式,一种方式是编程实现,如下:

getServiceFlow().buildFlow("ServiceA","ServiceB");

表示 ServiceA 的返回值将作为消息发送给 ServiceB,成为 ServiceB 的输入值,这样两个Service 就可以合作完成一些更复杂的业务逻辑。

Flower 还支持可视化的 Service 流程编排,像下面这张图一样编辑流程定义文件,就可以开发一个异步业务处理流程。

反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

 

那么这个 Flower 框架是如何实现的呢?

Flower 框架的设计也是基于依赖倒置原则。所有应用开发者实现的Service 类都需要包装在 Actor 里面进行异步调用,但是 Actor 不会依赖开发者实现的Service 类,开发者也不会依赖 Actor 类,他们共同依赖一个 Service 接口,这个接口是框架提供的,如上面例子所示。

Actor 与 Service 的依赖倒置关系如下图所示:

反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

 

每个 Actor 都依赖一个 Service 接口,而具体的 Service 实现类,比如 MyService,则实现这个 Service 接口。在运行期实例化 Actor 的时候,这个接口被注入具体的 Service 实现类,比如 MyService。在 Flower 中,调用 MyService 对象,其实就是给包装MyService 对象的 Actor 发消息,Actor 收到消息,执行自己的 onReceive 方法,在这个方法里,Actor 调用 MyService 的 process 方法,并将 onReceive 收到的 Message 对象当做 process 的输入参数传入。

process 处理完成后,返回一个 Object 对象。Actor 会根据编排好的流程,获取MyService 在流程中的下一个 Service 对应的 Actor,即 nextServiceActor,将 process返回的 Object 对象当做消息发送给这个 nextServiceActor。这样,Service 之间就根据编排好的流程,异步、无阻塞地调用执行起来了。

四、反应式编程框架Flower的落地效果

Flower 框架在部分项目中落地应用,应用效果较为显著,一方面,Flower 可以显著提高系统的性能。这是某个 C# 开发的系统使用 Flower 重构后的 TPS 性能比较,使用 Flower 开发的系统 TPS 差不多是原来 C# 系统的两倍。

反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

 

另一方面,Flower 对系统可用性也有较大提升,目前常见互联网应用架构如下图:

反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

 

用户请求通过网关服务器调用微服务完成处理,那么当有某个微服务连接的数据库查询执行较慢时,如图中服务 1,那么按照传统的线程阻塞模型,就会导致服务 1 的线程都被阻塞在这个慢查询的数据库操作上。同样的,网关线程也会阻塞在调用这个延迟比较厉害的服务1 上。

最终的效果就是,网关所有的线程都被阻塞,即使是不调用服务 1 的用户请求也无法处理,最后整个系统失去响应,应用宕机。使用阻塞式编程,实际的压测效果如下,当服务 1响应延迟,出错率大幅飙升的时候,通过网关调用正常的服务 2 的出错率也非常高。

反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

 

使用 Flower 开发的网关,实际压测效果如下,同样服务 1 响应延迟,出错率极高的情况下,通过 Flower 网关调用服务 2 完全不受影响。

反应式编程框架设计:如何使程序调用不阻塞等待,立即响应?

 

五、总结

事实上,Flower 不仅是一个反应式 Web 编程框架,还是反应式的微服务框架。也就是说,Flower 的 Service 可以远程部署到一个 Service 容器里面,就像我们现在常用的微服务架构一样。Flower 会提供一个独立的 Flower 容器,用于启动一些 Service,这些Service 在启动了以后,会向注册中心进行注册,而且应用程序可以将这些分布式的Service 进行流程编排,得到一个分布式非阻塞的微服务系统。整体架构和主流的微服务架构很像,主要的区别就是 Flower 的服务是异步的,通过流程编排的方式进行服务调用,而不是通过接口依赖的方式进行调用。



Tags:框架   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
译者 | 陈峻Laravel 和 Yii 是当前两种针对 PHP 的优秀框架。它们在全球都有成熟的社区、以及大量的编程爱好者。本文为您全面比较 Laravel 和 Yii,并和您讨论如何根据具体项...【详细内容】
2022-07-15  Tags: 框架  点击:(2)  评论:(0)  加入收藏
关于过气网红编程语言 Ruby,我们此前曾发过一篇文章去回顾其大受追捧的过往,并讨论了它每况愈下的生存状态。不过人气并不能直接说明语言质量差,一方面 Ruby on Rails(用 Ruby...【详细内容】
2022-07-08  Tags: 框架  点击:(9)  评论:(0)  加入收藏
21世纪的编程讲求高效,低门槛,低成本。作为一名程序员,你多少可能了解甚至从事过跨平台开发、多端开发。所谓"跨平台",就是编写一套代码,编译到多个平台运行。比如,时下流行的小程...【详细内容】
2022-07-05  Tags: 框架  点击:(14)  评论:(0)  加入收藏
前言Hertz 是字节跳动服务框架团队研发的超大规模的企业级微服务 HTTP 框架,具有高易用性、易扩展、低时延等特点。在经过了字节跳动内部一年多的使用和迭代,如今已在 CloudWe...【详细内容】
2022-06-29  Tags: 框架  点击:(22)  评论:(0)  加入收藏
在这个博客中,我们编译了一个基于编程语言和技术堆栈的顶级后端框架列表每天‬分享‬最新‬软件‬开发‬,Devops,敏捷‬,测试‬以及‬项目‬管理‬最新‬,最热门‬的‬文章‬,每天...【详细内容】
2022-06-28  Tags: 框架  点击:(11)  评论:(0)  加入收藏
写在前面上个星期分享了《基于Sikuli GUI图像识别框架的PC客户端自动化测试实践》,但sikuli看起来怎么都像是上个世纪的界面风格,且功能过于简陋。而同样基于图像识别框架的Ai...【详细内容】
2022-06-28  Tags: 框架  点击:(30)  评论:(0)  加入收藏
Vant 轻量、可靠的移动端 Vue 组件库Vant 是一个轻量、可靠的移动端组件库,于 2017 年开源。目前 Vant 官方提供了 Vue 2 版本、Vue 3 版本和微信小程序版本,并由社区团队维护...【详细内容】
2022-06-23  Tags: 框架  点击:(43)  评论:(0)  加入收藏
fastadmin中请求数据是通过bootstrap-table来请求数据的,对于一些数据需要树状层级展示数据不能够很直观的显示,于是小编找到了一个解决方案就是利用bootstrap-treegrid,bootst...【详细内容】
2022-06-23  Tags: 框架  点击:(19)  评论:(0)  加入收藏
作者: 俊欣来源:关于数据分析与可视化今天小编来为大家安利另外一个用于绘制可视化图表的Python框架,名叫Dash,建立在Flask、Plotly.js以及React.js的基础之上,在创建之出的目的...【详细内容】
2022-06-22  Tags: 框架  点击:(37)  评论:(0)  加入收藏
Vue-Vben-Admin 是一个基于 Vue3.0、Vite、 Ant-Design-Vue、TypeScript 的后台解决方案,目标是为开发中大型项目提供开箱即用的解决方案。包括二次封装组件、utils、hooks...【详细内容】
2022-06-21  Tags: 框架  点击:(54)  评论:(0)  加入收藏
▌简易百科推荐
本篇文章主要介绍了使用MyBatis框架完成数据库的增、删、改、查操作。准备工作运行schema.sql和data.sql脚本文件中的 SQL 语句创建t_user表并添加部分测试数据。schema.sql...【详细内容】
2022-07-15  嗨皮汪小成    Tags:MyBatis   点击:(0)  评论:(0)  加入收藏
1 Hive基本概念Hive是一个构建在Hadoop上的数据仓库框架。最初,Hive是由Facebook开发,后来移交由Apache软件基金会开发,并作为一个Apache开源项目。Hive是基于Hadoop的一个数据...【详细内容】
2022-07-15  秃头Java人    Tags:Hive   点击:(2)  评论:(0)  加入收藏
今天给大家讲讲 SpringBoot 框架 整合 Elasticsearch 实现海量级数据搜索。一、简介在上篇ElasticSearch 文章中,我们详细的介绍了 ElasticSearch 的各种 api 使用。实际的项...【详细内容】
2022-07-15  java小悠    Tags: Elasticsearch   点击:(3)  评论:(0)  加入收藏
SpringBoot开发Restful接口,有什么API规范吗?如何快速生成API文档呢?Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲,Swagger 就是将项目中所有(想要...【详细内容】
2022-07-14  Java全栈知识体系    Tags:Swagger   点击:(2)  评论:(0)  加入收藏
一、部署准备安装数据库、jdk、nginx、域名证书1、下载 nginx,官方网址如下:http://nginx.org/en/download.html2、解压安装包到任意目录 如:G:\nginx二、前端部署1、打开前端...【详细内容】
2022-07-14  智慧魔法豆浆    Tags:vue   点击:(2)  评论:(0)  加入收藏
SpringBoot 内置支持的 Web 容器有 Tomcat、Undertow、Jetty 和 Netty。默认情况下,这些 Web 服务的 AccessLog 日志是不开启的,而 AccessLog 日志对于做接口统计尤为重要。如...【详细内容】
2022-07-13  BUG弄潮儿    Tags:AccessLog 日志   点击:(10)  评论:(0)  加入收藏
什么是Starterstarter 是springboot 的核心,每个starter负责实现特定的功能,使用者只需引入starter即可自动配置,无需关心框架整合带来的问题。Starter 项目结构src |- main...【详细内容】
2022-07-12  IT食者    Tags:SpringBoot   点击:(9)  评论:(0)  加入收藏
mybaits非必填项处理数据库表字段khzjyxqx为日期型,非必填, 前台页面如下: 后台mybaits处理如下: 如果不处理,当为空时khzjyxqx=&#39;&#39;时会报错。<update id="updatesave" pa...【详细内容】
2022-07-11  在水一方357159258    Tags:mybaits   点击:(10)  评论:(0)  加入收藏
关于过气网红编程语言 Ruby,我们此前曾发过一篇文章去回顾其大受追捧的过往,并讨论了它每况愈下的生存状态。不过人气并不能直接说明语言质量差,一方面 Ruby on Rails(用 Ruby...【详细内容】
2022-07-08  InfoQ    Tags: Web 框架   点击:(9)  评论:(0)  加入收藏
1、JWT的构成- 头部(header):描述该JWT的最基本的信息,如类型以及签名所用的算法。- 负载(payload):存放有效信息的地方。- 签证(signature):base64加密后的header、base64加...【详细内容】
2022-07-08  dream19    Tags:SpringBoot   点击:(10)  评论:(0)  加入收藏
站内最新
站内热门
站内头条