2.3 关于分布式的一些概念与产品

从业IT行业就要做好心理准备,这个行业的技术日新月异,产品层出不穷,Hadoop还没学完,Spark就来了,Spark刚学会安装,Storm又来了……一个产品刚学会了安装配置开发步骤,没多久它又过时了,很多工程师就这样最终被拖累了,拖疲了,拖的放弃了技术路线,转向管理岗位。

这么多千姿百态的技术和产品背后有没有某些共性的东西呢?能让我们换了马甲还能认出它,能让我们超越学习每个产品的“安装配置开发”而掌握背后的精髓呢,这样学一反三,学一招应万招,能够牢牢掌握好技术的船舵,穿越一次次颠覆性的技术浪潮。

我们前面章节学习了Fourinone的很多分布式技术思想,那么当我们再次碰到市场上其他千姿百态的技术时,能否具备了一些识别能力,具备了一些悟性,帮助我们认清楚这些技术的本质和用途,避免重复性学习呢。

我们发现,市场上和分布式技术一起高频率出现还有这些技术名词:离线计算、实时计算、迭代计算、Mpi、Spark、Storm、BSP、DAG……我们接下来争取用简练的语言指出这些技术和产品的本质特征。

离线计算:它不是技术,也不是产品,而是一种分布式计算应用方式。Hadoop应用大部分都是离线计算,也就是数据通常保存在分布式文件系统中,在离线状态下去分析这些数据,类似离线状态的数据分析应用有很多,日志分析也属于一种离线计算,很多数据挖掘算法也是基于离线计算的。通常离线计算要分析的的数据量很大,一般用并行计算方式提高效率。

实时计算:其实多数是指一种增量计算,尽量让计算过程在短时间完成,而不是先导入多少数据到一个存储位置,然后再花费数个小时去分析这些数据,这是跟离线计算的本质区别,实时计算的数据是增量性的,少量的,变化的,来了就能算的。实时计算也是一种分布式计算应用方式。

迭代计算:这种计算方式使用Hadoop的Map/Reduce并不好弄,它有个最大的区别,就是需要考虑阶段,上一阶段的结果是下一阶段的条件,而且为了高效,最好中间结果保存内存,不要频繁读写文件,迭代计算在数据挖掘聚类算法、机器学习算法等等有很多应用,主要因为这些算法要大量训练,逐渐靠近精确结果,所以中间结果会做为下一轮条件。在2.1.11节我们讲述了一个迭代计算的简单例子。

这三种计算应用方式实际上是目前互联网行业运用最多的。下面介绍一些分布计算的产品。

1.Storm

国外Twitter公司的一款用于实时分析的软件,后来将其开源,Twitter这家企业技术实力并不强,规模也不能算大,国内新浪微博跟它类似,但是市场规模要更大。Storm是一个集成性质的软件产品,核心的消息通信和分布式协调都是使用其他开源软件,要运行Storm,需要依赖Apache ZooKeeper、JZMQ(ZeroMQ),ZooKeeper用于管理集群中的不同组件的协同,JZMQ(ZeroMQ)是其核心的内部消息系统。抛开Storm本身的安装配置开发步骤,我们认识到其流式计算方式,主要是指增量数据通过消息分发,多个消息接收者并行接收处理这样的一个机制,我们再回头看Fourinone能否也实现这样的机制。

2.3 关于分布式的一些概念与产品 - 图1提示

跟Storm集成ZooKeeper和JZMQ(ZeroMQ)去实现其核心功能不同,Fourinone的分布式协调和消息队列功能都是框架自带的功能,拥有自己的实现,底层技术上完全不依赖第三方厂商。

我们回头看2.1.2节“包工头-职介所-手工仓库-工人模式”的架构图会发现可以完全实现上面Storm的计算机制,通过工人分发任务调用,可以将数据以消息的形式封装到warehouse里,调用每个工人并行完成对数据消息的处理,而且根据前面章节的详细阐述,可以支持消息中枢的方式(跟Storm一样),也可以支持直接工头工人直接调用方式(这种方式更高效,避免消息中枢瓶颈),两种方式只需要配置修改一下,实现程序无须变动。

由于一直以来国内的工程师都是以学习使用国外开源软件作为生存技能,所以作为国外软件的Storm,在国内仍然拥有一些追随者,当然这也离不开Twitter公司的应用场景。但是我们看到Fourinone对分布式技术设计思想的归纳是覆盖这个应用范围的,在软件分布式技术这个层面,我们已经拥有相关的核心技术,并能做得更超越。Fourinone努力将分布式实现技术做到傻瓜化和普及化,让工程师能更快更轻松的掌握。

