severless 无服务架构简析2 - 架构设计

Serverless架构设计原则

无服务器架构有五大原则,描述了一个理想的无服务器系统应该如何构建。在构建无服务器架构时,可以运用这些原则,帮助指导你做出决定。

  1. 根据需要,使用计算服务来执行代码。
  2. 编写单一用途的无状态函数。
  3. 设计基于推送的、事件驱动的管道。
  4. 创建更粗实、更强大的前端。
  5. 拥抱第三方服务。

无服务器架构是SOA概念的自然延伸。在无服务器架构中,所有自定义代码作为孤立的、独立的、常常细粒度的函数来编写和执行,这些函数在AWS Lambda之类的无状态计算服务中运行。开发人员可以编写函数,执行几乎任何常见的任务,比如读取和写入到数据源、调用函数以及执行计算。在比较复杂的情况下,开发人员可以构建更复杂的管道,编排多个函数的调用。可能会有这种场景:仍需要服务器来处理某个任务。然而,这种情况并不多见;作为开发人员,应该尽量避免运行服务器、与之交互。

以下针对五大原则逐一进行解析。

根据需要使用计算服务执行代码

无服务器架构是SOA概念的自然延伸。在无服务器架构中,所有自定义代码作为孤立的、独立的、常常细粒度的函数来编写和执行,这些函数在AWS Lambda之类的无状态计算服务中运行。开发人员可以编写函数,执行几乎任何常见的任务,比如读取和写入到数据源、调用函数以及执行计算。在比较复杂的情况下,开发人员可以构建更复杂的管道,编排多个函数的调用。可能会有这种场景:仍需要服务器来处理某个任务。然而,这种情况并不多见;作为开发人员,你应该尽量避免运行服务器、与之交互。

编写单一用途的无状态函数

作为软件工程师,在设计函数时应该尽量着眼于单一职责原则(SRP)。单单处理某一项任务的函数更容易测试、运行稳定,而且带来的错误和意外的副作用比较少。通过以一种松散编排的方式将函数和服务组合起来,就能构建照样易于理解、易于管理的复杂后端系统。拥有明确定义的接口的细粒度函数也更有可能在无服务器架构里面被重复使用。

为Lambda等计算服务编写的代码应该以无状态方式来构建。它不得假设:本地资源或进程会在当前的会话之后生存下去。无状态性功能很强大,因为它让平台得以迅速扩展,以处理数量不断变化的入站事件或请求。

设计基于推送的、事件驱动的管道

可以构建满足任何用途的无服务器架构。系统可以一开始就构建成无服务器,也可以逐步重新设计现有的整体单一式应用程序,以便充分发挥这种架构的优势。最灵活、最强大的无服务器设计是事件驱动型的。图4显示了我们如何将亚马逊的简单存储服务(S3)、Lambda和Elastic Transcoder连接起来,构建一条事件驱动的、基于推送的管道。

构建事件驱动的、基于推送的系统常常有望降低成本和复杂性(你不需要运行额外代码来轮询变更),可能让整个用户体验更流畅。不言而喻,虽然事件驱动的、基于推送的模式是个美好的目标,但它们并非在所有情况下都是适当的或可以实现的。有时候,你不得不实施一个Lambda函数来轮询事件源或按时间表运行。

基于推送的管道设计风格

在这个例子中,用户上传一段转码成不同格式的视频。上传创建了一个事件,从而触发了Lambda函数。该函数创建一个转码作业。转码作业提交给转码视频服务,新的视频创建后,被保存到另一个S3存储桶。保存新视频这个过程触发了另一个Lambda函数,该函数反过来更新数据库。数据库中一个新的条目触发了最后一个函数,该函数创建通知,并调用通知服务,实现调派。在这个例子中,所有函数和服务仅仅负责一个动作,整个编排易于调整。

创建更粗实、更强大的前端

有必要记住这一点,在Lambda中运行的自定义代码应该快速执行。较早终结的函数较便宜,因为Lambda定价基于请求数量、执行时间段以及分配的内存量。在Lambda中要处理的事务比较少来得比较省钱。此外,拥有调用服务的更丰富的前端有利于更好的用户体验。在线资源之间更少的环节和缩短的延迟会让人觉得应用程序的性能和可用性更好。

数字签名的令牌让前端可以与不同的服务(包括数据库)直接进行通信。相比之下,在传统系统中,所有通信经由后端服务器来进行实现。让前端与服务进行通信有助于创建环节少得多、尽快获得所需资源的系统。

然而,不是一切都可以或者都应该在前端执行。有些秘密无法交给客户端设备。处理信用卡或向订户发送电子邮件只能由不受最终用户控制的服务来完成。在这种情况下,就需要计算服务来协调动作、验证数据,并实施安全。

