博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
项目中最困难的部分_微服务最难的部分是什么? 您的资料
阅读量:2528 次
发布时间:2019-05-11

本文共 7351 字,大约阅读时间需要 24 分钟。

项目中最困难的部分

在本文中,我将探讨创建和开发时最棘手的问题:您的数据。 使用 / / 并不意味着您在做微服务。 仔细查看您的域和数据将有助于您使用微服务。 (有关更多背景信息,请阅读我有关微服务实现的博客系列: 微服务 ;以及 。)

在我们尝试微服务架构的原因中,首要原因是使您的团队能够以不同的速度在系统的不同部分上工作,而对整个团队的影响却最小。 我们希望团队能够自治,能够就如何最好地实施和运营服务做出决策,并能够自由地根据业务需求进行更改。 如果我们有组织的团队来做到这一点,那么我们系统体系结构中的反思将开始演变为看起来像微服务的事物。

要获得这种自主权,我们需要摆脱依赖关系 ,但这说起来容易做起来难。 我已经看到人们在某种程度上简单地提到了这个想法,因为“每个微服务都应该拥有并控制自己的数据库,并且没有两个服务应该共享一个数据库。” 这个想法很合理:不要跨服务共享单个数据库,因为那样会遇到冲突,例如竞争的读/写模式,数据模型冲突和协调挑战。 但是,一个数据库确实为我们提供了许多安全性和便利性,包括ACID事务,一个单一的查找位置,一个众所周知的(有点?),一个管理的位置等等。

在构建微服务时,我们如何通过将数据库分为多个较小的数据库来协调这些安全性?

首先,对于构建微服务的企业,我们需要明确以下几点:

  • 什么是域? 什么是现实?
  • 交易界限在哪里?
  • 微服务应如何跨边界通信?
  • 如果我们仅将数据库内翻怎么办?

什么是域

这似乎在很多地方都被忽略了,但是,这是互联网公司如何实践微服务与传统企业可能(或者由于忽略这一点而失败)实现微服务之间的巨大差异。

在构建微服务并为其使用的数据(生产/消费等)进行推理之前,我们必须对数据代表什么有一个相当好的了解。 例如,在我们可以将有关 “预订”及其信息存储到数据库之前,我们需要了解什么是预订 。 与您的域一样,您可能需要了解什么是帐户雇员索赔等。

为此,我们需要深入研究实际情况是什么 ? 例如:什么是书? 考虑一下,因为这是一个非常简单的示例。 我们将如何在数据模型中表达这一点?

一本书有页吗? 报纸是书吗? (有书。)也许一本书有精装书? 还是不是每天发布/发布的东西? 如果我写了一本书(我做过 ),那么出版商可能会为我输入一个条目,其中一行代表我的书。 但是一家书店可能有我的五本书。 每个都一本书吗? 还是他们复制? 我们将如何表示呢? 如果一本书这么长必须将其分解成册怎么办? 每本书都是一本书吗? 还是它们全部结合在一起? 如果许多小的成分组合在一起怎么办? 组合是书吗? 还是每个人一个? 基本上,我可以出版一本书,在书店里有很多副本,每本书都有多个卷。

那么,什么是书?

现实是没有现实。 关于什么是一本关于现实的书没有客观的定义,因此要回答这样的问题,我们必须知道:谁在问这个问题,什么是背景?

我们人类可以快速-甚至在不知不觉中-解决这种理解的模棱两可,因为我们在头脑,环境和问题中都有一个背景。 但是计算机没有。 在构建软件和对数据建模时,我们需要使此上下文明确。 用书来说明这一点很简单。 您的域(企业)及其帐户,客户,预订,声明等,将变得更加复杂,而且冲突/模棱两可。 我们需要界限。

我们在哪里划定界限? 帮助我们应对了域中的这种复杂性。 我们围绕我们的域建模的绘制有 。 换一种说法:我们建立并完善了代表我们领域的模型,并且该模型包含在定义我们的上下文的边界内。 这是明确的。 这些边界最终是我们的微服务,或者边界内的组件最终是微服务,或两者。 无论如何,微服务都是关于边界的,DDD也是如此。

