You've successfully subscribed to 完美的胖达
Great! Next, complete checkout for full access to 完美的胖达
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.
[译]如何走钢丝:掌握微服务架构中的四个决策

[译]如何走钢丝:掌握微服务架构中的四个决策

. 13 min read

原文:Walking the wire: Mastering the Four Decisions in Microservices Architecture
作者:Srinath Perera

胖达注:这篇文章发布于2016年3月10日,虽然将近三年了,但是方法论的东西并不会过时。

任何对此文感兴趣的读者请花费50美元/年或5美元/月的价格参加Medium会员计划,否则每月只能查看三篇文章。
一个月一餐盒饭钱,我觉得并不贵,你说呢?

以下为本人对文章的翻译。不对内容负责,也不对任何翻译错误负责,同时不包含任何图片内容,如需查看图片请付费查看原文链接,谢谢。


微服务是使用简单,轻量,松散耦合的服务构建系统的新架构风格,可以相互独立地开发和发布。

如果您不熟悉微服务,我建议您阅读Martin Fowler的帖子。如果您想将其与SOA进行比较,请观看Don Ferguson的演讲。此外,Martin Fowler的“ trade-off of microservices ”和“ when it is worth doing microservices ” 时,可以帮助您确定微服务何时有用。

假设你听过,了解并且相信微服务。微服务架构的追随者面临着一些实际挑战。本文讨论了处理其中一些挑战的问题。我将在其余讨论中使用MSA来表示微服务架构。

决定1:不共享数据库

此处有图

每个微服务都应该有自己的数据库,数据绝对不能通过同一个数据库共享。此规则消除了导致服务之间紧密耦合的常见原因。例如,如果两个服务共享同一个数据库,则第二个服务将在第一个服务更改了数据库Schema时离线。所以,在更改数据库之前,团队必须互相沟通,导致延迟,一切和不使用MSA时一样。

我认为这条规则很好,不应该被打破。

但是,有一个问题。当两个服务共享相同的数据(例如银行帐户数据,购物车)并且需要使用数据库事务来强制一致地更新数据时,我们经常共享数据库。

任何其他解决方案都很难。我们来探讨一下。

解决方案1:如果仅会在一个微服务中发生数据更新(例如贷款审批流程检查余额),则可以使用异步消息(消息队列)来共享数据。

此处有图

解决方案2:如果两个服务都会发生数据更新,请考虑合并这两个服务或使用事务。帖子 Microservices: It’s not (only) the size that matters, it’s (also) how you use them 描述了第一个选项。下一节将详细介绍事务。

处理更新的一致性

我们已经讨论过如何在单个服务更新数据时处理这种情况。让我们讨论当多个服务更新数据时如何对它们进行数据处理。我们在前面的部分讨论过一个例子。

通常,我们使用事务来解决此问题,这在分布式设置中使用时会造成系统缓慢,繁重且昂贵。但是,我们有时可以在不引入事务的情况下解决问题。

有几种选择。

选项1:将所有更新都放在同一个微服务中

如果可能,请避免跨越微服务边界的多个更新。然而,有时通过这样做,你最终会得到较少的服务或糟糕情况下的一个巨大的独石应用,又退回去了。因此,有时候,这是不可能的。

选项2:使用补偿和其他低保证措施

正如著名的帖子“ Starbucks Does Not Use Two-Phase Commit ”所描述的那样,普通世界没有事务。例如,星巴克的咖啡师不会等到您的交易完成。相反,他们同时处理多个客户,同时补偿任何发生的错误。如果你愿意做更多的工作,你也可以这样做。

一个关键点是,如果一个动作失败,你可以补偿。例如,如果您要运送这本书,首先扣除这笔钱,然后运送书籍,如果运费失败,则退还款项。

另一个简单的想法是给用户一个按钮,如果他可以判断它已经过时,就强行刷新页面。此外,有时最终的一致性或超时就足够了。你咬紧牙关,并接受较低的一致性(例如Vogel的帖子是一个很好的起点)。

最后,Life Beyond Distributed Transactions:Anpostate's Opinion 是对所有技巧的详细讨论。

话虽如此,一些用例需要事务以获得正确的结果。它们就必须使用交易。请参阅 Microservices and transactions-an update,权衡利弊并明智地选择。

决定2:微服务安全性

在微服务之前,服务通过在收到请求时调用数据库或身份服务器进行身份验证。

此处有图

在MSA中,我们可以用微服务替换身份服务器,在我看来,这会导致一个大的复杂的依赖关系。

相反,我喜欢“构建微服务”一书中描述的基于令牌的方法,并由下图描述。

此处有图

客户端与身份或SSO服务器通信,对自身进行身份验证,接收使用SAML或OpenIDConnect描述用户及其角色的签名令牌,并在每次请求时将令牌发送到微服务。每个微服务都验证令牌并根据令牌中描述的用户角色授权调用。此模型将身份验证推送到客户端,并在微服务上进行访问控制,同时简化依赖关系。例如,对于此模型,对于同一查询,具有角色“publisher”的用户可能会看到与具有角色“admin”的用户不同的结果,因为他们具有不同的权限。

值得注意的是,客户端可以获取令牌一次并重复使用它,每次session只会发生一次。因此,额外调用的开销很小。

How To Control User Identity Within Microservices?提供有关此方法的更多信息。

决定3:处理微服务组合

这里,“组合”意味着“如何将多个微服务连接到一个流程以提供最终用户的需求”。

大多数使用SOA的组合看起来如下。有一个运行工作流程的中央服务器。

此处有图