另外要考虑的重要一点是一致性。如果前端负责写入到多个服务,可是中途出现故障,就会导致系统处于不一致的状态。在这种情况下,应该使用Lambda函数,因为它可以旨在从容地处理错误,重新尝试失败的操作。原子性和一致性在Lambda函数中实现起来和控制起来比在前端中容易得多。

拥抱第三方服务

如果第三方服务能提供价值、减少自定义代码,自然欢迎它们加入。如今,开发人员可以充分利用许多服务,从用于验证的Auth0服务,到用于支付处理的Stripe或Braintree服务,不一而足。只要考虑到价格、性能和可用性等因素,开发人员就应该试着采用第三方服务。开发人员花时间解决其领域独有的问题要比重复构建别人已经实施的功能有意义得多。如果已有了切实可行的第三方服务和API,别纯粹为了构建而构建。应站在巨人的肩上达到新的高度。

Serverless架构的推荐设计

软件设计已从昔日代码在大型机上运行,变成如今在多层系统上运行:在许多设计中,表示层、数据层和应用/逻辑层具有重要地位。在每层里面,可能有多个逻辑层次处理某一功能或领域的特定方面。还有可能跨众多层次的横切组件(cross-cutting component),比如日志或异常处理系统。青睐分层可以理解。分层让开发人员得以将关注点分离开来,开发出更易维护的应用程序。

但是,反过来也可能如此。层次太多可能导致效率低下。一个小小的变化常常带来连锁反应,导致开发人员修改整个系统中的每个层次,把大量的时间和精力花费在实施和测试上。层次数量越多,久而久之系统会变得越复杂、越笨拙。图4显示了具有多个层次的分层架构的一个例子。

图4:一个典型的三层应用程序通常由表示层、应用层和数据层组成。在每个层中,可能有多个层次,它们有特定的职责。开发人员可以选择各层次彼此如何联系。这可以是严格的自上而下的方式,也可以是一种松散的方式:各层次可以绕过紧邻的层次,与其他层次对话。层次的联系方式将影响性能、依赖项管理和应用程序的复杂性。然后还有横跨多个层次的功能――这叫作横切关注点(cross-cutting concern)

无服务器架构实际上有助于解决分层、非得更新太多对象这一问题。开发人员有机会消除或尽量减少层次,只要将系统分成多个功能,让前端得以安全地与服务、甚至数据库直接进行通信,如图5所示。这一切都可以以一种有组织的方式来实现,可以清楚地定义服务边界,防止意大利面条式实施和依赖项恶梦,让Lambda函数成为自治式,并且规划函数和服务如何交互。

图5:在无服务器架构中,没有单一的传统后端。通过API网关,应用程序的前端直接与服务、数据库或计算函数进行联系。然而,有些服务必须隐藏在计算服务函数后面,额外的安全措施和验证可能在这里进行

无服务器方法解决不了所有问题,也无法消除系统的底层复杂性。然而,如果实施得当,它还是可以为减少、厘清和管理复杂性带来机会。精心规划的无服务器架构可以让开发人员更容易在将来做出变化,这对任何长期的应用程序来说都是一个重要因素。下一节将更详细地讨论服务的组织和编排。

一些开发人员对于层次(layer)与层(tier)之间的区别分不太清楚。层是一种模块边界,它是为了隔离系统的主要组件而存在的。用户看得见的表示层与包括业务逻辑的应用层分开来。反过来,数据层是另一个独立的系统,负责数据的管理、持久化和访问。分组在一个层中的组件可能实际上驻留在不同的基础设施上。层次是逻辑片段,负责执行应用程序中特定的职责。每一层在里面可能有多个层次,负责功能的不同部分,比如域服务。

从现有应用转换到Serverless架构

Serverless方法的一个优点是现有应用可以逐渐转换为Serverless架构。如果开发人员面对一个单一的代码库,他们可以逐渐分开,创建应用程序可以与之通信的Lambda函数。

最好的方法是最初创建一个原型来测试,开发人员假设如果系统部分或全部无服务器的情况下系统将如何运行。遗留系统往往具有需要创造性解决方案进行和任何大规模的重构。因此将存在设计与转换中的妥协与过度,系统可能最终是一个混合,参见图6,但是整体系统可能会逐步的将一些方面使用Serverless和使用第三方服务,而不是继续使用传统架构,不再扩展或需要昂贵的基础架构运行。

图6:无服务器的架构不是一个全有或全无的命题。如果当前有在服务器上运行的传统应用程序,可以开始逐步提取组件并在隔离服务或计算功能中运行它们。 您可以将整体应用程序分离到各种基础架构即服务(IaaS),PaaS,容器,Lambda函数和第三方服务(如果它有帮助)

从传统的基于服务器的应用程序到可扩展的Serverless架构的过渡可能需要时间才能正确完成。它需要仔细和缓慢地进行,开发人员需要有一个良好的测试计划和一个伟大的DevOps战略作为开始。

分享到