f1_posta_500.png

我们的数据模型(我们希望如何表示物理数据存储中的概念(请注意此处的明显区别))是由我们的领域模型驱动的,而不是相反的。 当我们有这个边界时,我们知道(并且可以断言)模型中的正确和错误。 这些界限也意味着一定程度的自主权。 有界上下文A与有界上下文B可能对书本的理解有所不同。 例如,有界上下文A可能是一个搜索服务,用于搜索标题,其中单个标题是一本书 ; 可能有界的上下文B是一种结帐服务,它根据您要购买的图书(书名和份数)等来处理交易。

您可能会停下来说:“等一下。Netflix没有说任何有关域驱动设计的信息,Twitter也没有说LinkedIn的信息。为什么我要听有关DDD的信息?”

原因如下:

“人们试图复制Netflix,但他们只能复制所见内容。他们复制结果,而不是过程。” 前Netflix首席云架构师Adrian Cockcroft

通往微服务的旅程就是这样:一段旅程。 每个公司的情况会有所不同 。 没有硬性规定,只有权衡取舍。 复制对某家公司有效的内容,因为它似乎在该实例中有效,这是企图跳过流程/旅程,并且将不起作用。 您的企业不是 Netflix。 实际上,我想说的是,尽管网域在Netflix上多么复杂,但它并没有在您的旧企业中那么复杂。 搜索和显示电影,发布推文,更新LinkedIn个人资料等,都比保险理赔处理系统简单得多。 由于上市速度和庞大的规模/规模,这些互联网公司开始使用微服务。 (在Twitter上发布推文很简单。为5亿用户发布推文并显示推文流非常复杂。)

企业必须在领域和规模上面对复杂性。 因此,请接受这是平衡领域,规模和的旅程 。 每个组织的旅程将有所不同。

交易边界是什么?

回到故事。 我们需要诸如域驱动设计之类的东西,以帮助我们了解用于实现系统的模型,并在上下文中围绕这些模型绘制边界。 我们接受客户,帐户,预订等对于不同的受限上下文可能具有不同的含义。 我们可能最终将这些相关概念分布在我们的体系结构中,但是当变化发生时,我们需要一种方法来协调这些不同模型之间的变化。 我们需要考虑这一点,但是首先,我们必须确定我们的交易边界。

不幸的是,开发人员似乎仍然错误地构建分布式系统:我们通过一个单一的关系式数据库进行研究。 我们还忽略了异步,不可靠网络的危险。 总而言之,我们会做一些事情,例如编写精美的框架,使我们不必了解网络的任何知识(包括RPC框架,也忽略网络的数据库抽象),并尝试通过点对点同步调用(REST, SOAP,其他类似CORBA的对象序列化RPC库等)。 我们构建的系统不考虑 ,最终尝试通过诸如跨许多独立服务的两阶段提交之类的事情来解决分布式数据问题。 或者我们完全忽略了这些担忧。 这种心态导致构建无法扩展的脆弱系统,而将其称为SOA,微服务,微型服务或其他东西也没关系。

交易边界是什么意思? 我的意思是相对于业务不变量,您需要的最小原子单位。 究竟使用数据库的ACID属性实现原子性还是两阶段提交都没有关系。 关键是要使这些事务边界尽可能小(理想情况下是单个对象上的单个事务),以便我们进行扩展。 (Vernon Vaughn 了 。)当我们使用DDD术语构建域模型时,我们将识别实体值对象聚合 。 在这种情况下,聚合是封装其他实体/值对象并负责执行不变量的对象。 在有限的上下文中可以有多个聚合。

例如,假设我们有以下用例:

  • 允许客户搜索航班,
  • 允许客户选择特定航班的座位,
  • 并允许客户预订航班。

