分散计算模式与网络计算模式_高度分散的计算,无需同步

分散计算模式与网络计算模式

跨系统的数据同步是昂贵且不切实际的,尤其是在运行将应用程序部署在移动设备上或提供物联网(IoT)服务的机构所看到的规模的系统上时。 随着客户端数量的增加,成本不仅增加,而且当客户端访问连接的权限有限时,也无法同步操作。 这使得传统方法(例如Paxos状态机复制 )无法协调复制状态。

这些系统的高可用性和容错性也是一个主要问题,因为对于大多数这些公司来说,停机时间与收入直接相关,例如亚马逊在Dynamo上的工作中所体现的那样,他们推广了“最终一致性”的概念。作为解决问题的一种方法。 但是,在仍然可以执行有用的分布式计算的同时,可以减少多少状态是最小的。

考虑一家大型移动游戏公司,该公司需要在用户设备之间共享客户端状态:例如,特定用户拥有的所有设备之间的共享虚拟钱包,或团队设备的所有成员之间的项目共享列表。

在理想情况下,我们希望使用此共享的复制数据执行的操作在客户端脱机时能够成功。 但是,允许在共享数据结构上执行操作,同时又避免了同步,这是徒劳的,而且是错误程序的秘诀。 因此,我们旨在创建确定性应用程序,这些应用程序在通过数据结构操作以确保在并发操作的情况下会发生收敛时,会确保应用程序本身会发生收敛。

在本文的其余部分中,我们探索了制作此类应用程序的基本构建块。

无冲突的复制数据类型

无冲突的复制数据类型 (CRDT)为解决Amazon Dynamo论文中描述的“语义解决”问题提供了一种解决方案。 在对其高度可用的购物车的描述中,存在一个问题,即向复制的购物车中同时添加和删除商品会导致购物车的差异:在这种情况下,诸如版本向量向量时钟之类的因果关系跟踪机制,可以用来确定系统中事件的顺序,只能确定操作是同时发生的。

Dynamo通过存储差异项目的两个副本并在用户下次尝试检索密钥时将这两个副本返回给用户来解决此问题(注意:在原始Dynamo论文发表后浮出水面的一些Dynamo克隆也采用了这种策略,例如作为LinkedIn的Voldemort项目和Basho的Riak项目 。)

此时,用户应该解决这些冲突的写操作,并写回解决的对象。 在购物车示例中,使用集合联合操作将两个购物车连接起来以执行解析-但是,根据集合中商品的建模方式,可以在此解析逻辑下“恢复”已删除的商品。

Shapiro等人在“无冲突的复制数据类型 ”中。 制定一个最终目标强一致性(SEC)模型,如果该对象最终是一致的并且具有很强的收敛性,则该对象符合此条件。 它的收敛属性定义为已交付相同更新的正确副本具有等效状态。

在这种模型下,对象不再容易受到这些“并发异常”的影响,因为观察到强烈最终一致性的对象被设计为在并发和失败下都能正确收敛。 此属性使这些数据类型对于确保分布式系统(尤其是使用乐观复制技术的分布式数据库)中的正确性非常强大。

这些数据类型有两种形式:基于状态的(依赖半格的属性)和基于操作的(更节省空间)并且依赖于所有操作的可交换性。 这些数据类型采用原则上的方法来实现最终的一致性:通过设计,数据结构对有关用于创建事件的事件的信息进行编码,并且通过此元数据,可以确定性地解析并发操作。 在本文中,我们将重点介绍基于状态的CRDT。

因此,我们如何将无冲突的复制数据类型组合到程序中,同时确保通过此组合保留单个CRDT的强收敛性?

分布式确定性数据流

为了解决这个问题,我们转向确定性数据流编程,这是一种功能性编程,其中一系列代理或过程在共享的单分配变量存储中的变量绑定上进行同步。 下图显示了与共享约束存储进行通信的过程的示例。 (有关此模型的更多详细信息,请参见《计算机编程概念,技术和模型》的第4章。)

下图显示了与共享约束存储进行通信的过程的示例。

在此模型中,σ表示共享变量存储以及P1和P2进程。 我们的商店提供了两种原始操作: readbindread是对存储区的读取变量的阻塞操作; 该操作将一直阻塞,直到绑定了变量为止。 绑定操作允许将存储中的变量分配给特定值或另一个变量的值。

什么是联接半决赛?

如前所述,我们可以将此模型扩展到基于状态的无冲突复制数据类型。 回想一下,基于状态的无冲突复制数据类型依赖于联接符号的单调性。

那么,什么是联接半决赛? 一个join-semilattice是一个部分排序的集合,它具有一个称为join的二进制操作。 此连接操作是关联的,可交换的,和幂等并计算相对于该偏序最小上界。

举个例子,自然数形成一个格子,其中联接运算为最大运算。

泛化为联接词法

我们从将单个分配案例视为一个格子开始。 考虑以下:

如果我们对此模型进行一般化,则只要更新是会触发新状态绑定的通货膨胀,我们就可以允许变量重新绑定,该绑定高于格。 让我们来看一个例子,看看它是如何工作的。 在此示例中,为简单起见,我们假设数据流语言的单分配版本允许变量绑定到自然数。 在这里,我们将未绑定状态表示为⊥,而将错误状态表示为⊤。 该格子用作状态图:它显示了变量状态可以沿什么方向传播。 在这种情况下,我们可以将一个未绑定变量更改为值1(或2,或3,依此类推)。但是,一旦绑定了该值,如果尝试再次更改其值,则会进入错误状态。

考虑以下计算最大观测值的自然数格:

在此实例中,随后的绑定操作计算传递给操作和电流值的参数之间加入 ; 然后,此联接的结果将用作变量的值。 与以前类似,可以将其视为状态图:只要数字持续增加,我们就可以继续更改值,而在随后的更改触发错误之前。

此外,我们扩展了模型,以提供额外的原语,与Kuper在“ LVars:确定性并行度的基于格的数据结构 ”中描述的阈值读取操作类似但相关 这额外的读原语的激活值,这防止在完成直到变量被读取的值的读操作是等于,或在半格顺序严格更高。

分配

分布对于高可用性和容错性也很重要。 在我们的模型中,我们假设要么复制数据存储中的每个变量,要么复制整个应用程序。

模型

我们的模型假设使用Dynamo样式的数据分区和复制。 在此模型中,我们使用哈希空间分区和一致性哈希将哈希空间分解为一组不相交的复制集,每个复制集都有一组副本,这些副本负责完全复制该集中的数据。 如下图a所示:

(点击图片放大)

复制变量

在对变量进行分区和复制时,我们假设客户端应用程序将在集群外部运行,或者分布在集群内部或外部的一系列节点上。 每个操作(例如绑定或读取)都转换为请求,并通过网络发送到负责管理约束存储的集群,并且基于是否可以联系副本的仲裁人数,成功或失败。 如图b所示。

复制应用程序

通过引入两个新的原始操作,我们还提供了运行复制的整个应用程序的功能: register ,以远程加载程序, execute ,以远程执行程序。

考虑一种程序要对存储在一个副本集中的数据进行操作的情况:我们可以将整个应用程序推送到副本集,而不是远程运行整个应用程序并对副本集执行往返仲裁操作。 要执行该应用程序并获取结果,我们只需选择一个副本结果即可返回给用户。 如图c所示。

有哪些应用?

让我们看一个需要一系列客户端和服务器之间通信的应用程序示例:一个最终一致的广告计数器。

我们将看一下使用称为Lasp的库编写的Erlang代码,该库实现了我们一直在讨论的编程模型。

广告柜台

这是一个用我们的原型编程语言Lasp编写的广告计数器的示例,该计数器支持本文讨论的编程模型。 它由两套协调过程组成:

  • 服务器:负责跟踪所有客户端的广告印象。
  • 客户:负责增加广告展示次数。

在此示例中,我们使用仅增长计数器,我们将其称为G计数器。 仅增长计数器是可以安全且收敛地处理并发增量选项的计数器,但不能跟踪减量。

%% @doc Client process; standard recursive looping server.
client(Id, Ads) ->
    
    %% Receive messages from server processes.
    receive

        %% Respond to the view advertisement message.
        view_ad ->

            %% Choose an advertisement to display; we simply choose
            %% the first item in a list.
            Ad = hd(Ads),

            %% Update ad by incrementing value; issue an update 
            %% to increment the counter. 
            {ok, _} = lasp:update(Ad, increment, Id),(increment, Id, Value),


            client(Id, tl(Ads) ++ [Ad]);

        {remove_ad, Ad} ->
            %% Remove ad.
            client(Id, Ads -- [Ad])
    end.

在此代码段中,我们初始化了一系列客户端,为每个客户端提供了它们负责向用户显示的广告列表。 这些客户端代表在最终用户附近的客户端上运行的进程。

每个客户端进程处理三件事:返回活动广告的列表,查看广告和删除广告。 我们使用一个简单的递归过程来阻止接收消息来执行这些操作。

当观看广告的请求到达时,我们选择要显示的广告并为该特定广告增加计数器。

此绑定操作成功,因为在这种情况下,我们要推回约束存储区的值是晶格的膨胀。 柜台只会越来越多。

接下来,我们为每个广告初始化一个服务器进程。 该代码如下所示:

%% @doc Server functions for the advertisement counter.
server(Ad, Clients) ->
    %% Perform a blocking read, which will only unblock 
    %% once the counter reaches at least 5.
    {ok, _, _} = lasp:read(Ad, 5),
 
    %% For each client, send a message telling the client
    %% to disable the advertisement from being displayed again.
    lists:map(fun(Client) ->
                %% Tell clients to remove the advertisement.
                Client ! {remove_ad, Ad}
        end, Clients),

    %% Print a message to the screen that advertisement 
    %% limit has been reached.
    io:format("Advertisement ~p reached display limit!", [Ad]).

这些服务器进程中的每个进程都会针对要跟踪的广告针对计数器执行阈值读取; 此阈值读取操作将被阻塞,从而中止服务器进程的执行,直到计数器达到至少五个印象为止。

一旦达到阈值,服务器进程将解除阻止并通知所有客户端停止显示广告。

我们从这里去哪里?

我们用于最终一致计算的编程模型仍处于开发的早期阶段。 在我们的行业合作伙伴的要求以及参考实施的反馈的推动下,它一直在持续进行研究。

在功能方面,我们确定了计划在下一年开发编程模型时要探索的一系列工作。 这项工作的一些示例包括:

因果+一致性

为了支持因果一致性,编程模型和分布模型都需要进行哪些更改? 编程模型是否有可能检测何时需要因果关系+一致性,何时较弱的一致性模型足以满足给定的程序要求?

不同的分销模式

我们是否可以将对一组特定数据进行操作的应用程序重写为对可以以并行,容错方式执行的不相交子集进行操作的较小应用程序? 是否可以在编程模型中透明地在客户端的不同层次集之间分解程序,以支持脱机并纠正操作?

反馈

鉴于我们的评估很大一部分是基于编程模型是否使编程行为和正确性的推理更加容易,我们希望听到您的反馈。

翻译自: https://www.infoq.com/articles/Highly-Distributed-Computations-Without-Synchronization/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

分散计算模式与网络计算模式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值