2.Spark

这实际上是加州伯克利大学学生的作品,年少轻狂、意气风发的学生们开发了一款基于内存的用于迭代计算的框架,立刻便风靡世界。有时真佩服这种国外学校的市场影响力,不用做什么推广就产生市场效应,特别是Spark还公开表示自己并不成熟稳定就能吸引大量追随者。Spark建立在一个叫做mesos的资源调度框架上(我们会在第7章介绍到mesos),mesos也是伯克利大学学生们的作品,后来开源后成为Apache项目。在mesos的Github官方Wiki上用黑色醒目字体写了这么句话:Please note that Mesos is still in beta.Though the current version is in use in production at Twitter,it may have some stability issues in certain environments.(https://github.com/mesos/mesos/wiki),公开表示还不稳定。

Spark用比较少的Scala代码实现,跟Hadoop基于分布式文件IO操作方式不同,Spark尽可能利用内存去做迭代计算,并使用mesos管理机器资源分配。

Fourinone则是通过多个包工头多环节链式处理和包工头内部多阶段处理的粗细粒度方式支持迭代类型计算,对于内存的使用提供完整的单机小型缓存和多机分布式缓存功能(详见第4章)。因此,通过提供多环节计算支持和分布式缓存功能,也能实现Spark基于内存完成迭代计算的机制。

在互联网公司内部,Spark相对于Storm来说,使用的场景要少很多,这个原因作者也观察思考过,虽然加州伯克利大学耀眼的光环仍然吸引着大量研究者,但是有一点估计是哑巴吃黄连,就是用起来比较难,学习成本大,出了问题难以处理。因此导致工程师真正使用Spark建立应用的场景很少。就好比西藏虽然很美很理想,但是氧气稀薄,紫外线太厉害,对皮肤的杀伤力太大,让人很难拥有。还有一点永远不会变,那就是工程师和研究人员不同,研究人员不能研究太通俗易懂、简单直观的东西,但是工程师最终只会支持自己能学会的、容易学会的、容易谋生的工具。EJB被Hibernate打败就是一个活生生例子,曾经的EJB是分布式技术领域的重要代表,为什么EJB2.0几乎到死亡的边缘,原因可能很多,但有一点是肯定的,因为它开发配置复杂难用,而Hibernate更轻量级和简单易用,其实Hibernate只能做O/R映射,根本解决不了分布式框架的问题,但是这不影响它取代EJB,因为大部分开发人员对EJB的理解就是当做O/R映射来用。

3.MPI

MPI(Message Passing Interface)是消息传递并行程序设计的标准之一,它是一个规范或者是一个库,但是不包括实现,目前最新版本为MPI-2(1997年发布),MPI能完成并行机的进程间通信,当前的实现版本有MPICH2和OPENMPI,目前广泛用于互联网企业的广告算法和迭代算法,阿里和国内其他大型互联网公司都有成百上千的MPI计算集群。

MPI定义了进程间的通信接口,比如mpi_send和mpi_recv等两个进程发送接收最常用接口,由此可见,基于MPI可以利用多个进程并行作业和交互,相对于上面介绍的Hadoop、Storm等计算平台,MPI能实现更灵活的并行计算方式,MPI更接近开发工具包和开发规范,在这点上跟Fourinone有很大的相似性,比如giveTask和doTask跟mpi_send和mpi_recv在并行计算上有相似点。

但是MPI仅仅定义出进程间的通信接口和交互方式,它缺乏对并行计算模式的设计归纳和角色抽象,比如没有任务调度角色包工头,没有多环节处理模式设计,没有独立的协调者角色,等等。因此基于MPI开发并行计算的难度要更大,编程要基于进程通信接口,缺乏一个完善而灵活的框架去简化复杂度和屏蔽编程错误,比如多进程合并的死锁问题,MPI开发者需要自己在开发中考虑。另外MPI的调度是一个突出问题,由于缺乏专门的任务调度角色,MPI的启动借助于操作系统进程管理器,把各个MPI进程当作操作系统进程孤立地启动和管理,因此MPI的调度方案一直是阿里、百度等公司进行研究的项目(我们在7.4.1“其他MPI作业资源调度技术”中会详细谈到)。

MPI在中国著名高校里作为研究生课程,有比较深的普及度,但是在业界开源社区并不火热,多数应用于Linux下的C环境开发,Java开源社区涉及的很少,几乎没有太知名的Java版MPI实现。

MPI虽然原始,开发运行较繁琐,但仍不失为一种灵活的并行计算方式。比如前面我们举的并行计算递归,或者并行计算求圆周率这些灵活计算的例子,使用Hadoop/STORM/Spark几乎做不了,使用MPI可以做出,但是实现肯定比Fourinone要麻烦。

4.BSP

Apache发布了一款Hama的并行计算软件牵扯出了BSP(Bulk Synchronous Parallel)思想,工程师们感慨万千,老外又出新产品和技术了,赶紧学啊,指南在哪里,helloworld例子在哪里,安装部署文档在哪里,又要开始学习新技术的苦逼日子了……其实我们不妨先从原理上分析一下该技术的来龙去脉和核心思想,再看看我们掌握的已有技术是否已经覆盖了。

Hama基于BSP实现了Google的Pregel的思想,提供一个相对于Map/Reduce和MPI更灵活的计算模式,大致由并行进程、消息通信层、栅栏同步层组成,并划分成一系列的superstep。官方资料强调BSP相对于MPI更侧重解决通信密集型计算,并对计算过程用一些参数指标,比如:进程数、进出消息数、消息大小等等,试图预估出整个计算需要耗费的时间。BSP相对于MPI的优化主要还是设计模型上,通信接口等优化不了什么。提出一个栅栏的设计,其实就是划分阶段和环节,用于迭代类型。还有就是基于消息的进程间通信交互,用发布接收消息模式代替点对点通信。

Google相关论文是:Pregel:A System for Large-Scale Graph Processing。

我们看到,BSP思想的核心其实就是将复杂计算过程划分多个阶段,每阶段等待最慢的进程计算完才能进行下一个阶段,并且试图通过一些参数指标估算出并行计算时间,但是这个时间较难估计,跟用户业务实现复杂度相关。而Fourinone对阶段的粗细粒度设计,BSP并没有这样的考虑。

5.DAG

我们对任务调度DAG(有向无环图)的认识更多是从微软的分布式并行计算平台Dryad来的,Dryad系统的总体构建用来支持有向无环图(Directed Acycline Graph,DAG)类型数据流的并行程序。Dryad的整体框架根据程序的要求完成调度工作,自动完成任务在各个节点上的运行。在Dryad平台上,每个Dryad工作或并行计算过程被表示为一个有向无环图,如图2-10所示。

2.3 关于分布式的一些概念与产品 - 图2

图2-10 DAG(有向无环图)

上面的描述很抽象,但简单来说,DAG最有价值的地方是构成了一个任务并行流的概念,比如我们的任务共有几个子任务,第一个子任务需要20台计算机并行计算完成,第二子任务需要40台计算机并行计算完成,第三个子任务需要30台机器完成,现在我们来设计任务调度,由于业务需求,任务一+条件一+任务三是我们需要的,另外一个需求任务一+条件二+任务二是我们需要,每个子任务都是多个计算机并行计算,并且子任务之间有先后顺序,同时需要匹配一定条件,反过来观察我们的计算流程中的节点,刚好构成了一个有向无环图的树结构。

那么Fourinone如何实现这样的任务流结构,多个任务之间有先后顺序,而且要匹配流转条件,同时每个任务又是并行处理。我们回头看看2.1.7节“计算集群模式和兼容遗留计算系统”的工头工人计算集群模式,每一个工头计算单元就是子任务,它同时由多个工人并行处理,在计算集群模式的架构图里我们看不到先后顺序,各工头计算单元之间是平行的,但是我们根据计算集群的扩充性很容易就能延伸出,工头计算单元可以前后衔接,衔接方式是通过在工人实现里面调用,由于工人实现是灵活的,所以也可以在衔接时增加条件判断,根据需要选择下一个子任务交给哪个工头单元完成。因此,通过计算集群的衔接方式,我们能很容易实现出一个DAG的结构出来。

还有更多的技术和产品我们就不一一分析了。

通过对以上对市场上高频率出现的分布式技术和产品分析,我们发现一个道理,其实分布式技术都是那套原理,分布式技术的产品只不过往不同方面的侧重有所不同,然后就包装出一个新的概念,比如支持多阶段包装出BSP,支持消息分流包装出Storm,支持计算任务流包装出DAG……其实我们用一个灵活的框架就可以概括这些不同侧重面的应用了,这样节省了我们大量的学习成本,并且开阔了眼界。碰到国外新技术和新产品,我们应该学会思考一下看看它是穿了什么马甲,而不要急着进行“安装配置开发步骤”这样无休止地盲目跟学。