极客时间
http://139.196.35.134/time/#/article/7036[2018/8/1 10:48:01]
性能设计篇之"异步处理"
2018-05-15 陈皓
在弹力设计篇中我们讲过,异步通讯的设计模式有助于提高系统的稳定性和容错能力。其实,异步通讯在分布式系统中还可以增加整个系统的吞吐量,从而可以面对更高的并发,并
可以更多地利用好现有的系统资源。为什么这么说呢?
我们试想一下,在你的工作中,有很多人会来找你,让你帮着做事。如果你是这种请求响应式的工作方式,那么本质上来说,你是在被动工作,也就是被别人驱动的工作方式。
当你在做一件事的时候,如果有别人来找你做其它事,你就会被打断而要去干别的事。另外,没办法把这些事统筹安排。如果可以统筹安排,本来五件事只需要
2
个小时,如果不
能,你可能要做出
5
个小时来。异步处理任务可以让你更好地利用好时间和资源。利用好了时间和资源,性能自然就会提升上来。
这就好像邮递业务一样,你寄东西的时候,邮递公司会把大量的去往同一个方向的订单合并处理,并统一地调配物流交通工具,从而在整体上更为节省资源和时间。
在分布式架构中,我们的系统被拆成了很多的子系统。如果想把这堆系统合理地用好,并更快地处理大量的任务,我们就需要统一地规划和统筹整体,这样可以达到整体的最优。本
质上,这和邮递公司处理邮件一样,是相同的道理。
在计算机的世界里,到处都是异步处理。比如:当程序读写文件时,我们的操作系统并不会真正同步地去操作硬盘,而是把硬盘读写请求先在内存中
hold
上一小会儿(几十毫秒),
然后,对这些读写请求做
merge
和
sort
。
也就是说,
merge
是把相同的操作合并,相同的读操作只读一次,相同的写操作,只写最后一次,而
sort
是把不同的操作排个序,这样可以让硬盘向一个方向转一次就可以把所有的
数据读出来,而不是来来回回地转。这样可以极大地提高硬盘的吞吐率。
再如,我们的
TCP
协议向网络发包的时候,会把我们要发的数据先在缓冲区中进行囤积,当囤积到一定尺寸时(
MTU
),才向网络发送,这样可以最大化利用我们的网络带宽。而传
输速度和性能也会变得很快。
这就是异步系统所带来的好处
——
让我们的系统可以统一调度。
另外,我举上面这两个例子是想告诉你,我们可能会觉得异步通讯慢,其实并不然,我们同样也可以把异步做得比较实时。
多说一句,就算是有延时,异步处理在用户体验上也可以给用户带来一个不错的用户体验,那就是用户可以有机会反悔之前的操作。
异步处理的设计
之前,我们在弹力设计中讲的是异步通讯,这里,我们想讲的是异步任务处理。当然,这里面没有什么冲突的,只不过是,异步通讯讲的是怎么把系统连接起来,而我们这里想讲的
是怎么处理任务。
首先,我们需要一个前台系统,把用户发来的请求一一记录下来。这有点像请求日志。这样,我们的操作在数据库或是存储上只会有追加的操作,性能会很高。我们收到请求后,给
客户端返回
"
收到请求,正在处理中
"
。
然后,我们有个任务处理系统来真正地处理收到的这些请求。为了解耦,我们需要一个任务派发器,这里就会出来两个事,一个是推模型
Push
,一个是拉模型
Pull
。
所谓
Push
推模型,就是把任务派发给相应的人去处理,有点像是一个工头的调度者的角色。而
Pull
拉模型,则是由处理的人来拉取任务处理。这两种模型各有各的好坏。一般来
说,
Push
模型可以做调度,但是它需要知道下游工作结点的情况。
除了要知道哪些是活着的,还要知道它们的忙闲程度。这样一来,当下游工作结点扩容缩容或是有故障需要维护等一些情况发生时,
Push
结点都需要知道,这会增加一定的系统复杂
度。而
Pull
的好处则是可以让上游结点不用关心下游结点的状态,只要自己忙得过来,就会来拿任务处理,这样可以减少一定的复杂度,但是少了整体任务调度。
一般来说,我们构建的都是推拉结合的系统,
Push
端会做一定的任务调度,比如它可以像物流那样把相同商品的订单都合并起来,打成一个包,交给下游系统让其一次处理掉;也可
以把同一个用户的订单中的不同商品给拆成多个订单。然后
Pull
端来订阅
Push
端发出来的异步消息,处理相应的任务。
事件溯源
在这里,我们需要提一下
Event Sourcing
(事件溯源)这个设计模式。
异步处理