通常,当服务涉及到的数据量大到一定程度以后,我们会考虑拆分数据。在这种分布式架构中,每个结点只拥有总数据量的其中一部分,而最终的输出结果会汇总所有结点的结果。这种Map-reduce思想的架构,是尽量不去查分程序,而只是拆分数据来支持大数据的处理,如下图所示。这种框架对每个worker结点的可靠性要求比较高,如果某一个worker结点挂掉了,那么最后的输出结果将是不全的。
我设计的这个分布式框架解决的是另一个问题。比如一个程序中含有A, B, C, D四个逻辑,它们在程序中是串行执行的,彼此之间通信的信号量并不大,同时4个模块使用的数据独立。显然,我们将这四个模块合并在一个程序中,是去做一件独立的事情,但在某些情况下仍然会带来不方便。比如现在程序出现了一个问题,C模块的负责人认为是B模块的问题,如果要调试只能线下修改程序,在B模块和C模块中加入printf语句打印其通信的值,这显然是一件非常费力的事情。另一个问题是,如果B模块实在太通用的,它会被别的程序集成进去,但这么做对维护工作又造成了困扰,因为B模块的负责人(这是不一定是同一组人了)需要保证这个模块在多个程序中是一致的。所以我们的解决方案就是把每个模块独立出去,我们的框架需要满足它们的通信需求。
先来看看最终整个分布式系统长成什么样子。如下图所示。该图中所有的服务都有3个结点,所有上游的结点都可以把它们的结果投放到任意一个下游结点中。只要服务间的通信量不太大,拆分后对其性能的几乎没有影响。这样的拆分能完美的解决上诉提到的两个问题,因为每个结点均能支持http请求,非常方便问题的定位。同样,每个结点还能接受别的服务发来的请求,这样完全可以做到整个公司内该模块仅有一份。
基于上图中的结果,我们的框架设计需要满足哪些要求呢?
首先是可靠性,如下图所示,如果服务B中的某一台机器挂掉了,上游服务能及时感知该事件的发生,那么剩下的任务将不会分配给挂掉的结点。同样,如果B服务增加了一台机器,A服务器中的所有结点也能及时获知,并把一部分任务分配给新加入的机器。
其次是智能性,如下图所示,如果服务B中所有机器的性能不同,某些机器由于性能较弱处理速度要慢些,那么上游的服务会尽量减少该机器的任务量。
最后是协调性。虽然之前说到所有模块的数据独立,但这里说到的独立仅是不重复而已,它们之间会按照顺序号给关联起来。当这些数据有增量更新的时候,我们希望更新的速度尽量是同步的。在这里我们会加入一个控制结点,zookeeper是一个很好的选择。
具体的框架模块设计请看一种分布式框架设计(二)。