在这里,我们可能会有三个有界上下文:搜索,预订和票务。 (我们还有其他一些功能,例如付款,忠诚度,待机,升级等,但让我们集中讨论三个)。 搜索负责显示给定时间范围(天,时间等)内特定路线和路线的航班。 预订将负责准备包含客户信息(姓名,地址,常旅客号码等),座位偏好和付款信息的预订过程。 出票将负责与航空公司达成预定并发行机票。 在每个有界上下文中,我们要确定可以在其中强制执行约束/不变式的事务边界。 我们不会考虑跨边界上下文的原子事务,我将在下一节中讨论。

考虑到我们想要小的交易范围(预订机票的简化版本),我们将如何对此建模? 也许某个航班集合汇总了诸如时间,日期,航线和诸如客户,飞机和预订之类的实体的值? 这似乎是有道理的-航班有飞机,座位,客户和预订。 飞行集合负责跟踪飞机,座位等,以用于创建预订。 从数据库内部的数据模型的角度(具有约束和外键的精细关系模型等)的角度来看,这可能是有道理的,或者在我们的源代码中建立一个不错的对象模型(继承/组合),但是让我们看看会发生什么。

f2_posta_500.png

只是为了创建预订,所有预订,飞机,航班等中是否真的存在不变性? 也就是说,如果我们在航班集合中添加一架新飞机,那么我们是否真的应该在该交易中包括客户和预订? 可能不是。 我们这里的汇总是考虑到结构和数据模型的便利性而建立的; 但是,交易边界太大。 如果我们对航班,座位,预订等进行了很多更改,则将发生很多事务冲突(无论使用乐观还是悲观锁定都无关紧要)。 而且这显然无法扩展-不必介意一直在失败的订单,只是因为航班时刻表的变化是一种糟糕的客户体验。

如果我们打破交易界限小一点怎么办。

也许预订,座位可用性和航班是他们自己的独立汇总。 预订包含客户信息,偏好以及付款信息。 座位可用性集合封装了飞机和飞机配置。 航班总计由时间表,航线等组成,但是我们可以继续进行预订,而不会影响航班时间表和飞机/座位可用性的交易。 从域的角度来看,我们希望能够做到这一点。 我们不需要在飞机/航班/预订之间实现100%严格的一致性,但是我们确实想正确地记录作为管理员的航班时间表更改,作为供应商的飞机配置以及来自客户的预订。 那么,我们如何在航班上实施诸如“选择特定座位”之类的事情呢?

在预订过程中,我们可能会调用座位可用性总量,并要求其在飞机上预订座位。 该座位预订将实现为单个事务,例如,(保持座位23A)并返回预订ID。 我们可以将此预订ID与预订相关联,并在知道座位已“预订”时提交预订。 其中的每个(保留座位并接受预订)都是单独的交易,可以独立进行而无需任何两阶段提交或两阶段锁定。

请注意,在这里使用预订是一项业务要求。 我们不在这里分配座位; 我们只保留座位。 可能需要通过模型的迭代来限制此要求,因为起初用例的语言可能只是说“允许客户选择座位”。 开发人员可以尝试推断该需求意味着“从剩余席位中拣选,将其分配给客户,将其从库存中删除,并且售出的门票不超过席位。” 这将是多余的,不必要的不​​变式,这将给我们的事务处理模型增加额外的负担,而业务实际上并没有将其视为不变式。 在没有完成座位分配甚至超额销售机票的情况下,预订业务当然还可以。

f3_posta_500.png

这是一个示例,它允许真正的域引导您为所涉及的各个集合提供较小,简化但完全原子的交易边界。 尽管故事还不能到此结束,因为我们现在必须纠正一个事实,即所有这些单独的交易都需要在某个时候整合在一起。 涉及数据的不同部分(即,我创建了预订和座位预订,但是这些与获取登机证/机票等不是结算交易)。

微服务应如何跨边界通信?

我们希望保持真实的业务不变性不变。 使用DDD,我们可以选择将这些不变量建模为集合,并使用单个事务对集合进行强制。 在某些情况下,我们可能会在单个事务中(跨单个数据库或多个数据库)更新多个聚合,但这些情况除外。 我们仍然需要在聚合之间(最终在有限的上下文之间)保持某种形式的一致性,那么我们应该如何做呢?