SOA组合使用集中式服务器(例如ESB或工作流引擎)。MSA不鼓励使用ESB(例如 Top 5 Anti-ESB Arguments for DevOps Teams)。另一方面,Do Good Microservices Architectures Spell the Death of the Enterprise Service Bus也提供反驳论据。

我不打算在这篇文章中深入ESB太多。但是,我想讨论是否需要一个中央服务器来进行微服务组合。有几种方法可以完成微服务组合。

方法1:从客户端驱动流程

下图显示了在没有中央服务器的情况下执行微服务的方法。客户端浏览器处理流程。帖子, Domain Service Aggregators: A Structured Approach to Microservice Composition,就是这种方法的一个例子。

此处有图

这种方法存在几个问题。

  1. 现在需要由客户端触发多个呼叫。因此,如果客户端位于慢速网络后面,这是最常见的情况,则执行速度很慢。
  2. 可能会在浏览器中运行某些逻辑时添加安全问题(我可以破解我的应用程序给我自己发布一笔贷款)
  3. 以上示例专注于网站,但大多数复杂的组合通常来自其他用例。因此,在客户端组合微服务对其他用例的普遍适用性尚待证明。
  4. 在哪里保持状态?可以信任客户端以保持工作流的状态吗?使用REST维护状态是可能的。但是,它很复杂。

方法2:协同

从中心位置指定工作流称为编制。但是,这不是协调多个合作伙伴开展某项工作的唯一方法。例如,在舞蹈中,没有一个人指导表演。相反,每个舞者跟随谁靠近就跟他同步。协同将相同的想法应用于业务流程。

典型的实现包括事件系统,其中该过程中的每个参与者收听不同的事件并执行他或她的部分。每个操作都会生成异步事件,从而在流中触发参与者。像RxJava或Node.js这样的环境是使用它的编程模型。

例如,假设贷款流程包括请求,信用检查,其他未偿还贷款检查,经理批准和决策通知。下图显示了如何使用编排实现此功能。请求被放置在队列中,由下一个节点接收,他将结果放入下一个队列,并且该过程一直持续到完成为止。

此处有图

协同就像一个舞蹈。两者都很复杂,需要练习才能正确执行。例如,程序员不知道进程何时完成,是否发生错误,或进程是否卡住。协同需要广泛的监控,来跟踪进度,通知错误或从中恢复。

另一方面,协同创建了松散耦合的系统,这是它的主要优点。例如,您可以在不更改其他节点的情况下向流程添加新节点。您可以在Scaling Microservices with an Event Stream中找到更多信息。

方法3:集中服务器

最后但最简单的选择是集中式服务器(即编制)。

SOA使用两种方法实现组合:ESB或业务流程。MSA提出了一个API网关(例如,Microservices: Decomposing Applications for Deployability and Scalability)。我猜API网关更轻量级,并使用REST / JSON等技术。但是,在纯粹的架构意义上,所有这些都使用中央服务器完成编制。

集中式服务器的另一种变体是“服务于前端的后端”(BEF),它为每个客户端类型构建服务器端API(一个用于桌面,一个用于iOS等)。此模型为每个客户端类型创建不同的API,并针对每个用例进行了优化。有关详细信息,请参阅模式:Backends For Frontends

我建议不要对这里的所有选项操心太多,直接从API网关开始,因为这是最简单的方法。您可以根据需要增加切换到更复杂的选项。

决定4:避免依赖地狱

MSA的目标是使服务可以独立发布和部署。要做到这一点,你必须避免依赖地狱。

让我们考虑使用API“A1”的微服务“A”并将升级到API“A2”。现在有两种情况。

  1. 微服务B可能会将用于A1的消息发送到A2。需要向后兼容性来支持这种情况。
  2. 微服务A可能必须回滚到A1,微服务C可能继续发送A2版本的消息到A1。

如果要独立发布微服务,则必须处理上述情况。否则,你所有的建立MSA的努力都被浪费了。

通常,处理这些情况的办法是添加可选参数而永不重命名或删除现有参数。然而,更复杂的情况是可能的。

帖子 “Taming Dependency Hell” within Microservices with Michael Bryzek 详细讨论了这一点。Ask HN: How do you version control your microservices? 也是另一个很好的来源。

最后,应该通过时间限制向后和向前兼容性支持,以避免并发症。例如,您可以有一条规则,即微服务不应该依赖超过三个月的API。这将让微服务开发人员最终放弃支持旧版本的一些代码分支。

最后,我想谈谈你的依赖图在微服务架构中应该是什么样子。

一种选择是在需要时自由调用其他微服务。这将创建一个前ESB时代的意大利面建筑。我不是那种模型的粉丝。

另一个极端是说微服务不应该调用其他微服务,所有连接都应该通过API网关或消息总线完成。这将导致一个树形结构。例如,不是微服务A调用B,而是将微服务A的结果带到网关,网关再根据结果调用B. 这是编制模型。现在,大多数业务逻辑都将存在于网关中。是的,这使网关变重了。

我的建议是要么选编制模型,要么辛苦的实现协同。是的,不要选意大利面模式。

结论

微服务的目标是松耦合。精心设计的微服务架构允许您使用一组微服务实现项目,其中每个微服务都是独立管理,开发和发布的。

当您使用微服务进行设计时,您必须密切关注关键,即“松耦合”。有很多挑战,这篇文章回答了以下问题。

  1. 如何处理需要在两个微服务之间共享数据的场景?
  2. 如何在保持松散耦合的同时迭代微服务API?
  3. 如何处理安全问题?
  4. 如何协同微服务?

谢谢!欢迎评论。

如果您喜欢这篇文章,您可能还会发现以下内容有用。