分部式架构
- 275 次浏览
【分布式】资源与事务:可观测性的基本二重性
西格曼:我叫本·西格曼。我是Lightstep的联合创始人兼首席执行官。我在这里讨论的是资源和事务,这是可观察性的一个基本的二元性。我职业生涯的大部分时间都在研究可观察性。在我职业生涯之初,我在谷歌工作了九年,致力于谷歌的分布式跟踪系统Dapper,以及他们的高可用性监控和度量系统Monar。然后,Lightstep当然也专注于可观察性。我花了很长时间才到这里。我想出了一种与过去不同的思考可观察性的方法,这就是这次演讲的内容。
事务
什么是事务?在右边,您可以看到某个系统的示意图。我们将从这个银行账户服务的角度来讨论一些事情,它处于一些更大的微服务架构,一些云原生架构中。本演示文稿中的事务不一定像银行事务。它只是指从系统的一部分传播到另一部分的任何请求,而不是整个请求。这是它所做的所有工作,直到它回来并完成它试图完成的一切。事务是应用程序中实际上“为最终用户做点什么”的东西,不管最终用户是人,或者在某些情况下,如果是Twilio,或者类似的东西。也许Twilio的最终用户实际上只是另一个运行脚本的程序。事务为用户或客户提供价值。
现在,特别是对于原生云,事务是分布式的。它们不一定非得如此,但通常是这样。它们可以用许多不同的粒度来描述。我的意思是,同一个事务可以用非常粗的粒度来描述,就像整个最终用户工作流一样。比如,如果你是一个像Lyft、Uber之类的乘车共享应用程序,那么整个请求乘车的流程可能会被视为一个事务。这是您仅有的粒度级别。您可能希望更细粒度地考虑服务之间的单个请求,比如HTTP请求。您可能会认为这是您想要用来描述事物的粒度,或者您想要更详细地了解一些甚至所有本地函数调用。然后我假设,从理论上讲,您可以根据整个系统中为完成一个事务而发生的每个CPU指令来查看一个事务。这些都是建模事务的有效方法。当然,当我们记下这个列表时,在这个粒度级别记录东西的成本会越来越高。事实上,它可能会变得如此之高,以至于会产生大量的开销,并开始影响事务。理论上,这些是不同的粒度。用于描述事务的遥测通常是跟踪和结构化日志。结构化日志类似于文本日志语句,但具有明确的键值属性。这些事情在这里有说明。您可以看到银行帐户请求有一个请求大小属性、一些HTTP路径、状态代码、延迟等等。这些是此模型中事务片段的理论属性。
我还认为,跟踪最终将取代日志记录。这需要时间,但对于事务来说,跟踪将取代日志记录。现在,我将通过向您展示跟踪模型比简单的单进程日志记录灵活得多来激发这一点。这里我不谈论2021,但这更像是放大了可观测性。这是一个日志记录语句。第22行有一些伪代码。这些日志语句中的每一条都定义了自己的表。这里的结构定义了一系列键,请求大小、路径、状态、延迟都在这里反映出来。这些列将成为此表的列。然后是从本地状态或其他地方提取的值。这组值将成为表中的行。
跟踪只是跨事务的连接
为了阐明这一点并使之清楚,您有这个日志语句,因为生产中运行的代码填充了这个理论表。当然,我并不是建议我们将这些数据全部插入MySQL或类似的东西,甚至不一定要将其插入到Elastic、Splunk或类似的东西中。只是有一个由日志语句本身描述的理论表,您可以这样建模。在某些情况下,您可以使用工具对这些表运行查询。跟踪的酷之处在于,这些日志记录系统执行灵活连接非常困难或不可能,或者非常昂贵或不可能。分布式跟踪在整个系统中进行连接。同样,如果这是您的系统架构,我们将要做的是实现所有结构化事件的连接,无论您将它们称为日志还是跟踪范围,这其实并不重要。您仍在描述事务。我们将使用跟踪上下文将所有这些服务中的结构化事件连接到一个更大的表中。其中有一个表,其中包含来自这些不同服务的列,在这里用颜色编码,服务A、B和D也在其中连接。然后,每个分布式事务表示该表中的一行。
这是非常强大的,因为如果您能够在这个概念模型中思考问题,就可以运行各种分析来找出跨服务边界的列之间的相关性。这反过来允许您了解一个服务中的行为如何影响另一个服务中的某些行为。具体来说,您可能会在服务a中的堆栈顶部有一个customer ID字段,并且您可能会发现,当银行帐户服务的延迟较高时,某些客户参与的时间比例较高。然后这就给了你一些东西,比如,那个客户是如何改变他们的工作量的,或者你做了什么?这些类型的连接实际上是理解跨服务边界的因果关系的一种非常重要的机制。如果您一直想知道分布式跟踪的所有麻烦是什么,那么在这个模型中,分布式跟踪实际上是结构化日志语句的一个连接。然后是一系列语义和查询功能。这就是事务。
资源是什么?
接下来,我们将讨论资源。什么是资源?资源是事务为完成其工作而消耗的东西。这个定义的一个副作用是,根据定义,资源是有限的。你的亚马逊账单是一种资源。同样,许多不同的颗粒。通过Kafka主题的吞吐量,Kafka集群只能支持这么多负载。当你到了负载的末尾,并且你已经消耗了所有的负载,事情会很快变得非常糟糕。你最终会遇到很多推回,很高的延迟,请求被丢弃,诸如此类的事情。类似地,CPU使用率也完全正常,直到不正常为止。如果您使服务中的CPU饱和,所有您认为理所当然的事情都会中断。更糟糕的是,进程的内存使用率直接崩溃。例如,您还可以非常精细地讨论单个互斥锁。如果你在一个锁上有很多争用,你最终会得到一个读锁,这个读锁应该是180纳秒,如果一个锁有很多争用,可能需要一百万倍的时间。这也会带来问题。这些都是资源类型。资源之所以成为一种资源,是因为它们能够跨事务生存,并且能够跨事务共享。共享资源是非常重要的,因为如果你不共享资源,你的系统将非常昂贵。在多租户、多请求环境中运行的全部好处在于,您可以更好地利用资源,并在事务中共享资源。这就是资源。
为了让它更直观一点,我为一个微服务、一个Kafka集群和一个互斥锁绘制了这些框。这完全是说明问题的。我相信有更好的方法来衡量这些东西的健康状况。对于一种资源,您要考虑的是该资源在某种程度上的剩余量。它是资源消耗量的指标。您可以看到CPU使用率会急剧上升,RAM使用率会急剧上升。您可以看到,使用者延迟或生产者延迟是Kafka集群运行状况的指标,或者您必须等待获取互斥锁的时间是互斥锁运行状况的指标。任何资源都有一些健康指标。我想在这里强调的是,这些都不是单个事务成功或失败的指标。当然,当CPU和内存使用率达到峰值时,事务会出现问题。这意味着表明资源的健康状况。我会谈谈为什么这是相关的。然后资源也有一堆标签。这其实很重要。
在我看来,这些标签或属性的用途是多方面的。当然,你只是想理解和分解。如果您看到这样的峰值,您可能希望按区域分组,或按集群ID分组,或类似的方式。那很好。您应该能够在时间序列数据库中执行此操作。更重要的是,这些标签是沟通资源和事务的通用语言。理想情况下,当事务跨越资源时,该事务会以某种方式对该资源进行注释。它可以作为从事务数据连接到资源数据的一种方式,反之亦然。这是一个非常强大的东西。稍后,当我们进入一个实际的例子时,我将讨论这个问题。
资源也是一个层次结构
我说过有不同的粒度,也有层次结构。这对于事务来说是正确的,但我认为更重要的是在这里强调这一点。您可能有一个Kafka集群,它本身有许多微服务。在这些虚拟机中,有一堆虚拟机。在这些锁中,有一堆互斥锁。这些东西也会上下波动。在资源环境中有层次结构,以及这些健康指标。
相互依存
我们已经谈过事务了。它们是客户真正关心的工作。我们已经讨论过资源,它们是使事务做一些事情并在事务之间共享的东西。这些是相互依存的。下面是这些资源的图表。这些绿色的曲线旨在说明流入或流出这些资源并执行其工作的事务。您可以看到,在本例中,事务将转到不同的HTTP端点。在这种情况下,我们将讨论不同的Kafka主题。在本例中,读卡器和写卡器试图对互斥锁执行锁定。有不同类型的事务,我们希望当事务跨越资源时,使用该资源的标识符对其进行注释。如果这个主题是Y请求,那么根据不同粒度级别的模式进行事务,如果您想要了解资源和事务是如何交互的,那么对于使用Kafka实例状态的区域和集群ID对事务进行注释是非常有价值的。或者,对于这个端点,对于要在跟踪中用诸如主机名或容器ID、服务名称、版本之类的东西注释的事务。同样,您可以使用资源中的标记清空事务,并充当这两个世界之间的映射。这就是一个例子。绿色的东西基本上是痕迹。然后在资源中,您通常使用度量遥测、时间序列统计来表示这些资源的运行状况。不总是,但通常是。
资源和事务是完全、完全相互依赖的。这是一个非常重要的问题。也就是说,如果您的资源不健康,那么事务将受到很大影响。如果事务数量过多,资源就会受到很大影响。事实上,作为一个主题,持续集成最让我感到困扰的一点是,在不知道工作负载的情况下,代码甚至可以是正确的或不正确的。我认为那完全是海市蜃楼。我觉得CI很棒。当然,我们在Lightstep中使用CI。至少对于系统软件或后端软件来说,您可以知道代码是否正确的唯一方法是在实际工作负载下运行代码。因为工作负载实际上是语义的一部分,所有关于资源如何配置,甚至资源如何设计和实现的调优都在很大程度上取决于事务的实际工作负载。这不仅仅是你可能需要更多的东西,而是你可能真的想要一个不同的东西。我不喜欢人们太讨厌MySQL。我之前有点讨厌某些类型的数据,但是如果你的数据可以很容易地放在一台机器上,而且你只需要关系语义,那就太完美了。除了一些复制问题之外,它其实没有什么错。类似地,如果你想要一个真正的行星规模和便宜的东西,你必须离开这个模型,做一些其他的事情。从设计的角度来看,或者从代码的角度来看,直到您考虑事务性工作负载时,资源也不能被认为是正确的或不正确的。可观察性是理解工作负载如何影响资源健康的最简单方法之一,反之亦然。
说到相互依赖,应用程序的客户只关心事务。我的意思是,如果你有一次停电,有人试图写一份报告,特别是为一个非技术性的最终用户写的,他们真的一点也不关心你的资源耗尽这一事实。这不是他们的问题。这是你的问题,不是他们的问题。他们只关心自己的事务是否正确,并在合理的时间内回来。正确性,也意味着这不是一个错误。正确性和延迟是客户或最终用户关心的两件事,而不是其他。如何做到这一点取决于你自己。对于运营者(operator)来说,你唯一能控制的就是你的资源。按运营者,包括DevOps、SRE。软件的全部意义在于,我们不是坐在那里从客户那里获取事实,然后填写一些东西,然后作为一个人做一些工作。我们正在编写自动化软件。那个软件是靠资源运行的。
总体而言,运营者主要关注资源,因为这实际上是他们拥有的控制点。最终用户只关心事务。最终用户和运营者也以这种方式相互依赖。如果最终用户改变行为太快,可能会给运营者带来资源问题。显然,如果运营者最终做了一些损害资源健康的事情,那么最终用户就会受到影响。最终用户、运营者、事务、资源都以这种方式相互依赖。他们之间有这种关系。最终用户和开发人员,或者开发人员或运营者也完全相互依赖。我认为这是一件非常有趣的事情,对我来说,无论如何,这是一件深刻的事情。最终用户和事务、运营者和资源,这是他们倾向于思考的,因为这是他们可以控制和处理的。它们实际上是在工作负载本身中相交的完全不同的平面。
DevOps工程师/SRE要做什么?
我们到底在做什么?这听起来像个问题。有必要能够在这两个方面之间进行转换,跨越遥测类型、度量和跟踪,通过标记进行聚合,并自动进行转换。这是一种方法,您可以通过资源和轴心来了解资源的稀缺性或健康问题,以了解事务是如何变化的。或者,您可以从事务非常慢或返回错误开始,找出与此相关的资源,因为高延迟几乎总是处于争用状态的资源,无论是您的数据库,还是Kafka队列,或者其他什么。然后,要想理解为什么会出现这种情况,就要弄清楚一些工作负载是如何变化的,比如有人推出了新代码,使数据库调用的数量增加了100倍,这就是为什么我们的数据库变得缓慢。这是一件有趣的事。您经常会在事务处理速度缓慢、找到处于争用状态的资源,然后意识到有人将负载增加了100倍的情况下切换。同样,从事务到资源再到资源,或者从事务到资源再到资源。这真的很难,因为现在人们在前端集成度量和跟踪,但实际上需要在数据层集成它们才能实现这一点。这是一个非常困难的数据工程问题,因为数据实际上看起来非常不同。度量通常表示为时间序列统计数据,跟踪通常表示为结构化事件,不管它是否跨越日志,不管怎样,它基本上是一组结构化事件数据,而不是统计数据。很难在它们之间转换。标签就是我们这样做的方式。
最后,我做这件事已经15年了,我一直在想这件事。没有人应该是这方面的专家才能使用它。它需要某种直观的东西,并在日常工作流程中为人们带来这些见解。如果我们不能做到这一点,那么我们基本上没有成功地解决相互依赖问题。
服务级别目标(SLOs)
SLOs是一个有用的工具。我只想在资源和事务的上下文中简要介绍一下SLO。SLO是一个热门话题。我认为SLO就是目标。它们是关于一组范围限定为一组资源的事务的目标。资源和事务是思考SLO的一种非常好的方式。我认为“服务水平目标”一词,人们通常认为,服务意味着微服务。不是真的。如果你像我一样老了,你拿起电话,听到拨号音,服务水平是99.99%的时间都是这样,或者别的什么。它不一定是一个微型服务。服务级别只是说,事务有多可靠?这是否意味着他们不会经常出错,或者他们很快,或者你做了什么。您希望在某组资源的上下文中检查该服务级别。这实际上就是微服务的用武之地。你也可以为其他事情设置SLO,同样,Kafka队列,数据库,诸如此类的东西。通过这种方式,SLO可以表示这两种双重性之间的契约,一方是事务和资源,另一方是运营者和最终用户。我认为这是一种优雅的方式来思考为什么SLO在连接这两个不同世界方面如此重要和重要。
这在实践中是什么样子的?
我知道我到目前为止所说的都是理论上的。我决定用这些术语展示一个生产系统中真实事件的工作示例。它确实显示了一些产品截图的东西,但这不是演示或类似的东西。这只是为了帮助从概念上说明这在实践中是如何发挥作用的,特别是在Kafka停机期间。
下面是仪表板中的一张图表,显示消费者对Kafka队列的延迟,实际上是在Lightstep自己的内部系统中。这不像是一次需要发生事故或对客户有明显影响的停机,但这肯定是人们争先恐后地想弄清楚到底发生了什么。你可以看到,10点45分,一切都很好。然后他们变得不好了。经典的情况是,Kafka本身就是一个分布式系统。它是一种资源,显然出了问题,因为作为消费者,它做任何事情都变得非常缓慢。您必须等待很长时间才能从Kafka队列中获取任何数据。你想做什么?我认为一种选择是尝试分组,并以各种方式对其进行过滤。我们真正想做的就是说,这个资源发生了什么变化?这个资源有很多事务要处理,很多事务实际上就在这里。另外,一笔事务就在这里进行。本期与本期之间的事务发生了哪些变化?这就是我作为一个操作员想要知道的,因为这可能会给我一个线索,因为Kafka的代码没有改变。工作量发生了变化。怎样你应该可以点击这个东西。在这里,您可以看到查询。
您应该能够单击此更改,然后说,是什么导致了此更改?然后进入一个UI,我们说,好吧,这里是队列不满意的异常情况。这是它正常工作的基线。再次,我们只是放大看看这两个时期是什么样子。那么我们真正想做的是,从统计学上理解,这里的事务和那里的事务有什么不同?我们试图理解的不仅仅是Kafka不开心,还有这里和这里的工作量有什么不同。美妙之处在于有标签。Kafka队列有一个名称。有关的主持人都有名字。通过Kafka队列的跟踪也使用这些标记进行注释。我们实际上可以从这个资源加入到工作负载中,并回答这个问题。回到我刚才提到的那些大型SQL表,我们可以说,让我们看看通过这个特定Kafka队列的跟踪,因为这是大型表中的一列。让我们看看在这两个不同的时间窗口中通过特定队列的跟踪,并了解与此回归相关的其他因素。
我们看到的是,在这个拥有许多不同客户的多租户系统中,有一个客户的产品ID为1753,从占所有跟踪的0.8%增加到几乎占所有跟踪的16%。这在基线和回归之间大约增加了20倍。这真的很有趣。一些客户显著地改变了他们的工作量,而这正是我想要的。问题是,客户标签的位置太高了。Kafka队列中甚至没有。只有通过从资源转向分布式事务跟踪,我们才能自动理解在这个回归中涉及到一个特定的客户ID。我们通过扩展得到更多的细节,比如说,好的,我们又增加了大约20倍。然后,如果您愿意,我们可以查看样本跟踪,以明确了解该客户正在做什么。
我想说明的是,不需要写任何查询,就可以从这种感觉转到这种感觉。您不需要成为专家就可以从资源转向事务。现在,实际上很难做到这一点。总之,总的来说。我认为我对可观察性的看法是,我们不再将度量、日志和跟踪作为可观察性的重点。这只是原始数据。相反,我们允许运营者了解其应用程序在SLO方面的运行状况,了解其资源的运行状况,就像他们今天所做的那样。然后自然地在这两件事之间切换,而不必编写查询。了解事务工作负载如何影响资源,以及资源健康状况如何影响事务工作负载,而无需举手或做任何实际工作。
总结
事务遍历系统,使用资源。用户不关心您的资源。类似地,DevOps不能对单个事务做任何事情。他们只能对自己的资源进行操作,至少不能手动操作。我们必须能够使用自然连接资源和事务的系统和提供者,以解决可观察性中最重要的问题,即是什么导致了这种变化?无论是我事务的变化,还是我资源的变化。在这个资源和事务的框架内,这就是我认为可观察性的真正方向。
问答
但是,你提到了存储数据的成本。我认为,对于人们来说,这总是一次非常棘手的谈话。你在进行这些对话时有什么一般性的建议,这样你可以最小化成本,同时最大限度地提高观察力?
西格曼:现在肯定是个问题。关于这个话题,我有很多话要说。首先,我们想到的是,我们谈论的是事务还是资源?也就是说,我们是在谈论跟踪和日志之类的东西,还是更像是统计时间序列数据,比如度量?因为我认为这两类遥测的对话是不同的。我们发现,至少在我在谷歌工作的时候,还有Lightstep,它不仅仅是一个二进制的东西。你保存数据还是不保存?这就像,你一开始就做样品吗?你能把它从主机上取下来吗?您是否将其集中在广域网上?你储存多长时间?您存储它的粒度是多少?在精度方面,它是如何随时间降低的?当你看到组织在这方面变得非常成熟时,他们实际上在整个遥测生命周期中有不同的答案。我认为这是一个非常具有挑战性的问题,因为它取决于您所谈论的粒度。
我想说的主要一点是,如果一个组织没有能力在整个生命周期(包括网络)中分析其遥测成本,而网络实际上是生命周期成本中最大的组成部分之一,那么发送数据就是了。就像任何优化问题一样,你必须从这里开始。坦率地说,我认为很多组织都无法对其进行分析。您不能说应用程序的哪一部分导致了最长期的遥测成本。在你做到这一点之前,没有办法优化它。我从那里开始。那么对于已经这样做的人来说,我认为主要的事情是能够控制单个开发人员的成本,比如添加一行代码会给度量增加太多的基数。如果你做错了,每年可能要花费数十万美元。能够在中心位置纠正这一点,我认为是下一个重点。确保单个仪表线不能为平台团队带来无限的成本。
但是,我真的很喜欢你最后的例子,你经历了一个已经发生的事件,并在屏幕上分享。Ben提到了Lightstep,所以你可以看到它是如何工作的。我自己也用过。我觉得你能以如此快的速度找到一个真正的特定客户做某个动作并引发一个事件,这真是太令人惊讶了。我自己也知道,在我从事生产系统的职业生涯中,这种情况已经发生过很多次了,能够以极快的速度达到非常小的粒度级别是非常有效的。
在给定的两个时间间隔内,能够输出重要差异的web界面是什么?你想谈谈这个吗?你对此的想法,比如不同的界面如何帮助人们做得更好?这是件大事。
西格曼:这是一篇社论。我犹豫了一下,不想表现出来。我不知道还有什么其他的方式来表现它。如果我不展示一些东西,我觉得这太抽象了。要回答字面上的问题,那是Lightstep的产品。如果可以在数据层进行集成,那么就没有理由不能在其他地方进行集成。我认为在实践中很难做到的是,资源度量数据和事务跟踪数据之间的集成必须在数据层完成。你不能仅仅通过超链接来实现。我所描述的连接实际上是一个连接。您必须能够从度量中的标记连接到数千条记录道组上的标记。要做到这一点,需要在平台级别进行一些数据工程。事实上,我很想看到开源世界中有更多的解决方案朝着这个方向发展。事件解决的最短路径当然是能够通过时间序列数据和事务数据之间的标记进行透视。如果您必须从一个筒仓式度量解决方案和一个筒仓式跟踪解决方案(如web UI)开始,那么这实际上是一个困难的问题,因为集成必须在数据层进行。从产品的角度来看,这就是为什么它如此棘手的原因。
但是,我看到一些平台是在我工作过的地方内部创建的,但是要让它正常工作需要很多年和大量的迭代,所以一点也不容易。还有一件有趣的事,比如说,当你访问到这样的系统时,比如说,你有一个客户突然改变了他们的模式,但也许这很好。也许他们已经接触了所有这些新用户,并且正在进行一系列令人惊叹的工作,这意味着你可以对其他团队,比如公司的业务部门说,“看起来这个客户的工作真的很棒。你应该知道。你知道我们正在这样做吗?”它使工程团队能够更好地与真正有趣和有洞察力的数据进行对话,我认为这也很好。建议使用哪些工具进行跟踪?它始终是一个服务网格或类似的东西,还是在系统内部单独执行更好?
西格曼:是的。事实上,我在2017年在KubeCon做了一次关于服务网格和跟踪的演讲。服务网格是有帮助的,但它绝对不能解决问题。服务网格真正做的就是为您提供服务之间调用的遥测。跟踪最困难的部分始终是成功地将此跟踪ID上下文从服务的入口通过函数调用传播到该服务的出口。服务网格与此无关。服务网格只处理服务之间的调用。它在服务中,是跟踪中最困难的部分。这真的没用。您最终会得到一组带有服务网格的点对点数据,但要真正成功地解决上下文传播问题,我唯一的建议是转向OpenTelemetry。这实际上是试图以一种相当全面的方式解决这个问题,并使上下文传播成为一种内置功能。OpenTelemetry还将与服务网格集成。服务网格的部分优势在于它解决了跟踪问题,但事实并非如此。它添加了用于跟踪的数据,但没有解决上下文传播问题,而上下文传播问题实际上是分布式跟踪的核心。
但是,我看到一些平台是在我工作过的地方内部创建的,但是要让它正常工作需要很多年和大量的迭代,所以一点也不容易。还有一件有趣的事,比如说,当你访问到这样的系统时,比如说,你有一个客户突然改变了他们的模式,但也许这很好。也许他们已经接触了所有这些新用户,并且正在进行一系列令人惊叹的工作,这意味着你可以对其他团队,比如公司的业务部门说,“看起来这个客户的工作真的很棒。你应该知道。你知道我们正在这样做吗?”它使工程团队能够更好地与真正有趣和有洞察力的数据进行对话,我认为这也很好。建议使用哪些工具进行跟踪?它始终是一个服务网格或类似的东西,还是在系统内部单独执行更好?
西格曼:是的。事实上,我在2017年在KubeCon做了一次关于服务网格和跟踪的演讲。服务网格是有帮助的,但它绝对不能解决问题。服务网格真正做的就是为您提供服务之间调用的遥测。跟踪最困难的部分始终是成功地将此跟踪ID上下文从服务的入口通过函数调用传播到该服务的出口。服务网格与此无关。服务网格只处理服务之间的调用。它在服务中,是跟踪中最困难的部分。这真的没用。您最终会得到一组带有服务网格的点对点数据,但要真正成功地解决上下文传播问题,我唯一的建议是转向OpenTelemetry。这实际上是试图以一种相当全面的方式解决这个问题,并使上下文传播成为一种内置功能。OpenTelemetry还将与服务网格集成。服务网格的部分优势在于它解决了跟踪问题,但事实并非如此。它添加了用于跟踪的数据,但没有解决上下文传播问题,而上下文传播问题实际上是分布式跟踪的核心。
但是,关于OpenTelemetry还有一个问题。你的想法是什么?
西格曼:我是管理委员会的成员,是这个项目的共同创立者,我对此极有偏见。从拥有众多贡献者的角度来看,这确实是一个非常成功的项目。我认为仅在上个月我们就有1000名投稿人。每个主要的供应商和云提供商都已经购买了该项目,并且正在为该项目配备人员等等。OpenTelemetry的唯一问题是它有太多的活动,以至于我们在维护项目时遇到了一些困难。我认为它之所以如此成功,是因为它使许多方面受益。对于主要的基础设施提供商、云提供商、可观测性供应商,尤其是最终用户来说,这是一个双赢的局面,因为您最终可以获得高质量的遥测,而无需与任何特定的供应商或提供商绑定。这种便携性是一件非常吸引人的事情。我认为,长期以来,可观测性解决方案一直受到它们所能收集的遥测数据质量的限制。OpenTelemetry真的将掀起这股浪潮,然后您将看到解决方案的改进。OpenTelemetry,是的,这是一个非常激动人心的项目。我认为我们唯一真正需要的就是能够在项目中说一点不,这样我们就能达到我们的里程碑。在某种程度上,它是自身成功的牺牲品。它肯定有一个光明的未来。
但是,对于今年刚刚结束的OpenTelemetry的路线图,你有什么要分享的吗?有什么让你兴奋的事情吗?
西格曼:跟踪、度量和日志这三大支柱对于可观察性来说没有任何意义。我会坚持我的观点。它们对遥测技术来说绝对有意义,所以这三个方面。我们从追踪开始。到目前为止基本上都是这样。指标很快就要出来了。然后日志记录将在稍后出现。我认为从标准和API集成的角度来看,这实际上是OpenTelemetry要解决的三个问题中最不重要的一个。我很高兴指标能够超过这一点。我们还与普罗米修斯社区进行了大量的合作,以确保普罗米修斯和OpenTelemetry之间的互操作,这样您就不会被迫做出选择。我认为这也是一件非常好的事情,看到今年夏天稳定下来。
但是,关于分布式跟踪的性能成本,还有一个常见的问题。你的想法是什么?
Sigelman:从延迟的角度来看,分布式跟踪不需要任何开销。在占用资源的意义上,存在一些最小的边际吞吐量开销。通常,人们可以采取一些抽样来解决这个问题。我在谷歌帮助撰写的这篇短小精悍的论文详细描述了我们对绩效的衡量。从统计噪音的角度看,这是难以察觉的,Dapper 100%的时间都在运行100%的谷歌服务,并且已经运行了15年。如果做得正确,这绝对不是一件高开销的事情。这就是它的魅力之一。
原文:https://www.infoq.com/presentations/observability-resources-transaction…
- 39 次浏览
【分布式架构】Apache Helix vs YARN
原文:https://stackoverflow.com/questions/16401412/apache-helix-vs-yarn
Apache Helix和Hadoop YARN(MRv2)有什么区别。 有没有人有这两种技术的经验? 有人能解释一下Helix对YARN的优缺点,以及LinkedIn为什么开发自己的集群管理而不是使用YARN?
虽然Helix和YARN都提供了管理分布式应用程序的功能,但两者之间存在重要差异。
YARN主要提供跨机器群集的资源管理功能,同时要求应用程序编写其自定义逻辑以从资源管理器协商资源。另一方面,Helix提供了一种声明性地管理分布式应用程序状态的方法,从而使应用程序不必进行自定义实现。目前,Helix不像YARN那样提供资源管理功能。因此,这两个系统是相辅相成的。
作为一个例子,假设您有一组节点,并且您想要在它们上启动一些容器。
- 根据资源利用率在节点之间分配容器
- 启动容器,
- 监视容器,如果他们死了重启容器
YARN提供了执行上述操作的框架/机制。拥有容器后,必须实现以下功能:
- 分区和复制:您需要将任务分配给容器,可能会为每个容器分配多个任务。为了实现冗余,您可以选择将任务分配给多个容器。
- 状态管理:管理任务的状态
- 容错:当容器出现故障时,您可以选择在剩余容器中重新分配工作,也可以根据SLA要求重新启动容器。
- 群集扩展:您可以启动新容器来处理工作负载,然后您希望重新分配任务。
- 限制:在所有这些操作中,您可能希望限制某些操作,如数据移动
Helix可以轻松实现上述功能。在YARN中,需要编写应用程序主机来实现这些(这种实现的一个例子是hadoop map reduce作业的Application master)。
Helix是在LinkedIn开发的,用于管理在线/近线空间中的分布式数据系统。在这个空间中,一旦容器被启动,它就会一直运行直到它崩溃。当容器发生故障时,可能会在剩余的容器中重新分配任务。
YARN附带资源调度算法,可以灵活高效地利用可用硬件来完成短暂的任务,例如地图缩减作业。
- 169 次浏览
【分布式架构】Apache Spark vs Apache Ignite
原文:https://stackoverflow.com/questions/36036910/apache-spark-vs-apache-ignite
目前我正在研究Apache spark和Apache点燃框架。
本文描述了它们之间的一些原理差异点燃火花但我意识到我仍然不理解它们的目的。
我的意思是哪个问题比点燃更可取,反之亦然?
答案1:
我想说Spark是交互式分析的好产品,而Ignite更适合实时分析和高性能事务处理。 Ignite通过提供高效且可扩展的内存中键值存储以及索引,查询数据和运行计算的丰富功能来实现这一目标。
Ignite的另一个常见用途是分布式缓存,它通常用于提高与关系数据库或任何其他数据源交互的应用程序的性能。
答案2
Apache Ignite是一个高性能,集成和分布式内存平台,用于实时计算和处理大规模数据集.Ignite是一个与数据源无关的平台,可以在RAM中的多个服务器之间分发和缓存数据提供前所未有的处理速度和海量应用程序可扩展性
Apache Spark(集群计算框架)是一种快速的内存数据处理引擎,具有富有表现力的开发API,允许数据工作者有效地执行需要快速迭代访问数据集的流,机器学习或SQL工作负载。通过允许用户程序将数据加载到集群的内存中并重复查询,Spark非常适合高性能计算和机器学习算法。
一些概念差异:
Spark不存储数据,它从其他存储(通常是基于磁盘)加载数据以进行处理,然后在处理完成时丢弃数据。另一方面,Ignite提供具有ACID事务和SQL查询功能的分布式内存中键值存储(分布式缓存或数据网格)。
Spark用于非事务性的只读数据(RDD不支持就地突变),而Ignite支持非事务性(OLAP)有效负载以及完全符合ACID的事务(OLTP)
Ignite完全支持可以“无数据”的纯计算有效载荷(HPC / MPP)。 Spark基于RDD,仅适用于数据驱动的有效负载。
结论:
Ignite和Spark都是内存计算解决方案,但它们针对不同的用例。
在许多情况下,它们一起使用以获得优异的结果:
- Ignite可以提供共享存储,因此状态可以从一个Spark应用程序或作业传递到另一个。
- Ignite可以为SQL提供索引,因此Spark SQL可以加速超过1,000x(spark不会索引数据)
- 使用文件而不是RDD时,Apache Ignite内存文件系统(IGFS)也可以在Spark作业和应用程序之间共享状态
- 146 次浏览
【分布式架构】何时使用Apache Helix以及何时使用Apache Mesos
Apache Mesos是一个集群管理器,可跨分布式应用程序或框架提供有效的资源隔离和共享。 它可以在动态共享节点池上运行Hadoop,MPI,Hypertable,Spark和其他框架。
Apache Helix是一种通用的集群管理框架,用于自动管理托管在节点集群上的分区,复制和分布式资源。 Helix在面对节点故障和恢复,集群扩展和重新配置时自动重新分配资源。
两者都是集群管理者,可以选择哪一个,为什么?
这是我在Apache Helix vs YARN上编写的适用于Mesos v / s Helix的内容。 YARN / Mesos和Helix相互补充。
您可以使用Helix构建系统并管理系统的内部状态。 系统构建完成后,可以单独部署,也可以使用YARN / Mesos进行部署。
@janisz,Helix被广泛使用并积极开发。
原文:https://stackoverflow.com/questions/42154354/when-to-use-apache-helix-and-when-to-use-apache-mesos
- 138 次浏览
【分布式架构】最终一致性:暗示的切换队列(Hinted Handoff Queue)
在这个博客系列中,我们将探讨最终的一致性,如果没有合适的词汇表,这个术语很难定义。这是许多分布式系统使用的一致性模型,包括XDB Enterprise Edition。为了理解最终的一致性,我们需要知道两个概念:暗示切换队列和反熵,这两个概念都需要特别注意。
第一部分
什么是暗示的切换队列?
尽管有一个很酷的名字,暗示切换(HH)队列并没有得到很多关注。HH队列有一项非常重要的工作,但是除非您是系统管理员,否则很少直接与它交互。让我们深入研究一下暗示的切换队列到底是什么,以及为什么它对您很重要。
为了讨论HH队列,我们必须稍微讨论一下分布式计算。像XDB Enterprise这样的系统作为分布式系统存在的一个原因是消除单点故障。InfluxDB Enterprise使用复制因子(Replication Factor,RF)来确定任何一组数据应该存在多少个副本。将RF设置为1以上意味着系统有更高的机会成功地为请求提供服务,并且在数据节点中断期间不会返回错误,这意味着我们不再只有一个可能丢失或不可用的数据副本。分布式系统也提出了独特的挑战:我们如何知道数据在整个系统中是一致的,尤其是在存储多个数据副本时?
首先,我们必须理解最终一致性所作的一些承诺。扰流板警报:系统中的数据最终必须一致。当我们从分布式系统请求信息时,有时我们收到的答案可能不会一致地返回。当数据在整个系统中存储和复制时,我们收到的答案有一些“漂移”,但随着时间的推移,这种“漂移”应该被消除。在实践中,这意味着最近的时间范围可能在其结果中具有最大的变化,但是这种变化被消除,因为系统通过确保在任何地方都可以获得相同信息的机制工作。
如果我们保证系统最终是一致的,我们如何解释失败的写入?数据节点离线的原因有很多,从磁盘空间耗尽到普通的旧硬件故障。如果一个节点在离线时丢失了数据点,它就永远不可能是一致的,因此,我们对最终一致性的承诺将变成谎言。
失败的写入也会影响整个系统的复制系数。维护指定的RF是我们必须遵守的另一个承诺,如果数据节点脱机,这也是写入的另一个可能的失败点。
例子
让我们研究一下最简单的示例:具有2个数据节点和一个RF=2的数据库的XDB Enterprise。数据通过某个收集代理(例如Telegraf)到达您喜爱的负载平衡器,负载平衡器将写操作(也读取,但在本例中我们将使用写操作)分发到底层数据节点。通常,负载平衡器以循环方式分发写操作。接收数据的数据节点存储并复制数据(将其发送到另一个数据节点),瞧:RF达到2。
注意:图中未显示的是元节点,您可以在这里阅读。
我们仍然需要一个失败或延迟写入的解决方案。假设系统中的一个节点在物理上过热并离线。如果没有备份,任何不成功的写入都会被完全删除,再也看不到。
进入HH队列。
HH队列是一个持久的、基于磁盘的队列。它是XDB企业的一个基本部分,它试图确保最终的一致性,这是一种机制,确保所有的数据节点最终在它们之间拥有一组一致的数据。对于xdbenterprise,HH队列是实现最终一致性和确保最终实现每个数据库的数据复制因子的一个重要部分。
现在,让我们重温一下集群中的一个数据节点离线的场景。节点脱机的原因有很多:硬件缺陷、磁盘空间限制,甚至是定期维护。如果没有暗示的切换队列,不成功的写操作在存储之前就死了,但是现在我们有了一个安全的地方让它们着陆。
任何不成功的写入都会被定向到HH队列,当节点恢复联机时,它会检查HH队列中是否有挂起的写入。然后节点可以完成写操作,直到队列耗尽。Bam最终实现了一致性。
摘要
这是一个最终一致的集群内部发生的情况,但是外部有一些考虑因素:当数据成功写入一个节点,但无法正确复制时,用户看到成功还是失败?HH队列中的健康模式是什么样的?如果HH队列不断地充满和耗尽,对整个系统健康意味着什么?在下一篇文章中,我们将讨论如何解决和识别XDB企业集群中的问题模式。
原文:https://www.influxdata.com/blog/eventual-consistency-hhq/
本文:http://jiagoushi.pro/node/1454
讨论:请加入知识星球【首席架构师圈】或者微信【jiagoushi_pro】或者QQ群【11107777】
- 143 次浏览
【分部署架构】allthingsdistributed 说最终一致性
大约一年前,我在一致性模型上写了这篇文章的第一个版本,但我从来没有对它感到满意,因为它写得很匆忙,而且这个主题足够重要,需要得到更彻底的处理。ACM Queue要求我修改它以便在他们的杂志中使用,我利用这个机会改进了这篇文章。这是那个新版本。
最终的一致性——在全球范围内构建可靠的分布式系统需要在一致性和可用性之间进行权衡。
亚马逊云计算的基础是基础设施服务,如Amazon的S3(简单存储服务)、SimpleDB和EC2(弹性计算云),它们为构建互联网规模的计算平台和各种应用程序提供了资源。对这些基础设施服务的要求非常严格;他们需要在安全性、可伸缩性、可用性、性能和成本效益方面取得高分,并且他们需要在满足这些需求的同时,持续地为全球各地的数百万客户服务。
在这些服务的背后是在全球范围内运行的大规模分布式系统。这种规模带来了额外的挑战,因为当系统处理数万亿、数万亿的请求时,通常发生概率很低的事件现在保证会发生,需要在系统的设计和体系结构中预先考虑。考虑到这些系统的全球范围,我们广泛地使用复制技术来保证一致的性能和高可用性。尽管复制使我们更接近我们的目标,但它不能以完全透明的方式实现它们;在许多情况下,这些服务的客户将面临在服务内部使用复制技术的后果。
其表现方式之一是所提供的数据一致性类型,特别是当底层分布式系统为数据复制提供最终一致性模型时。在Amazon设计这些大规模系统时,我们使用一组与大规模数据复制相关的指导原则和抽象,并关注高可用性和数据一致性之间的权衡。在本文中,我将介绍一些相关的背景知识,这些背景知识为我们提供了交付需要在全球范围内运行的可靠分布式系统的方法。这篇文章的早期版本出现在2007年12月的All Things Distributed weblog上,并在读者的帮助下得到了极大的改进。
历史的角度
在理想的世界中,只有一个一致性模型:当进行更新时,所有观察者都将看到该更新。第一次出现这种难以实现的情况是在70年代末的数据库系统中。关于这个主题最好的“时期文章”是Bruce Lindsay等人写的“分布式数据库的注释”5。它列出了数据库复制的基本原则,并讨论了实现一致性的许多技术。这些技术中的许多都试图实现分布透明性—也就是说,对于系统的用户来说,看起来好像只有一个系统,而不是有许多协作系统。这一时期的许多系统采取的方法是,与其破坏这种透明度,不如让整个系统失灵
在90年代中期,随着大型互联网系统的兴起,这些做法被重新审视。那时,人们开始考虑可用性可能是这些系统最重要的属性,但他们也在为它应该与什么进行交换而挣扎。系统教授Eric Brewer的加州大学伯克利分校,当时Inktomi,带来了不同的交换在主题演讲PODC 2000.1(分布式计算的原则)会议上他提出上限定理,即三个属性的数据共享系统数据一致性、系统可用性和公差网络partition-only两个可以实现在任何给定的时间。Seth Gilbert和Nancy lynch在2002年的一篇论文中给出了更正式的确认
不能容忍网络分区的系统可以实现数据一致性和可用性,通常是通过使用事务协议实现的。要做到这一点,客户端和存储系统必须是同一个环境的一部分;在某些场景下,它们作为一个整体失败,因此,客户端无法观察分区。一个重要的观察结果是,在较大的分布式系统中,网络分区是给定的;因此,一致性和可用性不能同时实现。这意味着对于放弃什么有两种选择:放松一致性将允许系统在可分区条件下保持高可用性,而将一致性作为优先级意味着在某些条件下系统将不可用。
这两个选项都要求客户端开发人员知道系统提供了什么。如果系统强调一致性,开发人员就必须处理这样一个事实,即系统可能无法进行写入操作。如果由于系统不可用而导致写入失败,那么开发人员将不得不处理如何处理要写入的数据。如果系统强调可用性,它可能总是接受写操作,但在某些条件下,读操作不会反映最近完成的写操作的结果。然后开发人员必须决定客户端是否一直需要访问绝对最新的更新。有一系列应用程序可以处理稍微陈旧的数据,它们在此模型下得到了很好的服务。
原则上,ACID属性(原子性、一致性、隔离性、持久性)中定义的事务系统的一致性属性是一种不同类型的一致性保证。在ACID中,一致性指的是保证事务完成时数据库处于一致状态;例如,当从一个账户向另一个账户转账时,两个账户中的总金额不应改变。在基于acid的系统中,这种一致性通常是编写事务的开发人员的责任,但是数据库管理完整性约束可以帮助实现这种一致性。
一致性:客户端和服务器
有两种观察一致性的方法。一个是从开发人员/客户的角度:他们如何观察数据更新。第二种方法来自服务器端:更新如何流经系统,以及系统对更新可以提供哪些保证。
客户端一致性
客户端有以下组件:
- 一个存储系统。目前,我们将把它看作一个黑盒,但是我们应该假设它是一个大规模的、高度分布式的东西,并且构建它是为了保证持久性和可用性。
- 进程a。这是一个读写存储系统的进程。
- 进程B和进程c是独立于进程A的两个进程,它们对存储系统进行读写。它们是同一个进程中的进程还是线程无关紧要;重要的是,他们是独立的,需要交流来共享信息。
- 客户端一致性与观察者(在本例中是进程A、B或C)如何以及何时看到存储系统中数据对象的更新有关。在下面演示不同类型一致性的例子中,进程A对数据对象进行了更新:
- 强烈的一致性。更新完成后,任何后续访问(A、B或C)都将返回更新后的值。
- 弱一致性。系统不保证后续访问将返回更新后的值。在返回值之前,需要满足许多条件。从更新到保证任何观察者都能看到更新值这段时间被称为不一致窗口。
- 最终一致性。这是弱一致性的一种特殊形式;存储系统保证,如果没有对对象进行新的更新,最终所有访问都将返回最后更新的值。如果没有发生故障,可以根据通信延迟、系统负载和复制方案中涉及的副本数量等因素确定不一致窗口的最大大小。实现最终一致性的最普遍的系统是DNS(域名系统)。对名称的更新根据配置的模式进行分发,并与时间控制的缓存相结合;最终,所有客户端都将看到更新。
最终一致性模型有许多重要的变化需要考虑:
- 因果一致性。如果进程A通知进程B它已经更新了一个数据项,那么进程B的后续访问将返回更新后的值,并且保证一次写操作将取代之前的写操作。进程C的访问与进程A没有因果关系,遵循通常的最终一致性规则。
- “读己之所写”一致性。这是一个重要的模型,流程A在更新了数据项之后,总是访问更新后的值,永远不会看到旧的值。这是因果一致性模型的一个特例。
- 会话一致性。这是前一个模型的实际版本,其中进程在会话上下文中访问存储系统。只要会话存在,系统就保证“读己之所写”一致性。如果会话因为某种失败场景而终止,则需要创建一个新的会话,并且保证会话不会重叠。
- 单调读一致性。如果进程已经看到了该对象的特定值,那么任何后续访问都不会返回任何以前的值。
- 单调写一致性。在这种情况下,系统保证序列化同一个进程的写操作。不能保证这种级别一致性的系统是出了名的难以编程。
这些属性中有许多是可以组合的。例如,可以结合会话级别一致性获得单调的读取。从实际的角度来看,这两个属性(单调读取和read-your-write)在最终的一致性系统中是最理想的,但并不总是必需的。这两个属性使开发人员更容易构建应用程序,同时允许存储系统放松一致性并提供高可用性。
正如您可以从这些变化中看到的,相当多的不同场景是可能的。能否处理这些后果取决于特定的应用程序。
最终一致性并不是极端分布式系统的神秘属性。许多提供主备份可靠性的现代rdbms(关系数据库管理系统)同时以同步和异步模式实现它们的复制技术。在同步模式下,副本更新是事务的一部分。在异步模式下,更新以延迟的方式到达备份,通常通过日志传送。在后一种模式中,如果主备份在发送日志之前发生故障,从提升后的备份读取数据将产生旧的、不一致的值。另外,为了支持更好的可伸缩读性能,rdbms已经开始提供从备份中读取数据的能力,这是提供最终一致性保证的经典案例,在这种情况下,不一致性窗口取决于日志传送的周期。
服务器端一致性
在服务器端,我们需要更深入地研究更新如何流经系统,以理解是什么驱动了使用系统的开发人员可以体验不同的模式。在开始之前,让我们先建立一些定义:
N =存储数据副本的节点数
W =在更新完成之前需要确认已收到更新的副本的数量
R =通过读操作访问数据对象时所接触的副本数
如果W+R > N,那么写集和读集总是重叠的,可以保证强一致性。在实现同步复制的主备份RDBMS场景中,N=2、W=2和R=1。无论客户端从哪个副本读取数据,它都将得到一致的答案。在启用了从备份读取数据的异步复制中,N=2, W=1, R=1。在这种情况下,R+W=N,一致性无法保证。
这些配置是基本的quorum协议,它们的问题在于,当系统由于故障而无法写入W个节点时,写入操作必须失败,标志着系统不可用。当N=3 W=3且只有两个节点可用时,系统将不得不失败写操作。
在需要提供高性能和高可用性的分布式存储系统中,副本的数量通常大于两个。只关注容错的系统通常使用N=3 (W=2和R=2配置)。需要提供非常高读负载的系统经常复制超出容错要求的数据;N可以是数十个甚至数百个节点,R配置为1,这样一次读取就会返回一个结果。关注一致性的系统被设置为W=N进行更新,这可能会降低写入成功的可能性。这些系统关注容错性但不具有一致性,它们的一种常见配置是使用W=1运行以获得最小的更新持久性,然后依赖一种惰性(流行)技术来更新其他副本。
如何配置N、W和R取决于常见情况是什么,以及需要优化哪些性能路径。在R=1和N=W的情况下,我们优化读的情况,而在W=1和R=N的情况下,我们优化写的非常快。当然,在后一种情况下,在存在故障的情况下,持久性不能得到保证,如果W < (N+1)/2,那么当写集不重叠时,就有可能出现写冲突。
当W+R <= N时出现弱/最终一致性,这意味着读写集有可能不重叠。如果这是一个有意的配置,并且不是基于失败的情况,那么将R设为1以外的任何值都没有意义。这种情况通常发生在两种情况中:第一种是前面提到的为了读扩展而进行的大规模复制;第二个问题是数据访问更加复杂。在简单的键-值模型中,比较不同版本以确定写入系统的最新值很容易,但是在返回对象集的系统中,确定正确的最新值集就比较困难了。在大多数写集小于副本集的系统中,会有一种机制以一种惰性的方式将更新应用到副本集的其余节点。直到所有副本都被更新为止的时间段是前面讨论过的不一致窗口。如果W+R <= N,则系统容易从尚未接收到更新的节点读取数据。
“读你的写”、会话和单调一致性是否可以实现,通常取决于客户机对执行它们的分布式协议的服务器的“粘性”。如果每次都是相同的服务器,则相对容易保证“读己之所写”和单调的读取。这使得管理负载平衡和容错稍微困难一些,但这是一个简单的解决方案。使用会话,这是粘性的,使这一点显式,并提供了一个客户端可以推理的公开级别。
有时客户端实现read-your-write和单调读取。通过在写操作上添加版本,客户端将丢弃对版本在最后一个版本之前的值的读取。
当系统中的一些节点无法到达其他节点时,就会发生分区,但两个节点集都可以被客户端组访问。如果您使用传统的多数仲裁方法,那么具有W个副本集节点的分区可以继续进行更新,而另一个分区变得不可用。对于读集也是如此。给定这两个集重叠,根据定义,少数集变得不可用。分区并不经常发生,但确实会发生在数据中心之间以及数据中心内部。
在某些应用程序中,任何分区的不可用性都是不可接受的,重要的是能够到达该分区的客户机能够取得进展。在这种情况下,双方分配一组新的存储节点来接收数据,并在分区愈合时执行合并操作。例如,在Amazon中购物车使用这样的write-always系统;在分区的情况下,客户可以继续将商品放入购物车,即使原来的购物车存在于其他分区上。一旦分区恢复,cart应用程序将帮助存储系统合并购物车。
亚马逊的Dynamo
Amazon的Dynamo系统将所有这些属性置于应用程序体系结构的显式控制之下,这是一个键值存储系统,在组成Amazon电子商务平台的许多服务以及Amazon的Web服务内部使用该系统。Dynamo的设计目标之一是允许创建Dynamo存储系统实例(通常跨越多个数据中心)的应用程序服务所有者在一致性、持久性、可用性和性能之间以一定的成本进行权衡
总结
在大规模可靠分布式系统中,必须容忍数据不一致性,原因有二:提高高并发条件下的读写性能;以及处理大多数模型会导致部分系统不可用的分区情况,即使节点已经启动并运行。
不一致是否可以接受取决于客户机应用程序。在所有情况下,开发人员都需要意识到,存储系统提供了一致性保证,并且在开发应用程序时需要考虑到这一点。对于最终一致性模型有许多实际的改进,比如会话级一致性和单调读取,它们为开发人员提供了更好的工具。很多时候,应用程序都能够毫无问题地处理存储系统的最终一致性保证。一个特定的流行案例是一个网站,在其中我们可以有用户感知一致性的概念。在此场景中,不一致性窗口需要小于客户返回下一页加载的预期时间。这允许更新在预期下一次读取之前在系统中传播。
本文的目标是提高对工程系统复杂性的认识,这些系统需要在全球范围内运行,并且需要仔细调优,以确保它们能够交付应用程序所需的持久性、可用性和性能。系统设计者拥有的工具之一是一致性窗口的长度,在此期间,系统的客户可能暴露在大规模系统工程的现实中。
原文:https://www.allthingsdistributed.com/2008/12/eventually_consistent.html
本文:http://jiagoushi.pro/node/1387
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】或者QQ群【11107777】
- 40 次浏览
【微服务架构】多运行时微服务架构
视频号
微信公众号
知识星球
主要收获
- 创建分布式系统并非易事。围绕“微服务”架构和“12因素应用程序”设计出现了最佳实践。这些提供了与交付生命周期、网络、状态管理以及与外部依赖关系的绑定相关的指导方针。
- 然而,以可扩展和可维护的方法一致地实现这些原则是具有挑战性的。
- 解决这些原则的传统技术方法包括企业服务总线(ESB)和面向消息中间件(MOM)。虽然这些解决方案提供了一个很好的功能集,但主要的挑战是业务逻辑和平台之间的整体架构和紧密的技术耦合。
- 随着云、容器和容器编排器(Kubernetes)的流行,解决这些原则的新解决方案也出现了。例如,Knative用于交付,服务网格用于联网,Camel-K用于绑定和集成。
- 使用这种方法,业务逻辑(称为“微逻辑”)形成了应用程序的核心,可以创建sidecar“meca”组件,这些组件提供强大的开箱即用分布式原语。
- 这种微逻辑和Mecha组件的解耦可能会改善第二天的操作,如修补和升级,并有助于维持业务逻辑的内聚单元的长期可维护性。
创建良好的分布式应用程序并非易事:此类系统通常遵循12因素应用程序和微服务原则。它们必须是无状态的、可扩展的、可配置的、独立发布的、容器化的、自动化的,有时是事件驱动的和无服务器的。一旦创建,它们应该易于升级,并且可以长期维护。在这些相互竞争的需求与当今的技术之间找到良好的平衡仍然是一项艰巨的努力。
在本文中,我将探讨分布式平台是如何发展以实现这种平衡的,更重要的是,在分布式系统的发展过程中还需要做些什么来简化可维护的分布式架构的创建。如果你想看到我现场谈论这个话题,请加入我三月在伦敦的QCon。
分布式应用程序需求
在这次讨论中,我将把现代分布式应用程序的需求分为四类——生命周期、网络、状态、绑定——并简要分析它们近年来的发展情况。
Distributed application needs
生命周期
让我们从基础开始。当我们编写一个功能时,编程语言规定了生态系统中可用的库、打包格式和运行时。例如,Java使用.jar格式,所有Maven依赖项作为一个生态系统,JVM作为运行时。如今,随着发布周期的加快,生命周期中更重要的是能够以自动化的方式部署、从错误中恢复和扩展服务。这组功能广泛代表了我们的应用程序生命周期需求。
网络
在某种意义上,今天几乎每个应用程序都是分布式应用程序,因此需要联网。但现代分布式系统需要从更广泛的角度来掌握网络。从服务发现和错误恢复开始,实现现代软件发布技术以及各种跟踪和遥测。出于我们的目的,我们甚至将在这一类别中包括不同的消息交换模式、点对点和发布/订阅方法以及智能路由机制。
状态
当我们谈论状态时,通常是关于服务状态以及为什么最好是无状态。但管理我们服务的平台本身需要状态。这是进行可靠的服务编排和工作流、分布式单例、临时调度(cron作业)、幂等性、有状态错误恢复、缓存等所必需的。这里列出的所有功能都依赖于隐藏状态。虽然实际的状态管理不是本文的范围,但分布式原语及其依赖于状态的抽象是令人感兴趣的。
结合(绑定)
分布式系统的组件不仅要相互通信,还要与现代或遗留的外部系统集成。这需要连接器能够转换各种协议,支持不同的消息交换模式,如轮询、事件驱动、请求/回复、转换消息格式,甚至能够执行自定义的错误恢复过程和安全机制。
在不涉及一次性用例的情况下,以上内容代表了创建良好分布式系统所需的公共原语的良好集合。如今,许多平台都提供了这样的功能,但我们在本文中要寻找的是,我们使用这些功能的方式在过去十年中发生了怎样的变化,以及下一个十年的情况。为了进行比较,让我们看看过去十年,看看基于Java的中间件是如何满足这些需求的。
传统中间件的局限性
满足上述老一代需求的著名传统解决方案之一是企业服务总线(ESB)及其变体,如面向消息的中间件、较轻的集成框架等。ESB是一种中间件,使用面向服务的架构(即经典SOA)实现异构环境之间的互操作性。
虽然ESB可以为您提供一个很好的功能集,但ESB的主要挑战是业务逻辑和平台之间的整体架构和紧密的技术耦合,这导致了技术和组织的集中化。当一个服务被开发并部署到这样的系统中时,它与分布式系统框架深度耦合,这反过来限制了服务的发展。这通常只有在软件生命的后期才变得明显。
以下是每一类需求的一些问题和限制,这些问题和限制使ESB在现代没有用处。
生命周期
在传统中间件中,通常有一个受支持的语言运行时(如Java),它规定了软件的打包方式、可用的库、修补的频率等。业务服务必须使用这些库,这些库将其与用同一语言编写的平台紧密耦合。在实践中,这导致了协调服务和平台升级,从而阻止了独立和定期的服务和平台发布。
网络
虽然传统的中间件有一个高级功能集,主要集中在与其他内部和外部服务的交互上,但它也有一些主要缺点。网络功能以一种主要语言及其相关技术为中心。对于Java语言,即JMS、JDBC、JTA等。更重要的是,网络问题和语义也深深地刻在了业务服务中。有些库使用抽象来处理网络问题(例如曾经流行的Hystrix项目),但库的抽象“泄漏”到服务中——它的编程模型、交换模式和错误处理语义,以及库本身。虽然在一个位置编码和阅读混合了网络方面的整个业务逻辑很方便,但这将两个问题紧密地耦合到一个单独的实现中,并最终形成一个联合的进化路径。
状态
为了进行可靠的服务编排、业务流程管理和实现模式,如Saga模式和其他缓慢运行的流程,平台需要在后台保持持久状态。类似地,临时操作(如启动计时器和cron作业)是建立在状态之上的,需要数据库在分布式环境中集群化并具有弹性。这里的主要约束是,与状态交互的库和接口没有完全抽象,也没有与服务运行时解耦。通常,这些库必须使用数据库详细信息进行配置,并且它们位于将语义和依赖关系泄漏到应用程序域的服务中。
结合
使用集成中间件的主要驱动因素之一是能够使用不同的协议、数据格式和消息交换模式连接到各种其他系统。然而,这些连接器必须与应用程序一起使用,这意味着依赖关系必须与业务逻辑一起更新和修补。这意味着数据类型和数据格式必须在服务中来回转换。这意味着代码必须根据消息交换模式进行结构化和流设计。以下是一些示例,说明了即使是抽象端点也会如何影响传统中间件中的服务实现。
云原生趋势
传统的中间件功能强大。它具有所有必要的技术特征,但缺乏快速变化和扩展的能力,这是现代数字业务需求所要求的。这就是微服务架构及其设计现代分布式应用程序的指导原则正在解决的问题。
微服务背后的思想及其技术要求促成了容器和Kubernetes的普及和广泛使用。这开启了一种新的创新方式,将在未来几年影响我们处理分布式应用程序的方式。让我们看看Kubernetes和相关技术如何影响每一组需求。
生命周期
容器和Kubernetes将我们打包、分发和部署应用程序的方式发展为独立于语言的格式。有很多关于Kubernetes模式和Kubernete对开发人员的影响的文章,我将在这里简短地介绍一下。不过,请注意,对于Kubernetes来说,要管理的最小原语是容器,它专注于在容器级别和流程模型上交付分布式原语。这意味着它在管理应用程序的生命周期、健康检查、恢复、部署和扩展方面做得很好,但在容器内的分布式应用程序的其他方面(如灵活的网络、状态管理和绑定)改进得不太好。
您可能会指出,Kubernetes具有有状态工作负载、服务发现、cron作业和其他功能。的确如此,但所有这些基元都在容器级别,在容器内部,开发人员仍然必须使用特定于语言的库来访问我们在本文开头列出的更细粒度的功能。这就是Envoy、Linkerd、Consul、Knative、Dapr、Camel-K等项目的驱动力。
网络
事实证明,Kubernetes提供的围绕服务发现的基本网络功能是一个很好的基础,但对于现代应用程序来说还不够。随着微服务数量的增加和部署速度的加快,对更先进的发布策略的需求变得越来越有吸引力,这些策略包括在不接触服务的情况下管理安全、度量、跟踪、从错误中恢复、模拟错误等,并创造了一种新的软件类别,称为服务网格。
这里更令人兴奋的是,将与网络相关的关注点从包含业务逻辑的服务转移到外部,并转移到单独的运行时,无论是sidecar还是节点级代理。如今,服务网格可以进行高级路由、帮助测试、处理某些方面的安全,甚至可以使用特定于应用程序的协议(例如Envoy支持Kafka、MongoDB、Redis、MySQL等)。虽然服务网格作为一种解决方案可能还没有被广泛采用,但它触及了分布式系统中的一个真正的痛点,我相信它会找到自己的存在形式。
除了典型的服务机制外,还有其他项目,如Skupper,证实了将网络功能放入外部运行时代理的趋势。Skupper通过第7层虚拟网络解决了多集群通信挑战,并提供了高级路由和连接功能。但是,它并没有将Skupper嵌入到业务服务运行时,而是在每个Kubernetes命名空间中运行一个实例,该实例充当共享的sidecar。
总之,容器和Kubernetes在应用程序的生命周期管理方面迈出了重要的一步。服务网格和相关技术触及了真正的痛点,并为将应用程序之外的更多职责转移到代理中奠定了基础。让我们看看接下来会发生什么。
状态
我们前面列出了依赖于状态的主要集成原语。管理状态很困难,应该委托给专门的存储软件和托管服务。这不是这里的主题,但在语言无关的抽象中使用状态来帮助集成用例才是。今天,许多努力试图在语言无关抽象背后提供有状态的原语。有状态工作流管理是基于云的服务中的一项强制性功能,例如AWS Step Functions、Azure Durable Functions等。在基于容器的部署中,CloudState和Dapr都依赖于sidecar模型来更好地解耦分布式应用程序中的有状态抽象。
我所期待的也是将上面列出的所有有状态特性抽象到一个单独的运行时中。这意味着工作流管理、单重、幂等性、事务管理、cron作业触发器和有状态错误处理都可靠地发生在sidecar(或主机级代理)中,而不是生活在服务中。业务逻辑不需要在应用程序中包括这样的依赖关系和语义,并且它可以声明性地从绑定环境请求这样的行为。例如,sidecar可以充当cron作业触发器、幂等消费者和工作流管理器,自定义业务逻辑可以作为回调调用,也可以在工作流、错误处理、临时调用或唯一幂等请求的某些阶段插入。
另一个有状态的用例是缓存。无论是由服务网格层执行的请求缓存,还是使用Infinispan、Redis、Hazelcast等进行的数据缓存,都有将缓存功能推出应用程序运行时的例子。
结合
虽然我们讨论的主题是将所有分布式需求与应用程序运行时解耦,但绑定的趋势也在继续。连接器、协议转换、消息转换、错误处理和安全中介都可能移出服务运行时。我们还没有达到目标,但Knative和Dapr等项目正在朝着这个方向进行尝试。将所有这些职责从应用程序运行时中移出将导致更小的、以业务逻辑为中心的代码。这样的代码将生活在一个独立于分布式系统需求的运行时中,这些需求可以作为预打包功能来使用。
Apache Camel-K项目采用了另一种有趣的方法。该项目不是使用代理运行时来伴随主应用程序,而是依赖于一个智能的Kubernetes Operator,该Operator使用Kubernete和Knative的额外平台功能构建应用程序运行时。这里,单个代理是负责包含应用程序所需的分布式系统原语的操作员。不同之处在于,一些分布式原语被添加到应用程序运行时,而一些则在平台中启用(平台也可能包括sidecar)。
未来架构趋势
从广义上看,我们可以得出结论,通过将功能转移到平台级别,分布式应用程序的商品化达到了新的前沿。除了生命周期之外,现在我们还可以观察到网络、状态抽象、声明性事件和端点绑定,这些都是现成的,EIP是下一个。有趣的是,商品化是使用进程外模型(sidecars)进行功能扩展,而不是运行库或纯平台功能(如新的Kubernetes功能)。
我们现在正通过将所有传统中间件功能(也称为ESB)转移到其他运行时来接近完整的循环,很快,我们在服务中所要做的就是编写业务逻辑。
传统中间件平台和云原生平台概述
与传统的ESB时代相比,这种架构更好地将业务逻辑与平台解耦,但尚未完全解耦。许多分布式原语,如经典的企业集成模式(EIP):拆分器、聚合器、过滤器、基于内容的路由器;以及流处理模式:映射、过滤、折叠、连接、合并、滑动窗口;仍然必须包含在业务逻辑运行时中,并且许多其他依赖于多个不同且重叠的平台附加组件。
如果我们把在不同领域创新的各种云原生项目叠加起来,我们最终会得到如下图片:
多运行时微服务
这里的图表仅用于说明目的,它有目的地选择有代表性的项目,并将它们映射到一类分布式原语中。在实践中,您不会同时使用所有这些项目,因为其中一些项目是重叠的且不兼容的工作负载模型。如何解释这个图表?
Kubernetes和容器在多语言应用程序的生命周期管理方面实现了巨大的飞跃,并为未来的创新奠定了基础。
服务网格技术在Kubernetes上进行了改进,具有先进的网络功能,并开始利用应用程序问题。
虽然Knative主要通过快速扩展专注于无服务器工作负载,但它也解决了服务编排和事件驱动的绑定需求。
Dapr以Kubernetes、Knative和Service Mesh的思想为基础,深入应用程序运行时,以解决有状态工作负载、绑定和集成需求,充当现代分布式中间件。
这个图表可以帮助您直观地看到,在未来,我们很可能会使用多个运行时来实现分布式系统。多个运行时,不是因为多个微服务,而是因为每个微服务都将由多个运行时间组成,很可能是两个——自定义业务逻辑运行时间和分布式原语运行时间。
引入多运行时微服务
以下是开始形成的多运行时微服务架构的简要描述。
你还记得电影《阿凡达》和科学家们为探索潘多拉而开发的“机械套装”吗?这种多运行时架构类似于这些赋予人形驾驶员超能力的机械套装。在电影中,人类穿上西装以获得力量并获得破坏性武器。在这个软件架构中,您的业务逻辑(称为微逻辑)构成了应用程序的核心,sidecar meca组件提供了强大的开箱即用分布式原语。微逻辑与机械功能相结合,形成了一个多运行时微服务,该服务使用进程外功能来满足其分布式系统的需求。最棒的是,《阿凡达2》即将问世,以帮助推广这一架构。我们终于可以在所有软件会议上用令人敬畏的机甲图片取代老式的边车摩托车了;-)。接下来让我们看一下这个软件架构的细节。
这是一个类似于客户端-服务器架构的双组件模型,其中每个组件都是独立的运行时。它与纯客户端-服务器架构的不同之处在于,在这里,两个组件都位于同一台主机上,它们之间有可靠的网络连接,这不是一个问题。这两个组件的重要性相同,它们可以在任一方向启动操作,并充当客户端或服务器。其中一个组件被称为Micrologic,它包含了几乎所有分布式系统关注的最小业务逻辑。另一个附带的组件是Mecha,它提供了我们在文章中讨论的所有分布式系统功能(除了作为平台功能的生命周期)。
多运行时(进程外)微服务架构
Micrologic和Mecha可能有一对一的部署(称为侧车模型),也可以是一个共享的Mecha和几个Micrologic运行时。第一种模型最适用于环境,如Kubernetes,后者适用于边缘部署。
Micrologic运行时特性
让我们简要探讨一下Micrologic运行时的一些特性:
- Micrologic组件本身并不是一个微服务。它包含微服务所具有的业务逻辑,但该逻辑只能与Mecha组件结合使用。另一方面,微服务是自包含的,不具有分散到其他运行时的整体功能或部分处理流。Micrologic和它的机械对应物的结合形成了一个微服务。
- 这也不是一个功能或无服务器架构。无服务器主要以其管理式快速扩展和零扩展功能而闻名。在无服务器架构中,函数实现单个操作,因为这是可伸缩性的单位。在这方面,函数不同于实现多个操作的Micrologic,但实现不是端到端的。最重要的是,操作的实现分布在Mecha和Micrologic运行时。
- 这是客户端-服务器架构的一种特殊形式,针对众所周知的分布式原语的使用进行了优化,而无需编码。此外,如果我们假设Mecha扮演服务器角色,那么必须将每个实例专门配置为与单个客户端一起工作。它不是一个通用的服务器实例,旨在与典型的客户端-服务器架构同时支持多个客户端。
- 微逻辑中的用户代码不与其他系统直接交互,也不实现任何分布式系统原语。它通过事实上的标准(如HTTP/gRPC、CloudEvents规范)与Mecha进行交互,Mecha使用丰富的功能并在配置的步骤和机制的指导下与其他系统进行通信。
- 虽然Micrologic只负责实现从分布式系统中剥离出来的业务逻辑,但它仍然必须至少实现一些API。它必须允许Mecha和平台通过预定义的API和协议与其交互(例如,通过遵循Kubernetes部署的云原生设计原则)。
Mecha运行时特性
以下是Mecha运行时的一些特性:
- Mecha是一种通用的、高度可配置的、可重复使用的组件,提供分布式原语作为现成的功能。
- Mecha的每个实例都必须配置为与一个Micrologic组件(侧车模型)一起工作,或者配置为与几个组件共享。
- Mecha没有对Micrologic运行时做出任何假设。它使用开放协议和格式(如HTTP/gRPC、JSON、Protobuf、CloudEvents)与多语言微服务甚至单片系统协同工作。
- Mecha是用简单的文本格式(如YAML、JSON)以声明方式配置的,这些格式规定了要启用哪些功能以及如何将它们绑定到Micrologic端点。对于专门的API交互,Mechan可以额外提供规范,如OpenAPI、AsyncAPI、ANSI-SQL等。对于由多个处理步骤组成的有状态工作流,可以使用规范,如Amazon State Language。对于无状态集成,企业集成模式(EIP)可以与类似于Camel-K YAML DSL的方法一起使用。这里的关键点是,所有这些都是简单的、基于文本的、声明性的、多语言的定义,Meca可以在不编码的情况下完成这些定义。请注意,这些都是未来的预测,目前还没有用于有状态编排或EIP的机制,但我预计现有的机制(Envoy、Dapr、Cloudstate等)很快就会开始添加此类功能。Mecha是一个应用级分布式原语抽象层。
- 与其依赖多个代理用于不同的目的,例如网络代理、缓存代理、绑定代理,不如由一个Mecha提供所有这些功能。一些功能的实现,如存储、消息持久性、缓存等,将由其他云或内部部署服务插入并支持。
- 一些围绕生命周期管理的分布式系统关注点由管理平台(如Kubernetes或其他云服务)提供,而不是由使用通用开放规范(如开放应用程序模型)的Mecha运行时提供。
这种架构的主要优点是什么?
好处是业务逻辑和日益增多的分布式系统关注点之间的松散耦合。软件系统的这两个元素具有完全不同的动态特性。业务逻辑始终是唯一的、自定义的代码,由内部编写。它经常变化,这取决于您的组织优先级和执行能力。另一方面,分布式原语是解决本文中列出的问题的原语,它们是众所周知的。这些由软件供应商开发,并作为库、容器或服务使用。这些代码会根据供应商的优先级、发布周期、安全补丁、开源管理规则等而变化。这两个组对彼此几乎没有可见性和控制权。
业务逻辑和分布式系统关注不同架构中的耦合
微服务原则有助于通过有限的上下文将不同的业务领域解耦,在有限的上下文中,每个微服务都可以独立发展。但微服务架构并没有解决将业务逻辑与中间件问题耦合所带来的困难。对于某些轻集成用例的微服务来说,这可能不是一个大因素。但是,如果您的领域涉及复杂的集成(这对每个人来说都越来越普遍),遵循微服务原则将无法帮助您避免与中间件耦合。即使中间件被表示为包含在微服务中的库,当您开始迁移和更改这些库时,这种耦合也会变得明显。您需要的分布式原语越多,就越能耦合到集成平台中。将中间件作为预定义API上的单独运行时/进程而不是库使用有助于松耦合,并支持每个组件的独立演化。
这也是为供应商分发和维护复杂中间件软件的更好方法。只要与中间件的交互是通过涉及开放API和标准的进程间通信进行的,软件供应商就可以按照自己的速度免费发布补丁和升级。消费者可以自由使用他们喜欢的语言、库、运行时、部署方法和流程。
这种架构的主要缺点是什么?
进程间通信。分布式系统的业务逻辑和中间件机制(您可以看到名称的来源)处于不同的运行时,这需要HTTP或gRPC调用,而不是进程内方法调用。不过,请注意,这不是一个应该转到不同机器或数据中心的网络呼叫。Micrologic运行时和Mecha应该位于同一台主机上,具有低延迟和最小的网络问题可能性。
复杂性下一个问题是,它是否值得开发的复杂性,以及为了获得的利益而维护这样的系统。我认为答案会越来越倾向于肯定。分布式系统的需求和发布周期的速度都在增加。这个架构为此进行了优化。我前段时间写道,未来的开发人员必须具备混合开发技能。这种架构进一步证实并强化了这一趋势。应用程序的一部分将用更高级的编程语言编写,部分功能将由必须声明配置的现成组件提供。这两个部分不是在编译时相互连接,也不是在启动时通过进程内依赖注入相互连接,而是在部署时通过进程间通信相互连接。该模型实现了更高的软件重用率和更快的变化速度。
微服务之后不是服务(FaaS)
微服务架构有一个明确的目标。它针对变化进行优化。通过将应用程序划分为业务域,该架构通过解耦、由独立团队管理并以独立的速度发布的服务,为软件进化和可维护性提供了最佳的服务边界。
如果我们看一下无服务器架构的编程模型,它主要是基于函数的。功能针对可扩展性进行了优化。通过功能,我们将每个操作拆分为一个独立的组件,以便它能够快速、独立和按需扩展。在这个模型中,部署粒度是一个函数。选择该函数是因为它是具有输入的代码结构,其速率与缩放行为直接相关。这是一种针对极端可扩展性而优化的架构,而不是复杂系统的长期可维护性。
Serverless的另一个方面是什么?这源于AWS Lambda的流行及其完全管理的操作性质?在这方面,“AWS Serverless”以缺乏控制和锁定为代价,优化了供应速度。但完全管理的方面不是应用程序架构,而是软件消费模型。它是一个正交的功能,类似于消费一个基于SaaS的平台,在理想的世界中,该平台应该适用于任何类型的架构,无论是单片、微服务、机制还是功能。在许多方面,AWS Lambda类似于一个完全管理的Mecha架构,但有一个很大的区别:Mecha不强制执行功能模型,而是允许围绕业务领域构建更具凝聚力的代码,与所有中间件无关。
架构优化
另一方面,Mecha架构优化了微服务以实现中间件的独立性。虽然微服务是相互独立的,但它们在很大程度上依赖于嵌入式分布式原语。Mecha架构将这两个关注点划分为单独的运行时,允许独立团队独立发布。这种解耦改进了第二天的操作(如修补和升级)以及业务逻辑的内聚单元的长期可维护性。在这方面,Mecha架构是微服务架构的自然发展,基于引起最大摩擦的边界拆分软件。与功能模型相比,这种优化以软件重用和进化的形式提供了更多的好处,功能模型以过度分发代码为代价,优化了极大的可扩展性。
结论
分布式应用程序有许多要求。创建有效的分布式系统需要多种技术和良好的集成方法。虽然传统的单片中间件提供了分布式系统所需的所有必要技术功能,但它缺乏业务所需的快速更改、调整和扩展能力。这就是为什么基于微服务的架构背后的思想促成了容器和Kubernetes的快速普及;随着云原生空间的最新发展,我们现在正通过将所有传统中间件功能转移到平台和现成的辅助运行时中来实现完整的循环。
这种应用程序特性的商品化主要是使用进程外模型进行特性扩展,而不是使用运行库或纯平台特性。这意味着在未来,我们很可能会使用多个运行时来实现分布式系统。多个运行时,不是因为多个微服务,而是因为每个微服务都会由多个运行时间组成;用于自定义微业务逻辑的运行时,以及用于分布式原语的现成的可配置运行时。
- 111 次浏览
【数据一致性】理解分布式系统中的一致性
首先,什么是一致性?
一致性是指分布式系统中多个节点为达到某一数值而达成的协议。
具体来说,可以分为强一致性和弱一致性。
- 强一致性:所有节点的数据在任何时候都是相同的。同时,您应该得到节点A中的key1值和节点B中的key1值。
- 弱一致性:无法保证所有节点在任何时候都拥有相同的数据,并且存在许多不同的实现。最广泛实现的是最终的一致性。所谓最终一致性,是指任何节点上的相同数据在任何时间都是相同的,但随着时间的推移,不同节点上的相同数据总是朝着收敛的方向变化。也可以简单理解为,经过一段时间后,节点之间的数据最终会达到一致状态。
分布式和一致的应用场景:
多节点提供读写服务,确保高可用性和可扩展性(ZooKeeper, DNS, redis集群)
分布式系统面临的问题:
- 消息异步(asynchronous):真实的网络不是一个可靠的通道,存在消息延迟、丢失,以及节点间消息不能同步(synchronous)
- node-fail-stop:节点继续崩溃并且无法恢复
- 节点宕机恢复(fail- recovery):节点经过一段时间后恢复,在分布式系统中最为常见
- 网络分区:网络链接有问题,将N个节点分成多个部分
- 拜占庭故障(拜占庭故障)[2]:节点或宕机或逻辑故障,即使没有卡抛出干扰分辨信息
需要满足一致性的分布式系统设计的一般前提是不存在拜占庭一般问题(内部网可信)
本文介绍了分布式系统的基本理论FLP定理,即当只有节点宕机时,系统的可用性和强一致性不能同时满足。另一种观点是CAP理论,即强一致性、可用性和分区容错,其中只有两种可以保证。
有许多协议确保一致性,包括2PC, 3PC, Paxos, raft和PacificA。
2PC:
两阶段锁提交协议,保证多个数据片上操作的原子性。(分布式事务)
将节点分为协调者和参与者(participat),并将执行分为两个阶段。
- 阶段1:协调器发起一个建议,询问是否接受每个参与者。参与执行事务操作,将撤销和重做信息写入事务日志,并向协调器回复是或否。
- 阶段2:协调器根据参与者的反馈提交或终止事务。如果参与者的回答都是yes,则提交,只要参与者的回答是no。根据协调器的提交/回滚信息正式提交或终止事务,释放占用的资源并返回ack。
- 优点:原理简单,易于实现
- 缺点:同步阻塞,单点问题,数据不一致(协调员崩溃之前发送提交请求或网络原因共识不收到提交的一部分,那么参与者不能提交事务的一部分),过于保守(如果参与者在协调)如果有失败在沟通、协调器只能依靠超时机制来确定事务需要打断。
3PC:
3级锁提交协议,保证多个数据片上操作的原子性。(分布式事务)
相对于2PC,分为查询、预提交、提交3个阶段(解决阻塞,但仍有数据不一致的可能)
流程:在收到参与者的反馈(投票)后,协调器进入阶段2,并向每个参与者发送准备提交命令。参与者可以在收到提交指令后锁定资源,但要求回滚相关操作。在收到应答(ACK)后,协调器进入第三阶段并提交/中止。3PC的第三阶段与2PC的第二阶段没有什么不同。3PC也采用协调看门狗和状态记录。
Paxos算法(求解单点问题)
Paxos算法是目前最重要的一致性算法,所有的一致性算法都是Paxos或Paxos的简化版本。
Paxos算法会解析相同数据的多个值,以达成一个值的一致。证明正确性的理论基础:任何两个合法集合(包含超过一半节点的集合)的交集都不是空的。
性格:
在投票过程的提案中有三个作用:
- Proposer:可能有多于一个提案人,并负责提出建议。
- Acceptor:必须有多个收件人。他们对特定的提案进行投票,同意接受提案,或不同意。
- Learner:学习者,收集各Acceptor接受的提案,按照少数多数的原则形成最终提案。
事实上,分布式系统中的一个组件可以对应一个或多个角色。
算法描述:
*第一阶段(准备阶段)
申请人:
- 选择提案编号n,向大多数接受人发送编号n的准备请求。
受体:
- 如果提案号n大于已经收到的提案号,提案人承诺不接受编号小于n的提案号。如果之前已经接受过提案号,则以已接受提案号中编号最高的提案号为已发送提案号。
- 如果收到的提案号n小于它收到的提案号的最大数目。
*第二阶段(接受阶段)
申请人:
- 首先,依次接收响应:
- 如果收到拒绝,将不处理。
- 如果你收到了“同意”,同时也收到了“接受人”已经接受的建议书,记下建议书和编号。
- 处理完回应后,计算拒绝和同意的数量:
- 如果大多数人拒绝,为下一个提案做准备。
- 如果大多数人同意,从这些Acceptors接受的提案中选择提案号最大的提案作为提案,不使用自己的提案,逐个向Acceptor发送Accept消息。
受体:
- 如果收到的提案编号n小于它收到的最大提案编号。
- 如果收到的提案编号n等于它收到的最大提案编号,该提案将被接受。
- 如果接收到的提案号n大于它接收到的最大提案号。
- 形成共识(与准备和接受阶段平行)
受体:
- 每当一个提案被接受,提案和编号就会被发送给学习者。
学习者:
- 记录每个接受人当前接受的提案。如果Acceptor连续发送多个提案,则保留数量最大的提案。
- 计算接受每个提案的接受人数。如果有一半以上的人被接受,就会形成共识。
原文:https://medium.com/@mena.meseha/understanding-of-consistency-in-distributed-systems-27da174cc05a
本文:http://jiagoushi.pro/node/1385
讨论:请加入知识星球【首席架构师圈】或者小号【jiagoushi_pro】或者QQ群【11107777】
- 84 次浏览
【软件架构】支持大规模系统的设计模式和原则
今天,即使是小型初创公司也可能不得不处理数 TB 的数据或构建支持每分钟(甚至一秒钟!)数十万个事件的服务。所谓“规模”,通常是指系统应在短时间内处理的大量请求/数据/事件。尝试以幼稚的方式实现需要处理大规模的服务,在最坏的情况下注定要失败,或者在最好的情况下代价高昂。
本文将描述一些使系统能够处理大规模的原则和设计模式。当我们讨论大型(而且大多是分布式)系统时,我们通常通过查看三个属性来判断它们的好坏和稳定性:
- 可用性:系统应该尽可能地可用。正常运行时间百分比是客户体验的关键,更不用说如果没有人可以使用应用程序就没有用。可用性用“9”来衡量。
- 性能:即使在重负载下,系统也应该继续运行并执行其任务。此外,速度对于客户体验至关重要:实验表明,它是防止客户流失的最重要因素之一!
- 可靠性:系统应该准确地处理数据并返回正确的结果。一个可靠的系统不会静默失败或返回不正确的结果或创建损坏的数据。一个可靠的系统以一种努力避免故障的方式构建,当它不可能时,它会检测、报告,甚至可能尝试自动修复它们。
我们可以通过两种方式扩展系统:
- 垂直扩展(纵向扩展):将系统部署在更强大的服务器上,这意味着一台具有更强 CPU、更多 RAM 或两者兼有的机器
- 横向扩展(横向扩展):将系统部署在更多服务器上,这意味着启动更多实例或容器,使系统能够服务更多流量或处理更多数据/事件
纵向扩展规模通常不太可取,主要是因为两个原因:
- 它通常需要一些停机时间
- 有限制(我们不能“永远”扩大规模)
另一方面,为了能够扩展系统,它必须具有允许这种扩展的某些特性。例如,为了能够水平扩展,系统必须是无状态的(例如,大多数数据库不能横向扩展)。
本文的目的是让您体验许多不同的设计模式和原则,这些模式和原则使系统能够横向扩展,同时保持可靠性和弹性。 由于这种性质,我无法深入研究每个主题,而只是提供一个概述。 也就是说,在每个主题中,我都尝试添加有用的链接,指向关于该主题的更全面的资源。
所以让我们深入研究吧!
幂等性
这个术语是从数学中借来的,它被定义为:
f(f(x)) = f(x)
这乍一看可能有点吓人,但背后的想法很简单:无论我们调用函数 f
on x
多少次,我们都会得到相同的结果。 此属性为系统提供了极大的稳定性,因为它允许我们简化代码,也使我们的操作生活更轻松:可以重试失败的 HTTP 请求,并且可以重新启动崩溃的进程而无需担心副作用。
此外,一个长时间运行的作业可以被分成多个部分,每个部分都可以是自己幂等的,这意味着当作业崩溃并重新启动时,所有已经执行的部分都将被跳过(可恢复性)。
拥抱异步
当我们进行同步调用时,执行路径会被阻塞,直到返回响应。 这种阻塞有资源开销,主要是内存和上下文切换的成本。 我们不能总是只使用异步调用来设计我们的系统,但是当我们可以让我们的系统更高效时。 一个展示异步如何提供良好效率/性能的示例是 Nodejs,它具有单线程事件循环,但它正在与许多其他并发语言和框架进行斗争。
健康检查
这种模式特定于微服务:每个服务都应该实现一个 /health 路由,该路由应该在系统快速运行后很快返回。 假设一切正常,它应该返回 HTTP 代码 200,如果服务出现故障,它应该返回 500 错误。 现在,我们知道一些错误不会被健康检查发现,但假设处于压力下的系统会运行不佳并成为潜在的,它也会被健康检查反映出来,这也会变得更加潜在,这也可以帮助我们识别 存在问题并自动生成待命人员可以接听的警报。 我们也可以选择暂时将节点从队列中移除(参见下面的服务发现),直到它再次稳定为止。
断路器
断路器是从电力领域借用的术语:当电路闭合时,电流正在流动,当电路打开时,电流停止。
当一个依赖不可达时,所有对它的请求都会失败。 根据 Fail Fast 原则,当我们尝试调用时,我们希望我们的系统快速失败,而不是等到超时。 这是断路器设计模式的一个很好的用例:通过使用断路器包装对函数的调用,断路器将识别对特定目的地(例如特定 IP)的调用何时失败,并开始失败 调用而没有真正进行调用,从而使系统快速失败。
断路器将保持一个状态(打开/关闭),并通过每隔一段时间重试一次实际调用来刷新其状态。
Netflix 的 Hystrix 库中引入并广泛采用了断路器的实现,如今在其他库中也很常见。
终止开关/功能标志
今天的另一种常见做法是对新功能执行“静默部署”。 它是通过使用 if 检查功能标志是否已启用(或者,通过检查相关的 kill-switch 标志是否已禁用)的条件来控制功能来实现的。 这种做法并不能 100% 保证我们的代码没有错误,但它确实可以降低将新错误部署到生产环境的风险。 此外,如果我们启用了功能标志并且我们在系统中看到了新错误,则很容易禁用该标志并“恢复正常”,这从操作的角度来看是一个巨大的胜利。
舱壁(Bulkhead)
隔板是船底隔间之间的分隔墙或屏障。 它的工作是隔离一个区域,以防底部有洞——以防止水淹没整个船(它只会淹没有洞的隔间)。
通过在考虑模块化和隔离的情况下构建软件,可以将相同的原则应用于软件。 一个例子可以是线程池:当我们为不同的组件创建不同的线程池以确保耗尽其中一个中的所有线程的错误时 - 不会影响其他组件。
另一个很好的例子是确保不同的微服务不会共享同一个数据库。 我们还避免共享配置:不同的服务应该有自己的配置设置,即使它需要某种重复,以避免一个服务中的配置错误影响不同服务的情况。
服务发现
在动态的微服务世界中,实例/容器来来去去,我们需要一种方法来了解新节点何时加入/离开队列。 服务发现(也称为服务注册)是一种通过允许节点在中央位置(如黄页)注册来解决此问题的机制。 这样,当服务 B 想要调用服务 A 时,它会首先调用服务发现来请求可用节点 (IP) 的列表,它将缓存并使用一段时间。
超时、睡眠和重试
任何网络都可能遭受瞬时错误、延迟和拥塞问题。 当服务 A 调用服务 B 时,请求可能会失败,如果发起重试,则第二个请求可能会成功通过。 也就是说,重要的是不要以简单的方式(循环)实现重试,而不是“烘焙”到重试之间的延迟机制(也称为“睡眠”)。 原因是我们应该意识到被调用的服务:可能有多个其他服务同时调用服务B,如果它们都继续重试,结果将是“重试风暴”:服务B会 被请求轰炸,这可能会使它不堪重负并使其崩溃。 为了避免“重试风暴”,通常的做法是使用指数退避重试机制,该机制会在重试之间引入指数增长的延迟,并最终导致“超时”,这将停止任何额外的重试。
后备
有时我们只需要一个“B计划”。 假设我们正在使用推荐服务,以便为客户获得最佳和最准确的推荐。 但是,当服务出现故障或暂时无法访问时,我们能做些什么呢?
我们可以有另一个服务作为后备:其他服务可能会保留我们所有客户的推荐的快照,每周刷新自己,当它被调用时,它需要做的就是返回该特定客户的相关记录。 这种信息是静态的,易于缓存和服务。 这些后备建议确实有点陈旧,但是拥有不是完全最新的建议总比没有任何建议要好得多。
优秀的工程师在构建系统时会考虑这些选项!
请注意,断路器实现可能包括提供后备服务的选项!
指标、监控和警报
在运行大规模系统时,不是系统是否会失败的问题,而是系统何时会失败的问题:由于规模大,即使是百万分之一的罕见事件也会发生。最终发生。
既然我们理解并接受错误是“生活的一部分”,我们就必须找出处理它们的最佳方法。
为了拥有一个可靠的可用系统,我们需要能够快速检测(MTTD)和修复(MTTR)错误,为此,我们需要获得对系统的可观察性。这可以通过发布指标、监控这些指标并在我们的监控系统检测到“关闭”的指标时发出警报来实现。
Google 将 4 个指标定义为黄金信号,但这并不意味着我们不应该发布其他指标。我们可以将指标分为 3 个桶:
- 业务指标:源自业务上下文的指标,例如,我们可能会在每次下订单、批准或取消订单时发布指标
- 基础设施指标:衡量我们部分基础设施的大小/使用情况的指标,例如,我们可以监控我们的应用程序使用的 CPU 使用率、内存和磁盘空间
- 功能指标:发布有关我们系统中特定功能的信息的指标。一个示例可以是在我们正在运行的 A/B 测试中发布的指标,以提供有关分配到实验不同单元的用户的见解
小轶事:在我为 Netflix 工作的日子里,我和我的团队所做的一件事是开发 Watson,使团队能够通过创建程序化运行手册从已知场景中自动修复他们的服务!
限速
速率限制或节流是另一种有助于减轻系统压力的有用模式。
节流有 3 种类型:
- 用户限速(客户端)
- 服务器限速和
- 地理限速
背压
背压是一种用于处理来自上游服务的请求负载高于处理能力的情况的技术。 处理背压的一种方法是向上游服务发出信号,告知它应该对自身进行速率限制。
有一个专用的 HTTP 响应代码 429“请求过多”,旨在向客户端发出信号,表明服务器尚未准备好以当前速率接受更多请求。 这样的响应通常会返回一个 Retry-After 标头,以指示客户端在重试之前应该等待多少秒。
处理背压的另外两种方法是限制(也称为“在地板上抛出请求”)和缓冲。
可以在此处找到有关背压的其他推荐阅读。
金丝雀发布
金丝雀测试是一种用于将更改逐步推广到生产环境的技术。当监控系统发现问题时——金丝雀会自动回滚,对生产流量的损害最小。
请记住,为了启用金丝雀发布,我们需要能够与“正常”节点分开监控金丝雀集群,然后我们可以使用“常规”节点舰队作为基线,并将其与我们收到的指标进行比较从金丝雀。例如,我们可以比较我们在两者中收到的 500 个错误率,如果金丝雀产生更高的错误率,我们可以回滚它。
还有一种更保守的方法是使用生产中的影子流量来做金丝雀。
我以前的一位同事 Chris Sanden 与人合着了一篇关于 Kayenta 的好文章:一种在 Netflix 中开发的用于自动金丝雀分析的工具。
今天的内容就到这里了,希望大家能学到新东西!
如果你认为我错过了一个重要的模式/原则——请写评论,我会添加它。
- 85 次浏览
【软件设计】系统设计面试基础:CAP 与 PACELC
什么是 CAP 定理以及 PACELC 如何扩展它?
在分布式系统中,可能会发生不同类型的故障,例如,服务器可能会崩溃或永久故障,磁盘可能会损坏导致数据丢失,或者网络连接可能会丢失,导致系统的一部分无法访问。分布式系统如何对自身进行建模以从不同的可用资源中获得最大收益?帮助分布式系统在各种分布式特性之间选择理想平衡的指导原则是什么?
检查 Grokking the System Design Interview 以了解重要的分布式系统概念。
CAP 定理
CAP 定理指出,分布式系统不可能同时提供以下所有三个理想属性:
- 一致性(C):所有节点同时看到相同的数据。这意味着用户可以读取或写入系统中的任何节点并接收相同的数据。它相当于拥有一个最新的数据副本。
- 可用性(A):可用性是指系统中非故障节点收到的每个请求都必须产生响应。即使发生严重的网络故障,每个请求也必须终止。简单来说,可用性是指即使系统中的一个或多个节点出现故障,系统仍保持可访问性的能力。
- 分区容差(P):分区是系统中任意两个节点之间的通信中断(或网络故障),即两个节点都已启动但无法相互通信。即使系统中有分区,分区容错系统也会继续运行。这样的系统可以承受任何不会导致整个网络故障的网络故障。数据在节点和网络的组合之间得到充分复制,以使系统在间歇性中断时保持正常运行。
根据 CAP 定理,任何分布式系统都需要从三个属性中选择两个。三个选项是 CA、CP 和 AP。但是,CA 并不是一个真正的连贯选项,因为在网络分区的情况下,不能容忍分区的系统将被迫放弃一致性或可用性。因此,该定理实际上可以表述为:在存在网络分区的情况下,分布式系统必须选择一致性或可用性。
CAP 定理的证明
我们无法构建一个持续可用、顺序一致且能容忍任何分区故障的通用数据存储。我们只能构建具有这三个属性中的任意两个的系统。因为,为了保持一致,所有节点都应该以相同的顺序看到相同的更新集。但是,如果网络丢失了一个分区,则一个分区中的更新可能无法在客户端读取最新分区后从过期分区读取之前到达其他分区。应对这种可能性的唯一方法是停止为来自过期分区的请求提供服务,但随后该服务不再 100% 可用。
CAP 定理缺少什么?
我们无法避免分布式系统中的分区;因此,如上所述,根据 CAP 定理,分布式系统应该在一致性或可用性之间进行选择。 ACID(原子性、一致性、隔离性、持久性)数据库,例如 MySQL、Oracle 和 Microsoft SQL Server 等 RDBMS,选择一致性(如果无法与对等方检查,则拒绝响应)。相比之下,BASE(基本可用、软状态、最终一致)数据库,例如 MongoDB、Cassandra 和 Redis 等 NoSQL 数据库,选择了可用性(响应本地数据,但不确保它是最新的)。
CAP 定理沉默的一个地方是当没有网络分区时会发生什么?在没有分区的情况下,分布式系统有哪些选择?
救援 PACELC 定理
PACELC 定理指出,在复制数据的系统中:
如果存在分区(“P”),分布式系统可以在可用性和一致性(即“A”和“C”)之间进行权衡;
else(‘E’),当系统在没有分区的情况下正常运行时,系统可以在延迟(‘L’)和一致性(‘C’)之间进行权衡。
定理的第一部分(PAC)与CAP定理相同,ELC是扩展。整篇论文假设我们通过复制来保持高可用性。因此,当出现故障时,CAP 定理占上风。但如果不是,我们仍然需要考虑复制系统的一致性和延迟之间的权衡。
例子
- Dynamo 和 Cassandra 是 PA/EL 系统:它们在发生分区时选择可用性而不是一致性;否则,他们会选择较低的延迟。
- BigTable 和 HBase 是 PC/EC 系统:它们总是会选择一致性,放弃可用性和更低的延迟。
- MongoDB 可以被认为是 PA/EC(默认配置):MongoDB 在主要/次要配置中工作。在默认配置中,所有写入和读取都在主节点上执行。由于所有复制都是异步完成的(从主节点到辅助节点),当存在主节点丢失或在少数节点上被隔离的网络分区时,可能会丢失未复制到辅助节点的数据,因此会丢失分区期间的一致性。因此,可以得出结论,在网络分区的情况下,MongoDB 选择可用性但其他方面保证一致性。或者,当 MongoDB 配置为在多数副本上写入并从主副本上读取时,它可以归类为 PC/EC。
结论
CAP 和 PACELC 定理帮助分布式系统在各种分布式特性(如一致性、可用性、分区容限和延迟)之间选择理想的平衡。请查看 Grokking the System Design Interview 和 Grokking the Advanced System Design Interview 以获得一些系统设计基础知识的好例子。
原文:https://medium.com/interviewnoodle/system-design-interview-basics-cap-v…
- 97 次浏览
分布式计算框架
分布式计算框架的目标是为开发人员提供抽象,允许他们在将它们视为统一池时使用大量资源。该框架将提供一个协议来处理节点上的数据和任务分布,以及容错问题,例如重新启动失败的任务(无论是来自任务还是来自节点)。该框架不了解底层拓扑,必须应对基础架构演变。诸如MapReduce和Spark之类的流行框架还提供了一种编程模型,该模型允许将大型数据集推理为统一部分(位置透明性),并将其逻辑地分割为由群集节点独立处理的多个组件。数据集本身通常存储在分布式文件系统上,在处理任务的同一节点上;开发人员可以控制其分区,并创建一个应用于每个部分的计算管道。
MPI
消息传递接口是一种独立于语言的标准,允许程序与系统中的其他元素进行通信。 MPI在分布式计算系统中提供基本构建块,即允许程序访问计算机或远程机器中的分布式存储器的消息传递协议。但是,它需要用户在她的代码中实现消息传递,其中更高级别的框架(如Spark和Hadoop)可以透明地处理。结果是用户必须更多地了解底层基础架构,从而分散他们对程序的注意力。 MPI通常被归类为高性能计算(HPC)框架,而不是分布式计算框架,但它与后者共享许多属性。
Hadoop
Apache Hadoop是MapReduce论文的开源实现[1],该论文于2004年首次在OSDI上发布。该项目由Doug Cutting和Mike Cafarella于2006年开始,受雇于雅虎!当时。 Apache发行版的第一个正式版本于2011年发布。
Hadoop由三个主要构建块组成:分布式文件系统,HDFS(Google File System的开源实现[2]),MapReduce API和YARN [3],即调度程序。
MapReduce编程模型来自函数式编程:用户可以将函数应用(映射)到一组独立的数据段,然后聚合中间结果(reduce)。一个典型的例子是WordCount的实现,其中用户想要知道语料库中每个单词的出现次数。首先,文本被分成一定数量的分区,每个分区被映射到一个功能,该功能在其分配的分区中操作本地字数。然后,一旦完成map(),所有单独的中间结果将在reduce()阶段中连接和合并。
通常,Hadoop的Map Reduce实现将数据存储在HDFS中,这可确保有足够数量的块可用并传播到整个群集中。 YARN实现了一种称为延迟调度的技术[4],它试图通过降低网络I / O的成本来最大化数据局部性以增强性能。我们将在本章后面讨论进一步的数据局部性和延迟调度。
HDFS体系结构是主/从范例,其中主服务器保存有关数据放置和授权的所有信息,而从服务器存储数据块并定期向主服务器报告状态。为了确保容错和可用性,每个从站都有指定数量的副本(默认为3个),这些副本位于不同的物理位置,以增强其对物理故障的恢复能力。为了确保一致性,每组副本都有一个主副本,它验证(或拒绝)一段数据上的一系列突变。执行一组突变会使数据定义,这意味着该版本被确认为最终版本。
Spark
Apache Spark [5]属于新一代分布式计算框架。 Spark背后的一个关键动机是通过在内存计算中执行来增强迭代工作负载性能。原始Hadoop实现对于那些工作负载来说是非常低效的,主要是由于对将应用程序运送到其末端所需的大量磁盘访问。 Spark做了两个主要的贡献来解决这个问题:首先它引入了一种新型的数据结构,称为Resilient Distributed Dataset [6],它在内存计算中起着重要作用;第二,它提出了一个丰富的软件架构,可以轻松创建适用于各种类别的分布式计算程序,例如图形处理,机器学习和流媒体。
RDD是不可变的数据集合,用户可以从初始存储(HDFS,远程对象存储,本地文件等)创建数据,并通过转换进行修改。在某些时候,用户可以通过收集方法收集由这种转换生成的新数据。 RDD允许实现MapReduce模型的精炼版本,丰富各种可能的用户定义函数,并允许它们在数据集上创建真实的计算管道。 RDD是懒惰计算的,这意味着只有当用户想要收集数据时,才会应用转换。每个RDD都有一个谱系,这意味着应用于数据集初始切片的操作序列是已知和记录的。 Spark中的任务是对RDD的操作。每个操作在RDD的分区上,并且任务之间的关系是并发的或依赖的。任务(转换或集合)的本质定义了两种类型的依赖:narrow和wide。窄依赖性通常是RDD上两个连续map()函数的结果。广泛的依赖性通常会导致混乱,这意味着中间数据必须通过集群移动才能减少()。
在编译时,Spark分析应用于程序中RDD的操作,并生成有向非循环图。它试图在一个阶段中包含尽可能多的窄依赖关系,并且通常将广泛依赖关系的两端放入不同的阶段。优选地,通过称为延迟调度的技术将任务分配给持有所需数据的工作人员。它允许任务在不保存所需数据的情况下拒绝工人的报价。配置了不同的位置级别,节点,机架和群集位置,允许Spark逐渐增加任务对非位置的容忍度。即使在发生离散故障的情况下,通过重放RDD的谱系,框架也可以进行计算。
RDD可以在专用类型中实例化,具体取决于它们存储数据的计算类型。例如,存在图RDD,文件RDD和列存储RDD。
- 74 次浏览
【BPM架构】BPM 平台:独立还是微服务实现
介绍
BPM 是一个描述、建模和管理复杂业务流程的概念。使用 BPMN,我们可以轻松定义流程中的顺序,编排多个任务、决策和事件。有许多 IT 平台可以将 BPMN 设计变成工作代码。事实证明,协调服务、系统和业务任务的 BPM 模型和支持 IT 平台是实现业务流程的可靠来源。
那就是微服务出现的时候。也就是说,松散耦合的、基于事件的服务,旨在实现特定的业务功能,通过事件进行通信,并实现编排消息传递模型。微服务是否意味着 BPM 平台的终结?或者恰恰相反——像 Camunda 这样的 BPM 平台能否在复杂业务流程的微服务整合中发挥关键作用?
BPM 实施模型
当公司准备好启动 BPM 计划时,第一个决定是选择合适的实施模型和合适的 BPM 平台。
首先,让我们讨论该模型,它将定义整个 BPM 倡议方法本身。这是一个关键决策,需要深思熟虑,因为它将定义整个组织将如何创建和实现业务流程。有两种最流行的建模方法:
- BPM 平台可以是一个单一的 IT 系统,它将在一个地方为业务流程编排和配置规则。
- BPM 引擎可以是微服务的一部分,包含特定的子流程。这些微服务及其子流程将使用编排通信模式整合到业务流程中。
Camunda BPM Platform 可以从技术和业务角度实现这两种方法。这就是在 BlueSoft 中我们推荐该软件作为我们项目中主要 BPM 工具的主要原因之一,我们将使用它作为进一步概念的示例。
Camunda BPM 作为业务流程管理单体
自第一个 BPM 平台出现以来,这种方法已在许多组织中实施。它通常用于将集成层中的 ESB 服务编排成流程引擎层中定义良好的业务流程。我们可以从两个角度看待这种架构——业务和功能以及技术。
业务与功能视角
从业务和功能的角度来看,最重要的是业务流程本身。 Camunda 作为业务流程实现的核心,是业务定义规则和监控流程的第一层。每个业务流程都有其负责人负责业务成果和流程执行的可靠性。在流程引擎的顶部,作为最终用户的网关,通常有多个前端和门户,用户决定业务流程的实现。对于业务视角,集成层 API 就像负责数据供应和请求履行的“黑盒”,业务流程所有者不对其行为和数据管理负责。
正如我们所见,IT 团队和业务团队之间的合作主要集中在流程引擎上——而业务流程负责人定义需求和流程模型,而工程师则致力于他们的技术实现。集成和数据治理团队支持流程团队,但他们不负责业务流程和业务功能。
技术视角
从技术的角度来看,有一个多层的、集成的 IT 架构,它提供了实现业务流程实现的功能。 最重要的是,有可以使用多种不同技术(Javascript、PHP、Angular、React 等)交付的前端。 在较低级别,有 Camunda,它是业务流程定义的中心。 它存储业务规则、决策图表并在前端编排后端集成层功能和 UI 表单。 集成层是技术服务供应的中心。 在实践中,像 MuleSoft、WebMethods、Oracle ESB 等 ESB 平台通常是集成层。 他们的目标是为流程引擎提供具有标准化数据模型的 API。 该层负责公开多个系统功能、集成数据、统一协议、管理服务治理和编排简单的技术处理。
优点与挑战
优点 | 挑战 |
这种方法的重要好处是在一个地方定义和监控整个业务流程的简单性。 Camunda BPM Engine 可以轻松跟踪流程,即使是复杂的流程。 | 变更管理——在变更流程时,可能需要协调前端和集成层中的多项调整。 这些层通常由不同的技术(甚至业务)团队负责,从而使此类更改成为多个团队之间的多层计划。 |
决策规则、任务和业务流程定义在一个平台上处理,业务团队可以使用 Camunda Modeler 设计流程和 Camunda Task List 来完成处理。 | 数据所有权和治理。 业务流程所有者只负责流程实现和结果,而系统所有者和集成架构师只关心数据治理和一致性以及从技术角度来看 IT 系统的可靠性。 它可以围绕稳定、可靠的平台引发利益冲突,并推动业务流程调整。 |
IT 工程师也从他们的编码过程开始使用相同的 Camunda Modeler,因此团队之间在整个过程设计和实施方面的误解空间有限。 | 由于技术故障和安全方面的原因,拥有一个定义所有业务规则和流程的地方可能会带来潜在的风险。 |
微服务架构中的 Camunda BPM
微服务架构引入了一种不同的 IT 系统设计方法,其中具有大量业务功能的大型单一单体被专为业务目的设计的较小的自主服务所取代。这样的设计会对业务流程的实施方式产生影响。我们也可以从业务&功能和技术角度来看架构设计,但它们在一个微服务中密切相关,为一个业务领域提供业务功能。
业务与功能视角
从业务和功能的角度来看,将业务流程分解为更小的子流程非常重要,这些子流程专注于在一个业务对象中提供价值和决策。由于没有集中的业务流程引擎,这些子流程对事件流层中的事件做出反应。然后,他们启用自己的业务逻辑并为其他子流程生成结果事件。与 Camunda Monolith BPM Platform 不同,跟踪业务流程实现是在两个层面上完成的:在 Camunda Engine 中的微服务层面提供特定功能,以及在事件流层中跟踪子流程之间的事件。这种流程管理导致不同的组织模型——我们仍然有业务流程所有者,但他们在管理和监控整个流程本身方面的责任较少。相反,他们像乐高积木一样构建他们的流程,使用为组合提供小型子流程的微服务。微服务团队有负责端到端数据管理、与遗留系统和外部系统集成、业务子流程实现甚至最终用户 UI 的领导——无论是从技术角度还是业务角度。这种方法导致小型、敏捷、独立的团队与业务专家和 IT 工程师相结合。
这个概念比经典的分层架构具有更大的灵活性——但也让提供每个业务功能的团队承担更多责任。它可以选择适当的技术、语言、数据库和框架来有效地满足需求。团队全权负责完成业务服务,并与技术和业务专家结合,密切合作——从数据定义开始,到业务处理,最后到最终用户的 UI 表示。
技术视角
从技术的角度来看,任何分解的子流程都会成为一组在微服务的业务层中实现的功能——在我们的示例中,一个用于客户数据更新,另一个用于风险计算更新。每个微服务都有自己的数据存储和结构,自己的集成 API 层,自己的 Camunda 引擎来实现子流程,甚至自己的 UI 表示。每一层都可以用不同的技术编写——但是在业务层中坚持使用 Camunda 对于构建跟踪和监控整个业务流程的外部架构很有用。子流程通信是通过在一个地方发布事件来完成的,其他子流程也在事件流层中发布和消费事件。在这个架构中至关重要的是,Event Streaming Layer 只是事件共享的管道,不包含任何消息编排逻辑。用于此目的的最常用技术是分布式流和队列平台,如 Kafka、Pulsar 或 Rabbit MQ。
优点与挑战
优点 | 挑战 |
业务流程和技术组合变化的灵活性。 业务流程实现被分成小的、独立的、功能性的元素,当新的需求出现时更容易调整。 关注这些功能服务的专家团队有责任、知识和权力不断改进它们,从而消除分层架构中单独的技术和业务组之间潜在的利益冲突。 工程师和业务部门在 Camunda BPM 上一起工作的好处更加明显,因为在这种模式下,这些专家专注于业务流程的一小部分,因此他们之间可以更有效地共享技术和业务观点。 | 由松散耦合的子流程组成的业务流程并不容易跟踪和监控。 使用一个技术堆栈——Camunda BPM——构建业务处理可以简化这部分,但它仍然不如在 BPM Monolith IT Platform 中跟踪那么简单。 |
技术的灵活性。 在 BPM Monolith Platform 中,当当前的解决方案从技术角度来看已经过时或难以在特定业务需求中使用时,有一个很大的举措是重新编写在那里实现的整个业务流程。 借助微服务和分布式子流程,我们可以逐步规划这样的举措,甚至可以尝试多种技术以选择最佳技术。 | 业务服务组合管理。 借助集成平台,所有外部系统功能都具有代表性的 ESB 服务,易于通过标准化合同使用。 对于微服务,每一个都暴露了功能性 API,因此制定治理规则至关重要,不仅要规定如何构建和使用它们,还要规定在哪里可以找到它们。 |
错误的技术决策或重新实施整个业务流程中的人为错误的风险非常低。 使用这种方法,即使您认为 Camunda BPM 不再满足所有需求,也可以轻松地以小功能块迁移到其他解决方案。
|
遗留系统与微服务共存的情况可能具有挑战性。 微服务团队和遗留系统业务团队应该围绕数据一致性和数据所有权展开合作。 事实上,拥有微服务迟早会导致遗留系统分解,这对整个组织和 IT 系统管理都是一个挑战。 |
结论
重要的是要记住,并非每个 BPM 平台都可以实现微服务和独立实现模式。使用无代码平台,可能很难在微服务内部实现完整的业务逻辑,因此它们将作为业务流程管理的单一平台更好地工作。使用低代码平台,我们失去了 BPMN 图驱动的开发,只依赖于工程师和业务专家之间的密切理解。这种理解只发生在微服务团队中。 BPM 平台在这里是最灵活的。它们将这两个好处结合在一起:业务分析师的 BPM 图表建模工具,感谢 IT 工程师,它变成了工作代码。 Camunda BPM 是一个平台,可用于两种实现模型。
在 BlueSoft 中,我们设法将 Camunda BPM 合并到两种实施模型中:
- BPM & Microservices 并以 Trans.eu 架构为例:https://bluesoft.com/project/microservices-based-integration-platform-f…。
- BPM 作为一个简单的业务流程管理工具集,以支持在线商店和 Polarbulk 架构为例:https://bluesoft.com/project/innovative-one-click-purchase-robot-for-po…
您想知道使用 BPM 解决方案是什么样的吗?阅读这篇文章,了解有助于充分利用 Camunda BPM 并避免常见陷阱的关键最佳实践:https://bluesoft.com/camunda-bpm-best-practices/
原文:https://bluesoft.com/bpm-platforms-standalone-microservices-implementat…
- 114 次浏览
【BPM架构】Camunda BPM 最佳实践
介绍
BPM 平台是 BPMN 图成为工作代码的引擎。有许多产品实现了这些概念。其中一些被宣传为低代码,无需任何程序员帮助即可供企业使用。其中一些只是 Java 库,支持软件开发人员级别的业务流程实现。他们中的许多人都在努力获得简单性和 BPMN 驱动的代码,以实现复杂的、特定的要求和量身定制的解决方案。在众多平台中,Camunda BPM 作为一个平台脱颖而出,它是无代码简单性和低代码能力之间的诚实折衷。无论您选择哪种实施模型(在此处了解有关实施模型的更多信息:BPM 平台:独立和微服务实施),业务分析师和 BPM 平台程序员都可以在同一个 Camunda 项目上一起工作。
BPM 平台“圣杯”:无代码概念
我什么时候需要程序员?
现在,您可能想知道:“如果存在无代码 BPM 平台——我为什么还需要程序员?有许多工具被宣传为无代码概念,其中业务流程专家是设计和实施端到端流程的人。”答案很简单:您不需要程序员,如果您的 BPM 平台仅用于一个业务单元中非常简单的流程实现,无需数据集成。如果您想在组织级别实施业务流程,这对业务至关重要并且需要数据集成,那么没有无代码 BPM 平台可以满足您的需求。
在 BlueSoft 中,我们推荐 Camunda BPM 作为简单的、UI 驱动的业务流程设计(从无代码平台已知)和在 IT 工程师的帮助下实施数据集成和复杂业务规则的能力之间的最佳权衡。 Camunda BPM 的巨大优势在于,由业务专家完成的流程设计是 IT 工程师也在处理的代码的一部分。
实施 Camunda BPM 流程时的最佳最佳实践
现在,当我们知道如何建立在 Camunda BPM 中工作的团队时,让我们专注于业务专家和 IT 工程师在建模流程方面的最佳实践和工具。
当我们考虑流程建模时,我们有很多方法和工具来表达自己。它们由 BPMN 2.0 标准提供:流程应该如何工作以及它应该如何与其他微服务或遗留系统进行通信。不幸的是,在技术实现方面,标准方法是“少即是多”。作为程序员,我们习惯于遵守这条规则,但即便如此——这也不是不争的事实。
Camunda Modeler 的建模过程也是如此。常见的方法是仅使用主路径中的部分。但是,就从项目的其他贡献者的角度对过程的理解而言,我们需要在分析部分给他们一点帮助。在这种情况下,通道和外部系统调用就派上用场了。我们添加这些注释而不影响 Camunda 引擎处理流程 .bpmn 文件的方式。
现在,让我们试着设身处地为业务分析师着想。当试图仅使用主通道(示例图中的销售流程)来理解流程时,我们根本不知道这两个服务任务究竟做了什么。可以有一个逻辑调用内部数据库,或者从缓存中访问数据,或者从初始过程数据中计算一些东西。但是当所有部分都存在时,我们清楚地看到这些步骤调用了外部系统。我们甚至知道他们对外部系统使用了哪些特定的 REST 请求!
在对流程进行整体分析时,公司从上述方法中受益。这种方法可以作为设计高级业务流程时的第一个表达工具。然后可以将 .bpmn 文件发送给开发团队,作为开始使用的输入文件。
活动实施原则
当谈到 BPMN 流程编程的可读性时,原则就派上用场了。最常见的反模式是打破 SOLID 原则的第一条规则——“单一责任模式”。这是当今编程世界最重要的原则之一。它指出单个类或包应该只负责解决一个问题。它影响从低级类实现到高级架构设计的所有概念决策。就过程的长期开发和维护而言,步骤应尽可能简单。它应该只负责调用外部系统、为最终用户提供表单或计算收集的数据。
一起实现多个外部调用或在一个步骤中计算流程的所有数据是最常见的错误。即使该流程最初是由业务分析师以这种方式设计的,开发团队也有责任将这一业务步骤拆分为多个技术步骤,并保留原始业务描述。
最好的防线是坚持总体流程——当然,这只是总体思路的基本可视化:
- 第 1 步:从外部系统调用中获取数据
- 第 2 步:计算此数据,对其进行转换等。
- 第 3 步:使用已处理数据中的手动任务为最终用户提供表单。重要提示——不要试图在这部分中包含一种计算形式!对于字典等,尝试对表单进行建模以使用前端-后端 API。
- 第 4 步:保存用户表单中的数据并将其转换为流程模型(如果保存表单数据是唯一的选项,则从附加流程返回第 3 步)
- 重复一般的想法
请记住将可配置性带到步骤中
在 Camunda 中实施流程过程中的另一个重要事项是 SOLID 的“开闭”原则。有些步骤与流程非常相关,没有理由使外部配置成为可能。但其中许多步骤,即使涉及与其他系统的集成,也可以在流程的不同部分或流程的不同部分重复使用。为了实现这一点,我们应该使用元素模板(https://github.com/camunda/camunda-modeler/tree/develop/docs/element-te…)。 Modeler 的用户可以更灵活地重用流程步骤。当然,它需要为每个活动实现一点可配置性。在访问常见的产品数据、发送电子邮件或推送到客户的移动应用程序时,如果您为该步骤提供更多可配置性,那么游戏可能是得不偿失的。
异常处理和超时
在实施之前,我们可能需要更多的时间来分析和设计所有流程的所有出口点。特别是识别来自外部系统调用的所有异常或错误代码起着至关重要的作用。我们建议为每个流程制作一个专用矩阵。最后但同样重要的是,我们需要设计流程应该如何响应这些异常。有两种常见的方法:
- 第一个是将所有步骤回滚到前一个事务点。通常,这些将是人工手动任务或事件处理程序。这种行为很容易实现,但需要在下一次重试流程中覆盖对外部系统的所有数据更改。当然,这些更改不会影响相应系统中的任何业务相关流程)。
- 第二种是使用默认的 Camunda 的“重试和等待”机制。当 Camunda 尝试重复该步骤(默认 3 次)然后抛出异常等待管理员的操作时。当由于某些业务案例(例如,客户已经为产品付款,因此没有回头路)而难以实施甚至不可能回滚时,这是一种合适的方法。在这种情况下,必须考虑外部作业或 API 调用,以便在修复错误或系统重新联机时自动执行重试过程。这通常是指补偿流量。
- 最后,我们应该考虑进程超时的问题。在实际的行业案例中,大多数流程都应该有一个计时器,当客户没有反应时,它会结束它们。没有它,未完成流程的数量可能会不断增长,并扩展到数十万个。在大多数示例中,计时器仅分配给人工任务。这是一种有效且常见的方法,但是当我们需要在每一步都升级时怎么办?或者计时器应该是全局的?在这种情况下,全局处理程序或升级处理程序应该使用 BPMN 流程而不是纯粹的编程方法来建模,以便为业务分析师提供更清晰的信息。
避免冗长的流程
避免冗长的流程说起来很容易,但在实施时却很难获得。有时我们被迫设计流程,而人工手动任务可能需要几个月的时间。这与前面提到的微服务方法——短期流程相反。防止长期流程发生的最佳方法是将流程拆分为较小的流程,仅在当事方做出决定/输入有效数据时创建。但是,当您被迫设计和维护那些长期存在的流程时,请记住在对流程进行任何更改之前必须解决的关键问题:
- 每一条数据都可以处于任何状态并且是变化的一部分。有时不可能列出流程中的所有变量并创建升级矩阵。创建新版本流程的最佳方法是强制将所有流程移动到所需状态,并将这种方法传达给企业。
- 默认情况下,进程是版本化的。但复杂的前端表单和代码不是。当然,您可以使用 OSGi 插件系统 (https://www.osgi.org/) 和已维护的代码的几个版本,但在实际情况下,就成本而言,这是一种非常罕见的方法经过考虑的。很少有框架使用 OSGi 的所有功能(其中之一是我们的开源 BPMN 项目 - Aperte Worklow https://github.com/bluesoft-rnd/aperte-workflow-core),但即便如此,它的成本也很高为每个现有流程版本维护相同代码的多个版本。
- BPMN 系统不是面向表单的门户。它们强制特定的数据状态提供验证和流动。但正因为如此,当这个流程和数据发生变化时,它们很难维护。最简单的方法是在新版本的生产发布之前强制完成所有流程。在某些情况下,更改与可以使用单个脚本转换的其他步骤和数据有关。但是在这些情况下,当流程必须保持当前状态时,分析人员必须创建“数据矩阵”,即数据作为一个维度呈现,当前状态作为另一个维度呈现。并且您应该始终分析在引入使用历史数据状态的代码的新版本时该过程将如何进行。
更多最佳实践。
Camunda 的官方文档是最佳实践的重要资源,我们强烈建议参与设计流程或开发团队成员的每个人仔细阅读 - https://camunda.com/best-practices/using- 我们的最佳实践/。 在这篇特别的文章中,我们想总结我们参与的许多项目的经验,以突出最常见的错误以及如何避免它们。 根据我们的经验,BPMN 设计过程并非易事,大部分知识都是在犯错时收集的,这些错误在长期使用这些过程时会发生。 我们认为,这些突出显示的要点是设计至少适当流程的关键。 对于那些与 Camunda 一起开始冒险的人来说,这样做是巨大的成功。
- 267 次浏览