我们应该了解的一件事:分布式系统是挑剔的。 我们在有限的时间中的 -事情会失败,不确定性变慢或出现故障,系统具有不同步的时间边界等,因此为什么要尝试打吗? 如果我们接受这一点并将其纳入我们整个领域的一致性模型中怎么办? 如果我们说在必要的交易边界之间,我们可以与数据和域的其他部分一起使用,以便在以后的某个时间点进行协调并保持一致?

对于微服务,我们重视自主性。 我们重视能够独立于其他系统进行更改(在可用性,协议,格式等方面)。 时间的分离和任何有限时间内服务之间任何事物的保证都使我们能够实现这种自治( )。 所以我说,在事务边界之间和有界上下文之间,使用事件来传达一致性。 事件是不可变的结构,捕获了应该向对等方广播的有趣的时间点。 对等方将听取他们感兴趣的事件,并根据该数据做出决策,存储该数据,存储该数据的某些派生,根据对该数据做出的决策更新自己的数据,等等。

继续我开始的航班预订示例:通过ACID风格的交易存储预订时,我们如何最终对机票进行票务处理? 这就是前面提到的受票证限制的上下文进入的地方。受票证限制的上下文将发布类似NewBookingCreated的事件,而受票证限制的上下文将使用该事件并继续与后端(可能是遗留的)票证系统进行交互。 这需要某种类型的集成和数据转换,而 。

我们如何写数据库原子地发布到队列/消息传递设备? 如果我们在两次活动之间有订购要求/因果要求怎么办? 每种服务一个数据库呢?

理想情况下,我们的聚合将直接使用命令和 (作为一等公民),也就是说,任何操作均作为命令实现,而任何响应均作为对事件的响应实现),并且我们可以更清晰地在内部使用的事件之间进行映射到我们的有限上下文以及我们在上下文之间使用的上下文。 我们可以将事件(例如NewBookingCreated)发布到消息队列中,然后让侦听器从队列中使用该事件,然后将其幂等地插入数据库中,而不必使用XA / 2PC事务,而不是自己插入数据库中。 我们可以将事件插入到专用 ,该作用类似于数据库和消息发布-订阅主题(这可能是首选途径)。 或者,我们可以继续使用ACID数据库,并将对该数据库的更改流式传输到一个持久的复制日志中,例如使用类的并使用某种事件处理器/蒸汽处理器来推断事件。 无论哪种方式,我们都希望在具有不变的时间点事件的边界之间进行通信。

f4_posta_500.png

这具有很大的优势:

  • 我们避免跨边界的昂贵的,可能不可能的交易模型。
  • 我们可以对系统进行更改,而不会妨碍系统其他部分的进度(定时和可用性)。
  • 我们可以决定要多快或慢一点,以了解外部世界并最终保持一致。
  • 我们可以将数据存储在我们自己的数据库中,但是我们想使用适合我们服务的技术。
  • 我们可以随意更改模式/数据库。
  • 我们变得更具可伸缩性,容错性和灵活性。
  • 您必须更加关注CAP定理和为实现存储/队列而选择的技术。

值得注意的是,这有缺点:

  • 更复杂。
  • 调试很困难。
  • 因为看到事件时会有延迟,所以我们无法对其他系统知道什么做任何假设(无论如何我们都无法做,但是在此模型中更明显)。
  • 操作化更加困难。
  • 您必须更加关注CAP定理和为实现存储/队列而选择的技术。

我在两栏中都列出了“关注CAP等”,因为尽管这给我们带来了更多负担,但无论如何必须这样做。 同样,注意分布式数据系统中数据一致性和并发性的不同形式也很重要。 不再接受“我们的ACID中的数据库”(尤其是当该ACID数据库很可能仍默认为弱一致性时)。

这种方法中出现的另一个有趣的概念是实现称为命令查询分离责任的模式的能力,该模式中,我们将读取模型和写入模型分离到单独的服务中。 请记住,我们感叹互联网公司没有复杂的域模型。 这在他们的写模型很简单的过程中就很明显(例如, )。 但是,由于它们的规模,它们的读取模型非常复杂。 CQRS有助于分离这些问题。 另一方面,在企业中,写入模型可能非常复杂,而读取模型可能是简单的平面选择查询和平面DTO对象。 CQRS是一种功能强大的关注点分离模式,可以在您拥有适当的边界后进行评估,并且是一种在聚合之间以及有界上下文之间传播数据更改的好方法。

