
可扩展的Web架构概念与设计
对于每个Web应用程序而言,决定其成功的基本因素之一是其能否根据客户的要求无缝有效地适应增长或"扩展"的能力。 可扩展的Web应用程序是一个网站,它能够处理用户和负载的增加(无论是逐渐增加还是突然增加),而不会中断最终用户的活动。
在当今世界,客户对设计欠佳的应用程序的耐心和同情心有限。 没有等待网页加载,上传图像或处理表单的概念。 如果您的应用程序设计不当,并且能够及时处理请求,那么它必然会被遗弃。
在本文中,我将引导您完成一些可扩展Web应用程序的体系结构设计。 通过详细介绍每个体系结构的每个角落来使博客变得冗长是不理想的,我想充当缓解者,我将尽我所能来简要地和说明性地解释核心概念,并指导您 一些良好的资源,可供进一步阅读和研究。
该博客的内容将组织如下:
· 整体架构
· 通过微服务架构扩展
· 带走
· 下一步是什么?
· 参考文献
构建和运行可扩展的网站或应用程序到底意味着什么? 在原始级别上,它只是通过Internet将用户与远程资源连接起来-使它具有可伸缩性的部分是资源或对这些资源的访问分布在多个服务器上,这使得设计大型Web系统容易出错并且 有问题的。 那么,为什么需要健壮和可扩展的Web体系结构? 在其诞生之前,Web应用程序是如何构建的?
一,整体架构
"整体"一词的意思是"一块大石头",因此当我们谈论整体时,我们传达的是大型统一块的想法。 在软件体系结构中,单片应用程序是一个独立的,单层的传统软件应用程序,它在单个代码库中包含整个应用程序代码。 这是此架构的说明:
从图2中可以看到,应用程序的所有层和组件都紧密连接。 采用这种架构的应用程序易于开发,测试,部署和管理,因为所有内容都驻留在一个存储库中。 将不同的组件捆绑到一个统一的工作空间中并没有复杂性。
如果所需资源稀少并且需要处理的请求数量很少,那么单片应用程序可能会成功,但是越来越多的人对它们感到沮丧,尤其是当其紧密捆绑的属性带来一些主要缺点时,需要重新扩展应用程序以扩展计算能力:
· 当层中的次要代码更改需要重新部署整个应用程序时,连续集成/连续部署(CI / CD)会很麻烦。
· 当组件被分组到一个代码库中时,单片应用程序就会出现单点故障。 万一任何层都有错误,则有可能破坏整个应用程序。
· 灵活性和可伸缩性也是整体应用程序中的挑战,因为更改一层通常需要对所有其他层进行修改和测试。 随着代码大小的增加,管理起来可能会有些棘手。
· 由于使用不同的技术实现不同的组件可能会使跨组件集成面临兼容性问题,因此使用单片式体系结构构建复杂的应用程序限制了技术的利用。
这些挫折导致了可扩展性和健壮性大大提高的体系结构的诞生,我们将在以下各节中进行深入探讨。
二。 通过微服务架构扩展
与单片架构相反,微服务架构是将服务划分为多个松散耦合在一起的单独模块的应用程序结构,它们通过轻量级机制(通常是HTTP资源API,WebSocket或AMQP)相互通信。 松散耦合意味着每个服务在特定上下文边界内实现特定的端到端域或业务功能,并且每个服务必须自主开发并独立部署。 每个服务应独立于大型应用程序而拥有其相关的域数据模型和域逻辑(主权和分散数据管理)。
即使没有正式定义微服务架构样式,也有一些共同的特征,所有遵循该架构的应用程序都应展现出可扩展性。 这些特征包括智能端点和哑管道,分散式治理,基础架构自动化和故障隔离。 让我们在以下部分中讨论每个特征。
1.集成模式:智能端点和哑管道
当微服务应用程序将其结构拆分为松散耦合的多个组件时,自然要问的问题是:成对组件在哪些协议中进行通信? 在给定协议的情况下,应以哪种模式在整个应用程序中路由数据?
a。 通讯协议
在不同流程之间建立通信结构时,有许多产品和方法强调要在通信机制本身中投入大量精力。 一个很好的例子就是企业服务总线(ESB),其中ESP产品通常包括用于消息路由,编排,转换和应用业务规则的复杂工具。 (ESB也是微服务和面向服务的体系结构(SOA)之间的根本区别。两者之间的进一步比较可以在[2]中找到)。
相反,微服务倾向于一种替代方法:"智能端点和哑管道"。 微服务应用程序的目标是尽可能地减少耦合和凝聚力-它们拥有自己的域逻辑,在传统的Unix意义上更像是过滤器-接收请求,适当地应用逻辑并产生响应,这通常使用两种常见的方法进行编排 通讯形式:请求-响应和观察者。
在请求-响应通信模式中,微服务利用构造万维网的原理:一个服务通过发出显式请求来调用另一服务,通常是存储或检索数据。 然后,该服务期待一个响应,无论是资源还是确认。 实施此模式的最基本方法是使用HTTP,最好遵循REST原则。 两个服务之间基于标准HTTP的通信管道通常如下所示:
图3显示了服务之间的HTTP通信协议的简单抽象。 负载均衡器可用作中间件层,以将请求路由到支持服务的实例之一。
尽管REST设计模式非常普遍并且广为人知,但它具有同步性和阻塞性的局限性。 异步可以使用第二个协议范式,即观察者模型来实现。 在微服务中,更确切地说是应该是发布/订阅模式,而不是观察者模型(可以在[3]处分析两者之间的差异)。 发布/订阅模式是一种基于事件的隐式调用,其中生产服务(发布者)发布一个事件,监视该事件的一个或多个消费者服务(订阅者)通过异步运行逻辑来响应该事件,而发布者对此并不了解。 事件。 订户可以通过使用RabbitMQ,ZeroMQ,Kafka或Redis发布/订阅订阅消息队列/代理服务(因此引用中的哑管道)来确认事件的发生。 可以在[4]处更仔细地了解通信协议。
现在,让我们讨论在整个应用程序中路由/收集数据以实现无缝服务集成的模式。
b。 聚合模式
在微服务领域,Aggregator是指一个基本的网页,该网页调用各种服务以获取所需的信息或实现所需的功能。 在需要通过组合来自多个服务的数据进行输出的情况下,此模式非常有用。
例如,假设我们有一个滴答应用程序,该应用程序用于查看详细信息并为体育,电影和其他活动等各种活动预订门票。 假设每个活动都由一个服务来管理,如图所示:
通过REST端点公开管理活动的三个服务,每个服务都在AWS Lambda上实现。 在这里,聚合器服务为其数据调用其他三个服务,并将数据链接到其REST端点,该端点公开给客户端。 更全面的分析和代码可以在[5]中找到。
c。 API网关
在微服务架构中,客户端通常需要使用多个服务中的功能。 如果直接执行该消耗,则客户端需要处理对服务端点的多次调用,这会带来许多严重影响服务延迟的问题,例如过多的网络往返,跨领域关注(授权,SSL等),紧密耦合。 客户端应用程序与内部微服务设计之间的联系[6]。
API网关是一项为某些服务组提供单入口的服务。 它位于客户端应用程序和服务之间,充当反向代理,将请求从客户端路由到服务。 为了解决上述问题,它提供了跨领域功能,例如SSL终止,身份验证,负载平衡,缓存等。 它也可以用作反向代理或网关路由,并且可以进行请求聚合(类似于将多个请求批处理在一起)。
我们不仅可以通过安全,及时地处理更多请求来通过API网关扩展更大的系统,还可以通过基于业务边界和客户端应用将其隔离到多个API网关中来扩展API网关服务本身。 例如,可以有三个API网关,每个网关处理移动客户端的移动API,浏览器中的JS客户端的浏览器API,第三方开发人员的公共API(有关网关隔离的介绍,请参见[6]关于Azure的信息) API网关):
d。 连锁或责任链(CoR)
责任链是一种行为设计模式,它通过将请求沿沿着称为处理程序或处理元素的独立对象链传递来的多个链状输出合并,从而生成单个组合输出。 收到请求后,每个处理程序都会决定处理该请求,还是将其传递给链中的下一个处理程序。 该请求沿着火车行进,直到所有处理人员都有机会对其进行处理。 处理程序还可以决定不进一步处理,并停止将请求向下传递给链。
通过将服务封装在"管道"抽象中,CoR简化了对象互连。 代替发送者和接收者维护对所有候选接收者的引用,每个发送者保持对链的头部的单个引用,而每个接收者保持对链中其直接后继的单个引用。
由于所有这些服务都使用同步HTTP请求/响应消息传递,因此直到请求通过所有服务并生成相应的响应,客户端才获得输出。 因此,建议不要采用这种模式。
e。 分支模式
分支设计模式是一种模式,您可以在其中同时处理来自两个或多个独立服务的请求和响应。 它扩展了聚合器和链设计模式,在该模式中,它使用聚合器根据需求从多条链或单条链中产生响应。
在上图中,网页或复合微服务的服务A可以同时调用两个不同的链,在这种情况下,这类似于Aggregator设计模式。 或者,服务A只能基于从客户端收到的请求来调用一个链。
f。 Saga
Saga是一系列本地交易。 Saga中的每个服务执行其自己的事务并发布事件。 其他服务侦听该事件并相应地执行。 如果一项交易失败,则Saga会发出补偿交易,以回滚先前交易产生的影响。
Saga的类型有两种:
· 编排:负责协调所有交易并指导参与者服务根据先前交易执行期间/之后产生的事件执行本地交易的协调器:
· 编舞:没有这种类型的中央协调员。 每个参与服务都执行其事务并发布事件。 其他服务根据这些事件进行操作并相应地执行交易。 他们可能不会根据情况发布其他事件:
2.权力下放的治理
集中治理的后果之一是倾向于在单一技术平台上实现标准化。 由于并非每个问题都是钉子,也不是每个解决方案都是锤子,所以这个问题很棘手,因此使用正确的工具完成正确的工作更为可取。
将整体组件拆分为独立运营的服务,我们在构建每个组件时都有一个选择。 服务可以使用不同的语言(C ++,Go,Node.js等)和不同的数据存储(SQL,NoSQL)。 在开发过程中使用多种语言的能力意味着可以使用适合该特定组件的语言和框架来构建每个组件。 此外,团队可以在发布时使用新的语言和框架来构建新的组件和服务。
分散数据管理:数据库模式
秉承微服务的核心原则,在考虑我们的数据库结构时,请快速考虑以下几点:
· 服务必须是松散耦合的。 它们可以独立开发,部署和扩展。
· 商业交易可能会强制跨越多个服务的不变量
· 商业交易可能会查询多个服务拥有的数据
· 有时会复制数据库并对其进行分片以进行扩展
· 不同的服务有不同的数据存储要求
考虑到上述原则,让我们描述微服务应用程序应遵循的一些数据库设计模式。
a。 每个服务的数据库
整体应用程序倾向于使用单个逻辑数据库存储持久性数据,而微服务则通过让每个服务管理自己的数据库来分散数据存储决策的权限,这些数据库可以是同一数据库技术的不同实例(棕场应用程序),也可以是完全不同的数据库系统(Polyglot Persistance –绿场应用程序) )。 每个数据库都是为一项服务专门设计的,并且只能由其他服务通过公开的API访问。
b。 每个服务共享数据库
上面,我们提到了每个服务只有一个数据库对于微服务来说是理想的。 但是,如果应用程序是一个棕色的领域,后来又转换为微服务,那么非规范化就不那么容易了。 因此,单片和微服务之间的中间解决方案是让不同的服务(图13中的服务1和服务2)共享一个数据库。 每个服务都使用本地ACID事务自由访问其他服务所拥有的数据,这对已经构建了整体应用程序并仍然保持分布式服务模型意义的开发人员更为熟悉。
不出所料,即使单个数据库更易于操作且更熟悉开发,它可能会带来多个问题,例如架构耦合,运行时耦合或多个服务数据的存储扩展不足。
c。 命令查询职责隔离(CQRS)
在微服务架构中,服务可以查询从多个其他服务收集的数据。 对于普通数据库,我们使用一个系统来对数据进行修改和查询数据。 使用CQRS,系统的一部分处理命令,这些命令捕获修改状态的请求,而系统的另一部分处理查询。 例如,使用事件源数据库实现,开发人员可能选择将命令作为事件来处理和处理,也许将命令列表存储在数据存储中。 同时,查询模型可以查询事件存储并根据存储的事件创建投影以组合域对象的状态。
这种分离形式允许不同类型的缩放。 我们系统的命令和查询部分可以存在于不同的服务或不同的硬件中,并且可以使用完全不同类型的数据存储。 例如,通过使用查询模型的多种实现,您甚至可以支持不同类型的读取格式,也许支持数据的基于图形的表示或数据的基于键/值的形式。 可以在这里仔细查看此模式。
3.基础设施自动化
更快的发布周期是微服务体系结构的主要优势之一。 但是,如果没有良好的CI / CD流程,您将无法实现微服务所承诺的敏捷性(当我们谈论CI / CD时,我们实际上是在谈论几个相关概念:持续集成,持续交付和持续部署)。 设计健壮的CI / CD流程时应采用允许团队独立构建和部署自己的服务而不会影响或破坏其他团队的方式。 此外,CI / CD流程应首先在dev / test / QA / staging环境中部署服务新版本以进行验证,然后才能在生产中生效。
早在2019年夏季,我在Got It的任务之一就是实施CI / CD管道管理工具,以使用AWS CodePipeline加速基础架构更新和产品迭代周期。 任务的目标很简单:构建内部门户的后端,使工程师可以使用Boto3配置管道的触发器,部署管道并返回任何错误(如果有的话),以提高临时环境中的团队速度和工作吞吐量。 该工具在Got It的产品生态系统中扮演着重要角色,有助于实现微服务架构所承诺的敏捷性。
过去,用于单片应用程序的管道往往共享它们正在构建的应用程序的相同特征。 由于两个不同的项目可能具有不同的管道,因此对于整体应用程序,公司可能必须处理1-5个管道(假设有1-5个项目)。 对于微服务体系结构,如果将每个整体分为5个微服务,则数量迅速跃升至25。 根据此计算,如果管道数量继续线性增长,那么拥有50多个项目(每个项目都划分为10个微服务)的大型组织一次维护500多个管道也就不足为奇了。 这就解释了为什么在处理CI / CD管道设计时要特别考虑对旧管道进行维护和增加服务,以及创建新管道。
CodeFresh提出了一种简单但功能强大的设计来解决上述问题。 这里的基本思想是单个应用程序的所有服务都具有几乎相同的生命周期。 它们以类似的方式进行编译,打包和部署。 因此,我们没有为每个服务使用多个管道,而是由所有多个服务共享一个管道。
现在,管道的构建非常简单,因为它仅包含一个用于多个git触发器的共享管道,每个触发器都来自特定微服务的git来源。 当在应用程序中添加新的微服务时,管道已经存在,并且仅添加了该微服务的新触发器。 因此,随着微服务数量的增长,唯一增加的是触发器列表。 所有管道完全相同。
4.故障隔离
微服务架构的好处之一是组件或功能的故障隔离。换句话说,如果一个服务崩溃,则其他组件可以继续运行,直到该服务恢复为止。以电子商务应用程序为例。理想情况下,每当客户尝试购买商品或查看某个商品时,我们的应用程序都会根据所选商品给出关于下一步该买什么商品的建议,以尝试创造更多的销售额。在这种情况下,故障隔离意味着如果推荐服务因某种原因出现故障,客户仍然可以使用结帐服务进行购买。可能不会向客户提供建议,并且可能会失去一些潜在的销售,但并不是全部。身份验证服务的崩溃可能更具破坏性,但是用户可以继续填写自己的购物车,而不是完全中断服务,并且在准备结帐时,可以显示一个屏幕,警告他们是否无法进行购物。在完成购买后,他们应该稍后再试。此外,由于我们可以在发生故障时快速回滚,因此中断时间很短,并且可以将业务影响最小化。
可以隔离故障,并从此缓解。 为此,面对服务部分故障,我们需要有效的策略/模式。
a。 断路器模式
这是我在Sam Newman的Building Microservices中找到的与该模式有关的有趣示例:
在您自己的家中,存在断路器来保护您的电气设备免受电源尖峰的影响。 如果出现尖峰,则断路器会烧断,从而保护了昂贵的家用电器。 您还可以手动禁用断路器以切断部分房屋的电源,从而使您可以安全地使用电子设备。 [8]
实际上,在微服务架构中,我们将有一个或多个服务调用其他服务以进行数据/任何其他事务。 在最终返回错误之前,下游服务可能会因负载过重或其他相关资源而关闭,或者响应速度可能很慢。 如果多次重试该请求,可能会更糟,这会花费大量时间,直到我们最终收到错误消息。
因此,为避免此类问题,我们实施了断路器。 一种典型的实现方式是使用代理作为电路屏障。 对下游资源的一定数量的请求失败后,断路器被炸断,在特定时间段内向该服务发出请求而跳闸:
当断路器关闭时,有一些选择:一种是将请求排队,然后稍后重试,如果请求是异步作业的一部分,则这是适当的。 如果请求是同步调用链的一部分,则最好快速失败,然后将错误传播到整个链上,或者选择微妙的功能降级。 其他一些策略可以在[7]中找到。
总结
可伸缩Web体系结构设计的六项原则包括可用性,可靠性,一致性,可伸缩性,可管理性和成本。
在软件体系结构中,单片应用程序是一个独立的,单层的传统软件应用程序,它在单个代码库中包含整个应用程序代码。 尽管单片应用程序可以在较小的应用程序规模上取得成功,但它带来了可伸缩性问题,例如在代码更改,单点故障,复杂的修改和测试以及受限的技术杠杆作用下重新部署整个CI / CD管道。
微服务架构提供了长期的敏捷性。 它们使您可以基于许多可独立部署的服务创建应用程序,从而在复杂,大型和高度可扩展的系统中实现更好的可维护性,每个服务均具有细化和自治的生命周期。 由于服务彼此独立运行,因此我们也具有开发,部署和扩展的自主权,因为每个服务都可以独立扩展。 这样,只有需要更多处理能力或网络带宽的服务才可以按需扩展,从而节省了扩展应用程序其他不需要扩展的区域的成本。
下一步是什么?
在下一篇文章中,我们来看看另外两种架构来扩展您的Web应用程序:面向服务的架构(SOA)和带有AWS Lambda的无服务器架构(FaaS)。
请继续关注下一篇文章!
五,参考资料
[1] KeyCDN。 比较URI和URL。 2018年10月4日,https:
//www.keycdn.com/support/comparing-uri-vs-url
[2] Edureka !。 微服务与SOA。 Youtube,2018年3月12日,https://www.youtube.com/watch?v = EpyPFnjue38
[3] Ahmed Shamim Hassan。 观察者vs Pub-Sub模式。 Hackernoon,2017年10月28日,https:
//hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c
[4] Nathan Peck,微服务原理:智能端点和哑管道。 中等,2017年9月1日,https://medium.com/@
nathankpeck/microservice-principles-smart-endpoints-and-dumb-pipes-5691d410700f
[5] Satrajit Basu,使用AWS Lambda的微服务聚合器设计模式。 Dzone,2018年9月7日,https:
//dzone.com/articles/microservices-aggregator-design-pattern-using-aws
[6] Microsoft,API网关模式与直接客户端到微服务的通信。 微软文档,2019年1月7日,https:
//docs.microsoft.com/zh-cn/dotnet/architecture/microservices/architect-microservice-container-applications/direct-client-to-microservice-communication-versus-the- api网关模式
[7] Microsoft,处理部分故障的策略。 微软文档,2018年10月16日,https:
//docs.microsoft.com/zh-cn/dotnet/architecture/microservices/implement-resilient-applications/partial-failure-strategies
[8]纽曼,山姆。 构建微服务。 O'Reilly,2015年。
(本文翻译自Dung Le的文章《Scalable Web Architectures Concepts & Design》,参考:
https://medium.com/distributed-knowledge/scalable-web-architectures-concepts-design-6fd372ee4541)


