首页 > 代码库 > 把异步架构延伸到客户端

把异步架构延伸到客户端

作者:陈叶皓(携程邮轮研发部软件架构师)

我们先来回顾一下之前提到过的知识点,
1.在一台电脑上,使用异步编程可以提高cpu的使用效率
2.使用Actor模型,实现同一台电脑上,在并发环境下的串行操作,保证事务执行的正确
3.在多服务器环境下,actor模型配合Zookeeper,可以实现在多服务器环境下的串行操作,保证事务执行正确
4.对应用进行读写分离的设计,做到“写服务”(有状态)执行正确,同时又能方便地(增加服务器)提高“读服务”(无状态)的性能

所以,在服务实现的内部,异步已经无处不在,今天我来讲服务的消费者,也就是浏览器客户端,为什么也需要使用异步模式。

在Joe Armstrong创造erlang的时候,他假设网络通讯是不可靠的,外部服务的响应时间是不可靠的,所以erlang里面只有异步调用,同步调用是用异步来模拟。Erlang软件的运行,不会因为任何的外部操作而阻塞cpu。

而到了互联网时代,Joe Armstrong的假设依旧成立,网络通讯不可靠,外部服务不可靠。设计同步调用的应用架构,从一开始就引入了巨大的风险。举个简单的电商下单的例子,步骤如下,
1.浏览器发送下单请求到网页服务器(web server)
2.网页服务器发送请求到应用服务器
3.应用服务器检查库存,锁定库存,生成订单。通知网页服务器下单成功
4.网页服务器把下单结果传送到用户的浏览器

在这个场景下,步骤3最为耗时,有时需要超过1秒。对于步骤2,我们有很多方法来实现异步操作,如果不能实现,那网页服务器被应用服务器的响应所阻塞,吞吐量将急剧下降。

今天我们主要讨论步骤1,用户在下单,期望看到下单的结果,这是一个典型的同步操作,随着并发量上升,服务器的响应时间可能会超过30秒,最终造成浏览器的超时,用户什么也看不到,这是最坏的结果。我们来把这个同步调用,用异步来模拟,
1.浏览器发送下单请求到网页服务器(web server)
2.网页服务器发送请求到应用服务器
3.a)应用服务器生成订单号,把订单号返回网页服务器
b)应用服务器检查库存,锁定库存,把订单状态改成“下单成功”
4.浏览器收到订单号,采用ajax方式,每隔2秒,请求网页服务器,查询订单状态,直到获得“下单成功”的状态,跳转到下单成功页面

在这个异步流程中,步骤3.b和步骤4在时间上是并行执行的,但是,还记得我们的“读写分离”设计吗,步骤3访问的是“写”服务,步骤4访问的是“读”服务,这两个服务可以独立优化,不会成为对方的瓶颈。

使用浏览器的异步访问还带来额外的好处,在异步架构下,网页服务器和应用服务器的响应都非常迅速。原先的耗时请求“请帮我下单,并告诉我下单结果”被拆分成不耗时的请求“帮我下单”(一次)和“刚才的下单成功了吗”(一次或多次)。

如果使用了老式的网页服务器,不支持异步请求应用服务器,这样还能一举解决网页服务器访问应用服务器阻塞的问题。

在互联网高并发的状态改变(写)操作,把业务流程设计成异步是上策,比如大众点评的退款操作,系统会告诉用户“已收到您的退款请求,会在x个工作日能把钱退到您的账户”,这样的异步业务流程,对于系统毫无压力。有时出于用户体验不得不采用同步流程,比如上面说的下单流程,如果一开始设计成用异步流程来模拟,有两个好处
1.不会因为并发量突然上升而触发浏览器超时
2.可以对“读”,“写”服务独立优化
另外,在ajax异步操作时,还可以在浏览器绘制动画来安抚用户焦躁的情绪,不要说我没告诉你~

用异步流程模拟同步流程的补充说明,在浏览器内使用ajax轮询下单结果,是不得以。在服务器可以主动访问客户端的环境下,应该总是优先考虑回调的方式。浏览器是特殊的环境,服务器访问浏览器有时有困难,所以才使用比较保险的轮询模式。

原文链接:http://techshow.ctrip.com/archives/792.html

把异步架构延伸到客户端