对于只有一个数据库且不与其他任何服务共享的服务呢? 在这种情况下,我们可能有侦听器订阅事件流,并且可能将数据插入到最终聚集可能使用的共享数据库中。 这个“共享数据库”非常好。 请记住,没有规则,只有权衡取舍。 在这种情况下,我们可能有多个服务与同一个数据库协同工作,并且只要我们(我们的团队)拥有所有流程,我们就不会否定自治的任何优势。 因此,当您听到有人说:“微服务应该拥有自己的数据库,而不要与其他人共享”时,您可以回答:“好吧。”

如果我们仅将数据库内翻怎么办?

如果我们将上一节中的概念推向逻辑极限怎么办? 如果我们只是说我们将对事件使用所有事件/流, 永久保留这些事件怎么办? 如果我们说数据库/缓存/索引实际上只是过去发生的事件的持久日志/流的物化视图,而当前状态是所有这些事件的左折,那该怎么办?

这种方法带来了更多好处,您可以在通过事件进行通信的好处(上面列出)中添加这些好处:

  • 现在,您可以将数据库视为记录的“当前状态”,而不是真实的记录。
  • 您可以引入新的应用程序,并在会发生什么事情方面重读往事,并检查他们的行为。
  • 您可以免费完善审核日志记录。
  • 您可以引入应用程序的新版本,并通过重播事件对其进行详尽的测试。
  • 通过将事件重播到新数据库中,您可以更轻松地推断数据库版本/升级/架构更改。
  • 您可以迁移到全新的数据库技术(例如,也许您发现关系数据库已不复存在,并且您想要切换到专门的数据库/索引)。

有关此的更多信息,请阅读Martin Kleppmann的 。

f5_posta_500.png

当您在aa.com,delta.com或united.com上预订机票时,您会发现其中一些概念正在发挥作用。 当您选择一个座位时,您实际上并没有得到分配,而是要保留它。 预订机票时,实际上并没有机票-稍后会收到一封电子邮件,告诉您您已被确认/已预订机票。 您是否曾经更改过预订并为航班分配了其他座位? 还是去登机口听到他们因为航班超卖而要求志愿者放弃座位? 这些是事务边界,最终一致性,补偿事务甚至工作中的道歉的示例。

结论

数据,数据集成,数据边界,企业使用模式,分布式系统理论,时间安排等都是微服务的硬部分(因为微服务实际上只是分布式系统 )。 我对技术感到太多困惑( “如果我使用Spring Boot,我正在做微服务我需要解决服务发现, 在进行微 服务之前在云中进行负载平衡”“每个微服务我必须有一个数据库 ”)以及有关微服务的无用规则。

不用担心 一旦大型供应商向您出售了所有精美的产品套件,您将仍然需要做我在本文中概述的困难部分。

翻译自:

项目中最困难的部分

转载地址:http://yzczd.baihongyu.com/

你可能感兴趣的文章
ubuntu系统下创建软件桌面快捷方式
查看>>
ES6学习笔记二
查看>>
zipHelper
查看>>
SAP HANA Delivery Unit概念简述
查看>>
Vi/VIM键盘图, Vi/vim学习图
查看>>
java面试题
查看>>
JVM概念
查看>>
数据结构:二叉树
查看>>
OpenCV2:硕士篇 图像分割技术-分水岭分割
查看>>
【算法】【JAVA】栈的实现
查看>>
Git总结
查看>>
canvas个人总结
查看>>
spring-aop-1
查看>>
TCP通信(上传文件)
查看>>
android Activity初次的启动的时候播放声音
查看>>
redis desktop manager连接远程linux中的Redis
查看>>
POJ刷题计划
查看>>
css3 背景透明
查看>>
Android适配API23之后权限的动态申请
查看>>
[operator]ELK6 index pattern的问题
查看>>