基于云的应用与运行在私有数据中心的应用之间最大的差别就是可扩展性。云提供了按需扩展的能力,能够根据负载的波动对应用进行扩展和收缩。但是传统应用要充分发挥云的优势,并不是简单地将应用部署到云上就万事大吉,而是需要根据云的特点围绕可扩展性重新进行架构设计,近日 AppDynamics 的开发布道者 Dustin.Whittle撰文阐述了适合云端部署的应用架构,对我们传统应用往云端部署有很大的启发和借鉴意义。
应用的架构
Dustin.Whittle 给出了云应用的示例架构,它具有高度的可扩展性,如下图所示:
在这个图中,应用按照分层的理念进行了拆分,分别介绍如下:
**客户端层:** 客户端层包含了针对目标平台的用户界面,可能会包括基于 Web 的、移动端的甚至是胖客户端的用户界面。一般来讲,这可能会是 Web 应用,包含诸如用户管理、会话管理、页面构建等功能,但是其他客户端所发起的交互都需要以 RESTful 服务的形式调用服务器。
**服务:** 服务器包含了缓存服务以及聚合(aggregate)服务,其中缓存服务中持有记录系统(system of record)中最新的已知状态,而聚集服务会直接与记录系统交互,并且会执行一些破坏性的操作(会改变记录系统中的状态)。
**记录系统:** 记录系统是领域特定的服务端,它会驱动业务功能,可能会包括客户管理系统、采购系统、预定系统等等,这些很可能是遗留系统,你的应用需要与其进行交互。聚集服务要负责将你的应用从这些特有的记录系统中抽象出来,并为你的应用提供一致的前端接口。
ESB_:_ 当记录系统发生数据变化的时候,它需要触发到指定主题(topic)的事件,这就是事件驱动架构(event-driven architecture,EDA)能够影响应用的地方了:当记录系统进行了一项其他系统可能感兴趣的变更时,它会触发一个事件,任何关注记录系统的其他系统会监听到这个事件,并作出对应的响应。这也是使用使用主题(topic)而不是队列(queue)的原因:队列支持点对点(point-to-point)的消息,而主题支持发布 - 订阅(publish-subscribe)的消息或事件。当与遗留系统进行集成时,我们很期望这些遗留的系统能够免遭负载的影响。因此,我们实现了一个缓存系统,这个缓存系统维持了记录系统中所有最新的已知状态。缓存系统会使用 EDA 的规则监听记录系统的变化,它会更新自己所保存数据的版本,从而保证与记录系统中的数据相匹配。这是一个很强大的策略,不过会将一致性模型变为最终一致性,也就是说如果你在社交媒体上发布一条状态的话,你的朋友可能在几秒钟甚至几分钟之后才能看到,数据最终是一致的,但有时你所看到的与你的朋友所看到的并不一致。如果能接受这种一致性的话,就能在很大程度上实现可扩展性的收益。
**NoSQL****:** 在数据存储方面,有很多的可选方案,但如果要存储大量数据的话,使用 NoSQL 存储能够更容易地扩展。有多种 NoSQL 存储可供选择,不过这要匹配所存储数据的特点,如 MongoDB 适合存储可搜索的数据,Neo4j 适合存储高度互相关联的数据,而 Cassandra 适合存储键 / 值对,像 Solr 这样的搜索索引有利于加速对经常访问数据的查询。
将应用拆分为多个层的时候,最好的模式就是使用面向服务架构(service-oriented architecture,SOA)。要实现这一点,可以使用 SOAP,也可以使用 REST,但是 REST 更为合适,因为它可扩展性更强。作者接下来对 REST 的理念进行了深入的阐述,InfoQ 上关于 REST 已有很多相关的文章,如这里和这里,甚至包括 Roy Fielding 经典博士论文的中译本,所以这里不再赘述。不过,作者在这里特别强调了RESTful Web 服务能够保持无状态性(stateless)。无状态是实现可扩展性的关键需求,因为无状态,所以请求可以由任何一个服务器响应。如果你在服务层上需要更多的容量时,只需要为该层添加虚拟机即可,而不需关注客户端状态保持的问题,负载可以很容易地重新分配。
部署到云端
前面介绍了基于云的应用架构,接下来作者阐述了这样的应用该如何部署到云端。
RESTful Web 服务要部署到 Web 容器中,并且要位于数据存储之前。这些 Web 服务是没有状态的,只会反映其暴露的底层数据的状态,因此可以根据需要部署任意数量的服务器。在基于云的部署中,开始时可以开启足够的实例以应对日常的需求,然后配置弹性策略,从而根据负载增加或减少服务器的数量。衡量饱和度的最好指标就是服务的响应时间。另外还需要考虑这些服务所使用的底层数据存储的性能。示例的部署图如下所示:
如果在云部署时有 EDA 的需求,那么就需要部署 ESB,作者给出的建议是根据功能对 ESB 进行分区(partitioning),这样一个 segment 的过大负载不会影响到其他的 segment。如下图所示:
这种分离使 System 1 的负载与 System 2 的负载实现了隔离。如果 System 1 产生的负载拖慢了 ESB,它只会影响到自己的 segment,并不会影响到 System 2 的 segment,因为它部署在其他硬件上。如果 ESB 产品支持的话,我们还可以给指定的 segment 添加服务器来实现扩展。
基于云的应用与传统应用有着很大的差别,因为它有着不同的扩展性需求。基于云的应用必须有足够的弹性以应对服务器的添加与移除,必须松耦合,必须尽可能的无状态,必须预先规划失败的情况,并且必须能够从几台服务器扩展到成千上万台服务器。
针对云应用并没有唯一正确的架构,但是本文所阐述的 RESTful 服务以及事件驱动架构却是经过实践检验有效的架构。作者认为 REST 和 EDA 是实现云端可扩展应用的基本工具。
目前,国内许多传统的软件厂商正在逐渐往云端迁移,希望本文所阐述的架构理念能够为读者提供一些借鉴。
评论