跳转至

分布式系统大纲

介绍分布式系统基础。直观地了解关键的分布式系统术语,概述算法领域,并探索生产环境的问题。

为什么需要分布式?

Lamport, 1987:

分布式系统是:你不知道存在计算机故障会导致您自己的计算机无法使用的系统。

  • 首先,在托管中心运行*nix的盒子,进程通过 TCP 或者 UDP 通信。
    • 或者EC2, Rackspace的盒子等等
    • 也许通过InfiniBand通信
    • 以短距离的局域网分割
    • 或者以数千公里互联网分割
  • 许多移动应用也参与分布式系统
    • 通过糟糕的网络进行通信
      • 桌面Web浏览器也是如此
      • 这不仅仅是服务器 —— 它也是客户端
  • 更一般地说,分布式系统具有如下特征
    • 由交互的组件构成
    • 很慢
    • 不可靠
    • 无论那些对你意味着什么
  • 还有:
    • 飞机上的冗余CPU
    • ATM和销售点终端
    • 太空探测器
    • 支付账单
    • 医生进行推荐
    • 醉酒的朋友试图通过短信制定计划
    • 每次商务会议

节点和网络

  • 我们将分布式系统中的每个部分叫做 节点
    • 也称之为进程,代理,参与者

节点

  • 延迟特征
    • 在一个节点内部的操作很快
    • 节点之间的操作很慢
    • 快和慢取决于系统的目的
  • 节点是可靠的
    • 作为一个故障单元
    • 知道什么时候发生问题
    • 状态是连贯的
    • 状态转换以优雅有序的方式进行
    • 典型的模型是某种类型的单线程状态机
  • 节点可以自己组成一个分布式系统
    • 但只要该系统作为一个整体提供“快速,连贯”的操作,我们就可以将其视为单个节点。
  • 进程模型
    • 顺序进程通信模型
    • Pi-演算
    • Ambient演算
    • Actor模型
  • 节点故障模型
    • 故障停止 Crash-stop
    • 故障恢复 Crash-recover
    • 故障遗忘 Crash-amnesia
    • 拜占庭 Byzantine

消息流通的网络

  • 节点通过网络交互
    • 人类通过口头语言进行交互
    • 粒子通过磁场交互
    • 计算机通过IP,TCP,UDP,SCTP 或者其它协议交互
  • 在节点之间发送的离散 消息
  • 消息需要 时间 来传播
    • 这是分布式系统中比较慢的一部分
    • 我们称之为 时延
  • 消息可能会丢失
    • 这是分布式系统中另一个不可靠的部分。
  • 网络几乎不会是同构的
    • 一些连接比其它的连接速度更慢、带宽更小、更容易出错

因果关系图

  • 我们可以将节点和网络交互表示为图表
    • 时间从左到右或者从上到下表示
    • 节点是时间方向上的线(因为它们保持不动)
    • 消息通过倾斜的路径 连接 节点

同步网络

  • 节点以锁步方式执行:节点步骤之间的时间始终为1。
  • 消息延迟有限
  • 有效的完美的全球时钟
  • 易于证明的
    • 你可能没有

半同步网络

  • 像同步一样,但时钟只是近似的,例如 在 [c,1]

异步网络

  • 独立执行,无论何时:步进时间在[0,1]中的任何位置
  • 无限制的消息延迟
  • 没有全球时钟
  • 比半同步或同步网络弱
    • 意味着某些算法效率不高
    • 意味着某些算法是 不可能的
    • 参见例如 Attiya&Mavronicolas,“半同步与异步网络的效率“
  • IP网络肯定是异步的
    • 在实践中 真正的病态事情不会发生
    • 大多数网络在几秒到几周内恢复,而不是“从不”
      • 相反,人类的时间尺度大约为几秒到几周
      • 所以我们不能臆想不存在的问题

当网络出错时

  • 异步网络允许
    • 重复
    • 延迟
    • 丢失
    • 重新排序
  • 丢失和延迟是很难区分的
  • 拜占庭网络被允许随意弄乱消息
    • 包括重写内容
    • 在真实的网络中几乎不会出现
      • 绝大部分情况

底层协议

TCP

  • TCP 有效。 用它。
    • 不完美; 你可以更快
    • 但是你知道什么时候会这样
  • 实际上,TCP可以在单个TCP连接中防止重复和重新排序
    • 但是你可能会打开多个连接
    • 如果没有其他原因,TCP 连接最终会失败
    • 当发生这种情况时,你可以 a)错过消息;b)重试
    • 您可以在TCP连接之上构建序列号来保证有序

UDP

  • 与TCP相同的规则,但没有流的不变性
  • 很多人都希望UDP “速度”
    • 不要认为路由器和节点可以并且会随意丢弃数据包
    • 不要认为他们的包会被重复
    • 并重新排序
    • “但至少它是公正的吗?”
      • 错了!
    • 这会导致各种各样的混乱,例如,指标收集
    • 调试很难
    • TCP为您提供流量控制并将逻辑消息重新打包成数据包
      • 您需要重新构建流量控制和背压
    • 通过基于UDP的TLS很难
  • 在TCP有限状态机FSM开销过高的情况下,UDP非常有用
    • 内存压力
    • 许多短连接和套接字重用
  • 在系统目标是尽力传输的场景下尤其有用
    • 语音通话:人们会道歉并重复自己
    • 游戏:口吃和滞后,但后来会追上
    • 更高级别的协议对底层的混乱增加了可靠性

时钟

  • 当一个系统被分割为独立的部分时,我们还期望对事件有某种类型的顺序
  • 时钟可以帮助我们排序:先是这个,然后是那个

墙上时钟

  • 在理论上,操作系统时钟给出了系统中事件的部分顺序
  • 不要使用墙上时钟

Lamport时钟

  • Lamport 1977:“时间,时钟和分布式系统中事件的排序”
    • 每个进程一个时钟
    • 每个状态转换,时间单调递增:t'= t + 1
    • 包含在发送的每条消息中
    • t'= max(t,t_msg + 1)
  • 如果我们有进程的全序,则我们可以对事件实现全序
    • 但那个顺序可能非常不直观

向量时钟

  • 将Lamport时钟推广到所有进程时钟的向量
  • t_i'= max(t_i, t_msg_i)
  • 对于每个操作,在向量中增加该进程的时钟
  • 提供部分因果顺序
    • A < B iff all A_i <= B_i,并且至少一个 A_i <B_i
    • 具体而言,给定一对事件,我们可以确定因果关系
      • B的原因是A,则意味着 A < B
      • A的原因是B,则意味着 B < A
      • 否则独立
  • 务实地:过去是共享的; 现在是独立的
    • 只有“现在”,需要保留独立状态
    • 祖先状态可以被丢弃
    • 让我们对过去做垃圾收集
  • 空间:O(进程数)
    • 对于GC需要协调
    • 或者牺牲正确性并修剪旧的vclock条目
  • 变种
    • 虚线版本矢量 - 用于客户端/服务器系统,对更多的事件排序
    • 间隔树时钟 - 用于进程进入和离开

GPS 和原子钟

  • 比NTP好
    • 毫秒精度的全球分布的全序
    • 将异步网络提升为半同步网络
    • 解锁了更多高效算法
  • 当前只有Google有这个能力
    • Spanner:全球分布的强一致事务
    • 并且他们不共享
  • 比你想要的要贵
    • 每个GPS几百个接受者
    • 原子钟用于本地的正确性:$$$$?
    • 需要多个类型的GPS:供应商可能会出错
      • 我不知道是谁在做这件事,但我敢打赌数据中心
      • 未来将为有限精度时间提供专用的硬件接口

回顾

我们已经介绍了分布式系统的基本原理。节点通过网络交换消息,节点和网络都可能以各种方式失败。TCP和UDP等协议为我们提供了原始信道通信流程,我们可以使用时钟排序时间。现在,我们会讨论分布式系统的一些高级 属性

可用性

  • 可用性基本上是尝试成功操作的一部分

完全可用

  • 原始想法:每次操作都成功
  • 一致性:非故障节点上的每个操作都成功
    • 您无法对故障节点做任何事情

粘性可用

  • 对于非故障节点每次操作都成功
    • 约束:客户端总是可以和相同的节点交互

高可用

  • 最好,如果系统没有被分布。
  • 例如 容忍多达f次失败,但不能再多
  • 也许有些操作失败了

多数可用

  • 如果操作的节点可以跟集群中的多数通信,则操作可以成功
  • 操作少数节点会失败

量化可用性

  • 我们谈论了很多 运行时间
    • 如果没有人用,系统是运行的吗?
    • 在高峰时期会比宕机更差吗?
    • 可以 “在时间窗口内满足的请求的比例” 做衡量
    • 然后在不同时间在窗口上绘制该比例
    • 时间刻度会影响报告的正常运行时间
  • Apdex 是基于设定阈值的响应时间的度量
  • 不是所有的成功都是等价的
  • 将操作分为 “OK”,“meh”和“糟糕”
  • Apdex = P(OK) + P(meh) / 2
  • 可以每年报告
    • “我们今年达到了99.999 apdex”
  • 并且在更精细的时间尺度上!
    • “用户服务的Apdex刚下降到0.5;页面操作!”
  • 理想情况:您的服务提供的整体很好?

一致性

  • 一致性模型是系统中事件的“安全”历史集

单调读

  • 一旦我读取了一个值,任何后续读取都将返回该状态或以后的值

单调写

  • 如果进行写操作,所做的任何后续写操作都将在第一次写入后的值上写入

读自己

  • 一旦一个值写入后,任何后续的读取都会返回写入的值(或者后续的值)

写后读

  • 一旦一个值被读取,后续的写入只会在读取后的位置进行

串行化

  • 所有的操作(事务)都是原子执行的
  • 以某种顺序
  • 对顺序没有限制
  • 例如可以从过去的历史读取也没有问题

因果一致性

  • 假设操作可以由DAG因果关系连接
    • 例如,读取之后的写入与因果关系有关
    • 假设进程不只是丢弃读取数据
    • 该DAG中未连接的操作是 并发的
  • 约束:在进程可以执行操作之前,所有之前的操作已经在该节点上执行
  • 并发操作可以自由重新排序

顺序一致性

  • 跟因果一致性类似,限制了可能的顺序
  • 所有操作必须原子执行
  • 所有进程对操作的顺序达成一致
  • 对于一个进程而言,操作总是按顺序出现
  • 但是节点可以落后

线性化

  • 所有操作必须原子执行
  • 每个进程对操作顺序达成一致
  • 每个操作必须在调用和完成时间之间进行
  • 实时性,玩不的约束可以让我们构建强壮的系统

ACID隔离级别

  • ANSI SQL的ACID隔离级别是很奇怪的
  • 基本上是已有厂商实现的效果
  • 规范中的定义含糊不清
  • Adya 1999:Weak Consistency: A Generalized Theory and Optimistic Implementations for Distributed Transactions
  • 每个ANSI SQL隔离级别都禁止出现奇怪的现象
  • 读未提交
    • 防止P0:脏写
    • w1(x) ... w2(x)
    • 在提交之前,无法覆盖其他事务的数据
    • 在事务仍在修改时可以读取数据
    • 可以读取即将回滚的数据
  • 读已提交
    • 防止P1:脏读
    • w1(x) ... r2(x)
    • 不能读取一个事务未提交的值
  • 可重复读
    • 防止P2:模糊读 (update)
    • r1(x) ... w2(x)
    • 一旦一个事务读取到一个值,事务在提交之前都不能变
  • 串行化
    • 防止P3:幻读 (select id < 2 from t; insert into t values(1);)
    • 给出一些条件P
    • r1(x) ... w2(y in P)
    • 一旦读取了满足查询条件的集合,这个集合不能变化,直到事务提交
    • 不仅仅是值,是那些会参与到集合中的值
  • 游标稳定
    • 事务有一个游标的集合
    • 一个游标引用一个事务访问的对象
    • 持有读锁,直到游标被释放或者提交
    • 在提交的时,游标被升级为写锁
    • 防止丢失更新
  • 快照隔离
    • 事务总是从已经提交的快照读取数据,快照从事务开始时获取
    • 仅当没有其他具有重叠[start..commit]间隔的已提交事务已写入我们编写的任何对象时,才会发生提交
    • 第一个提交者成功

这实际上有什么关系吗?

  • 真实世界并不是那样并发的
  • 很多公司选择读已提交
  • 但恶意攻击者可以诱导并发
  • Flexcoin
    • 比特币交换,允许用户通过在账户之间进行混洗来创造资金
    • 2014年遭遇袭击,365,000英镑被盗
    • 交易所完全崩溃了
  • Poloniex
    • 并发提款被错误地隔离,允许用户超支
    • 安全审核未发现负余额
    • 12.3%的外汇资金被盗; 损失在用户之间传播
  • Warszawski&Bailis 2017:Acidrain
    • 自动识别Web应用程序中的一致性违规
    • 例如 购买一张礼品卡,然后无限次使用
    • 例如 购买笔,在结账时添加笔记本电脑到购物车,获得免费的笔记本电脑
    • 超过50%的电子商务网站中都存在漏洞
    • 弱DB隔离默认值
    • 交易范围的不当使用
    • 未能使用任何交易

权衡

  • 理想情况下,我们需要完全可用性和线性化
  • 一致性需要协调
    • 如果允许每个顺序,我们不需要做任何工作!
    • 如果我们想要禁止某些事件顺序,我们必须交换消息
  • 协调(通常)都有代价
    • 更多的一致性更慢
    • 更多的一致性更直观
    • 更多的一致性更少的可用性

可用性和一致性

  • CAP理论:线性化或者完全可用
  • 但是等等,这里还有更多!
  • Bailis 2014:Highly Available Transactions: Virtues and Limitations
  • 其他理论不允许完全可用或粘性可用......
    • 强串行化
    • 串行化
    • 可重复读
    • 游标稳定
    • 快照隔离
  • 你可以粘性可用...
    • 因果关系
    • PRAM
    • 读后写
  • 你可以完全可用...
    • 读未提交
    • 读已提交
    • 单调原子视图
    • 写后读
    • 单调读
    • 单调写

收获与收益

  • Fox & Brewer, 1999: Harvest, Yield, and Scalable Tolerant Systems
  • 收益:请求完成的概率
  • 收获:响应中反映的数据部分
  • 例子
    • 搜索引擎中的节点故障可能导致某些结果丢失
    • 更新可能反映在某些节点上,但不反映在其他节点上
    • 考虑一个被分区的AP系统
    • 你可以写入数据一些人不能读取到
    • 流式视频降级以保持低延迟
  • 这不是违反安全不变量的借口
    • 只是帮助您量化您可以 超过 安全不变量的数量
    • 例如,99%时间,你可以读取到前面90%的写入
  • 强依赖负载,硬件,拓扑,等
  • 可以根据每个请求调整收获与收益
    • 尽可能在10ms以内
    • 我需要一切,我理解你可能不会应答

混合系统

  • 有一系列的选择
  • 不同部分设施有不同的需求
  • 选择可以满足你的约束最弱的模型
    • 但是考虑概率界限,可见性延迟可能会令人望而却步
    • 请参阅Dynamo Quorums中的概率有界陈旧性
  • 不是所有的数据都是一样的
  • 大数据通常不是很重要
  • 小数据通常很重要
  • 线性化用户的操作,因果关一致性社交信息

回顾

可用性衡量运营成功的频率。 一致性模型是管理可能发生的操作以及何时发生的规则。 更强的一致性模型通常以性能和可用性为代价。 接下来,我们将讨论构建系统的不同方法,从弱到强一致性。

尽可能避免共识

CALM 猜想

  • Consistency As Logical Monotonicity
  • 如果你可以证明一个系统是逻辑单调的,则不需要协调
  • 什么是“协调”?
  • 什么是“单调”?

  • 单调性,非正式的,是不会回退的

  • 从部分信息推论永远不会因为新信息而无效
  • 没有否定的关系代数和Datalog是单调的

  • Ameloot, et al, 2011: Relational transducers for declarative networking

  • 理论上,不知道网络范围的无协调网络进程只能计算Datalog中的单调查询
    • 这读起来不容易
  • 无协调并不意味着没有通信
    • 即使面对任意水平分区,Algo也能成功
  • 用非常宽松的实际术语
    • 尝试说明您的问题,以便您只向系统添加新的事实
    • 当您根据当前已知的事物计算新事实时,您是否可以确保永远不会撤回事实?
    • 考虑特殊的“密封事实”,将一系列事实标记为完整
    • 这些“仅增长”算法通常更容易实现
    • 可能的权衡:不完整的读取
  • Bloom语言
    • 使用flow分析进行无序编程
    • 可以告诉你那里需要协调

Gossip

  • 消息广播系统
  • 对于集群管理、服务发现、健康、传感器、CDN等很有用
  • 通用的若一致性/高可用
  • 全球广播
  • 发送消息给每个其它节点
  • O(节点数)
  • 网状网络
  • 流行病模型
  • 与邻居接力
  • 传播时间按最大自由路径的顺序排列
  • 生成森林
  • 替代网络,使用树
  • 跳到连接节点,该节点继续连接到其他连接节点
  • 减少多余的消息
  • 减少时延
  • Plumtree (Leit ̃ao, Pereira, & Rodrigues, 2007: Epidemic Broadcast Trees)
  • Push-Sum等
  • 来自您收到数据的每个人的总和输入
  • 将其广播到随机对等体
  • 最小值,最大值,平均值的扩展
  • 有助于实时指标,速率限制,路由,识别群集热点

CRDTs

  • 无序的数据类型
  • 计数器,集合,映射等等
  • 容忍欺骗,延迟和重新排序
  • 与顺序一致的系统不同,没有“单一的事实来源”
  • 但不像天真最终一致的系统,永远不会丢失信息
  • 除非你明确让他们丢失信息
  • 在高可用系统中工作的很好
  • 网页/移动客户端
  • Dynamo
  • Gossip
  • INRIA: Shapiro, Preguiça, Baquero, Zawirski, 2011: "A comprehensive study of Convergent and Commutative Replicated Data Types"
  • 由数据类型X和合并函数m组成,是:
    • 关联:m(x1, m(x2, x3)) = m(m(x1, x2), x3)
    • 交换:m(x1, x2) = m(x2, x1)
    • 幂等:m(x1, x1) = m(x1)
  • 容易构建。容易推理。 摆脱各种头痛的问题。
  • 沟通失败了吗? 重试! 它会收敛!
  • 消息是否无序到达? 没关系!
  • 如何同步两个副本? 合并!
  • 缺点
  • 有些算法需要顺序,不能用CRDT表示
  • 读取可能是任意旧值
  • 更高的空间成本

HATs

  • Bailis, Davidson, Fekete, et al, 2013: "Highly Available Transactions, Virtues and Limitations"
  • 从任何副本确保响应
  • 低延迟(比可序列化协议快1-3个数量级!)
  • 读已提交
  • 单调原子视图
  • 非常适用于可交换/单调系统
  • 多个项更新的外键约束
  • 有限的唯一性约束
  • 可以确保给定任意有限延迟的收敛(“最终一致性”)
  • 地理分布系统的良好候选
  • 可能最好与更强大的事务系统协调一致
  • 另见:COPS,Swift,Eiger,Calvin等

需要共识,怎么办?

  • 共识问题
  • 三种进程类型
    • 提议者:提出一个值
    • 接受者:选择一个值
    • 学习者:读取选择的值
  • 接受者的分类
    • N个接受者
    • F个允许失败
    • M个恶意的接受者
  • 三个变体

    • 非平凡性:只能学习提出的值
    • 安全性:最多可以学习一个值
    • 活跃性:如果一个提议者p,一个学习者l和一组N-F各接受者没有错误并且可以相互通信,并且如果p提出一个值,则l最终将学习一个值。
  • 系统类型与共识问题等价

  • 所以我们这里的任何证明也适用于那些系统
  • 锁服务
  • 排序的日志
  • 复制状态机

  • FLP告诉我们不可能在异步网络中实现共识

  • 在正确的时间杀死一个进程,你可以打破任何共识算法
  • 是这样,但情况也不是你想的那样坏
  • 实际上,网络通常足以达成共识
  • 此外,FLP假设确定的进程

    • 实际的计算机系统不是确定的
    • Ben-Or 1983: "Another Advantage of free choice"
    • 非确定性算法可以达成共识
  • Lamport 2002: tight bounds for asynchronous consensus

  • 至少有两个提议者或一个恶意提议者,N> 2F + M
    • 需要大多数
  • 对于至少2个提议者或一个恶意提议者,学习提案至少需要2个消息延迟。

  • 这是一个实用的可操作范围

  • 在稳定的集群中,可以使用一次往返大多数节点的方式。
  • 群集转换期间需要更多。

Paxos

  • Paxos是共识算法的黄金标准
  • Lamport 1989 - The Part Time Parliament
    • 对想象中的希腊民主的描述方式来写作
  • Lamport 2001 - Paxos Made Simple
    • “用于实现容错分布式系统的Paxos算法一直被认为难以理解,也许是因为原始表示对于许多读者来说是希腊语[5]。事实上,它是最简单和最明显的分布式算法之一。 最后一节解释了完整的Paxos算法,它是通过直接应用协议来建立分布式系统的状态机方法得到的 - 这种方法应该是众所周知的,因为它是最可能的主题。 经常被引用的关于分布式系统理论的文章[4]。“
  • Google 2007 - Paxos Made Live
    • 生产谷歌锁定服务Chubby的笔记
  • Van Renesse 2011 - Paxos Made Moderately Complex
    • 事实证明你必须优化
    • 伪代码也会有所帮助
    • 一个伪代码页 -> 几千行C ++
  • 就独立提案达成共识
  • 通常部署在多数仲裁中,5或7个节点
  • 几个优化
  • Multi-Paxos
  • Fast Paxos
  • Generalized Paxos
  • 并不总是清楚使用哪种优化,哪些可以安全地组合
  • 每种实现都使用略有不同的风格
  • Paxos实际上更像是一系列算法,而不是一个描述良好的单一实体
  • 用于各种生产系统
  • Chubby
  • Cassandra
  • Riak
  • FoundationDB
  • WANdisco SVN servers
  • 新研究:Paxos法定人数不需要占多数:可以优化快速阶段2法定人数 Howard, Malkhi, and Spiegelman
  • 我们还不确定如何使用它
  • 持久性仍然需要分发
  • 新研究:Distributed consensus revised
  • 一般化共识

ZAB

  • ZAB是Zookeeper原子广播协议
  • Junqueira, Reed, and Serafini 2011 - Zab: High-performance broadcast for primary-backup systems
  • 与Paxos有区别
  • 提供顺序一致性(可线性化写入,滞后有序读取)
  • 很有用,因为ZK客户端通常需要快速本地读取
  • 但是还有一个SYNC命令可以保证实时可见性
  • (SYNC + op)允许线性化读取
  • 仍然是多数派,5个或者7个节点

Humming共识

  • 用于管理分布式系统重新配置的元数据存储
  • 看起来有点像CORFU的复制日志
  • 另见:链式复制

Viewstamped Replication

  • 作为复制协议提供,但也是共识算法
  • 事务处理加视图变更算法
  • 保证多数达成的值可以将来继续生存
  • 我不知道任何生产系统,但是我不确定它们与Paxos一起以某种方式影响了Raft

Raft

  • Ongaro & Ousterhout 2014 - In Search of an Understandable Consensus Algorithm
  • Lamport说Paxos很容易,但我们仍然有各种问题
  • 如果有一个我们可以理解的一致性算法怎么办?
  • 当我们想要是状态机时,Paxos接近独立决策
  • 改为维护状态机转换的复制日志
  • 还构建了集群成员转换,这对于真实系统来说是关键
  • 非常新,但是我们有一个核心算法的 Coq 证明
  • 可用于顺序或可线性化的状态机
  • RethinkDB
  • etcd
  • Consul
  • CockroachDB

回顾

只添加而不是收回的系统需要较少的协调就能构建。我们可以使用gossip系统向其他进程广播消息,CRDT用于合并来自对端的更新,以及用于弱隔离事务的HAT。可串行化和线性化需要共识,我们可以通过Paxos,ZAB,VR或Raft获得。现在,我们将讨论分布式系统的不同规模。

时延特征

  • 时延不会为0
  • 带宽一直在增加,但是正在接近光和电子的物理极限
  • 延迟预算决定了您的系统设计
    • 你能负担多少个网络电话?
  • 对于慢,不同类型的系统有不同的定义
  • 不同的目标
  • 不同的算法

多核系统

  • 多核(尤其是NUMA)架构是类似的分布式系统
  • 节点不会病态故障,但是消息传递很慢
  • 同步网络通过一个总线提供(例如,Intel QPI)
  • 硬件和微代码中的整个复杂的协议集使内存看起来很清晰
  • 非临时存储指令(例如,MOVNTI)
  • 提供了屏蔽分布特性的抽象
  • MFENCE/SFENCE/LFENCE
    • 引入针对加载/存储指令的序列化点
    • 延迟特征: ~100周期 / ~30ns
    • 依赖硬件,cache,指令等等
  • CMPXCHG 比较和设置(顺序一致修改内存)
  • LOCK
    • 锁定整个跨核心的内存子系统!
  • 但是抽象的同时也伴随着开销(abstractions come with costs)
  • HLE可能会有所帮助,但尚未成熟
  • Blog: Mechanical Sympathy
  • 在可能的地方,避免协调
  • 上下文转换(进程或者线程)代价很高
  • 当编写多线程程序时,将你的工作切分成独立的块

逻辑网络

  • 你通常会在局域网内部部署复制系统
  • 消息延迟可以低至100us
  • 但是,在任何规模较大的网络(EC2)中,最少在ms范围
  • 又是,包可能会被延迟五分钟
  • 对这种情况需要做规划
  • 与未命中缓存的磁盘查找相比,网络在一个数量级内
  • 或者更快,在EC2中
    • EC2磁盘延迟大约是20ms
    • 200ms ?
    • 20000ms ?
      • 但是EBS实际是其它计算机
      • LMAO 如果你认为EC2中一切都是真实的
      • 等等,实际的磁盘也会这样做?
        • 什么是IO调度程序?
  • 但是网络比内存/计算更慢
  • 如果你的目标是吞吐量,工作单位应该花费超过一毫秒
  • 但是分布还有其它原因
    • 资源分片
    • 隔离故障

地理复制

  • 全球范围部署的两个原因
  • 最终用户的延迟
    • 人类可以探测到 ~10ms的延迟,容忍 ~100ms的延迟
    • SF--Denver: 50ms
    • SF--Tokyo: 100ms
    • SF--Madrid: 200ms
  • 灾难恢复
    • 数据中心是很好的,但是不是完美的
    • 飓风是一个问题
    • 整个亚马逊的区域可能会故障
    • 是的,是区域,不是可用区(AZ)
  • 最少一轮的共识
  • 差的情况下可能需要4轮
    • 如果你有一个糟糕的Paxos 实现(例如Cassandra),也许总是4轮
  • 所以如果想在数据中心之间使用Paxos,准备好应对这种开销
  • 因为最小的延迟比用户可容忍的延迟还要高
    • cache cache cache
    • 写入队列,异步传递
    • 考虑减少一致性保证以换取低延迟
    • CRDT 可以保证安全的本地写入
    • 因果一致性和HAT可以成为好的方式
  • 强一致性呢?
    • 地理分布的服务具有天然的分裂性
    • EU用户在EU服务器上;US用户在US的服务器上
    • 使用共识在不同的数据中心中间迁移用户
  • 固定/代理 更新到所在地数据中心
    • 很有希望是最近的数据中心
    • 但也可能不是。我认为Facebook仍然将所有写入推送到一个数据中心
  • 当顺序一致性没问题时,将读取在本地缓存
    • 你可能已经在单个数据中心中使用缓存

回顾

我们讨论了分布式系统的三个特征尺度:与同步网络耦合的多核处理器,由LAN链接的计算机,以及通过互联网或专用光纤链接的数据中心。 CPU的主要后果是性能问题:了解如何最小化协调。 在LAN上,在用户注意到之前,延迟对于许多网络跃点来说足够短。 在地理复制系统中,高延迟最终会推动一致的固定数据中心的解决方案。

常见的分布式系统

外置堆

  • Redis,memcached,...
  • 数据适合放到内存中,复杂的数据结构
  • 当你的数据结构的内置数据结构很慢/丑陋时很有用
  • 跟缓存一样优秀
  • 或者作为平台之间共享状态的快速便捷的暂存器
  • 不是特别安全

KV存储

  • Riak,Couch,Mongo,Cassandra,RethinkDB,HDFS,...
  • 通常1,2,3维度的键
  • O(1)访问时间,又是O(range)根据ID的范围扫描
  • 值之间没有强依赖关系
  • 对象可以是不透明的或结构化的
  • 大数据集
  • 通常是可线性扩展的
  • 通常没有事务
  • 一致性模型范围 - 通常是可选的线性化/顺序操作

SQL数据库

  • Postgres, MySQL, Percona XtraDB, Oracle, MSSQL, VoltDB, CockroachDB, ...
  • 通过关系代数定义:restrictions of products of records 等
  • 中等大小的数据集
  • 通常包含多记录事务
  • 关系和事务需要协调,减少扩展性
  • 许多系统是主从切换
  • 访问代价依赖索引
  • 典型的强一致性(SI,可串行化,阉割的可串行化)

搜索

  • Elasticsearch, SolrCloud, ...
  • 索引引用的文件
  • 中等到很大的数据集
  • 使用O(1)文档访问,日志类型的搜索
  • 很好的扩展性
  • 典型的若一致性

协调服务

  • Zookeeper、ETCD、Consul,...
  • 典型的强(顺序或者线性化)一致性
  • 小数据集
  • 作为无状态服务的协调原语很有用

流系统

  • Storm、Spark ...
  • 通常是定制设计、或者使用工具包来构建
  • 典型的小内存数据容量
  • 低延迟
  • 高吞吐
  • 若一致性

分布式队列

  • Kafka, Kestrel, Rabbit, IronMQ, ActiveMQ, HornetQ, Beanstalk, SQS, Celery, ...
  • 在多个节点将日志写入磁盘来实现冗余
  • 当您需要立即确认工作,并在以后使用很有用
  • 在无状态服务之间可靠地发送数据
  • 我知道的仅有的一个不会在分区时丢失数据的是Kafka
    • 或许 SQS 也可以?
  • 队列不会提高端到端延迟
    • 总是更快地立即完成工作
  • 队列不会提高平均吞吐
    • 平均吞吐被消费者限制
  • 当消费者并发时,队列不提供总事件排序
    • 消费者几乎肯定是并发的
  • 同样,队列不保证异步使用者的事件顺序
    • 因为消费者的副作用可能无序发生
    • 所以,不要依赖顺序
  • 队列不能提供最多一次或者最少一次的递送
    • 任何声称不这样做的人都试图向你推销一些东西
    • 恢复一次性递送需要仔细控制副作用
    • 使您的排队操作具有幂等性
  • 队列确实提高了突发吞吐量
    • 平滑负载峰值
  • 分布式队列还提高了容错(如果不丢失数据)
    • 如果不需要容错或者大缓冲,使用TCP
    • 很多人使用具有六个磁盘写入和十五个网络跃点的队列,其中单个套接字write()可能已经足够
  • 当您选择了糟糕的运行时时,队列可以让您脱离绑定

回顾

我们使用数据结构存储作为外置堆:它们是分布式系统的管道磁带。 KV存储和关系数据库通常被部署为记录系统; KV存储使用独立key,不太适合关系数据,但与SQL存储相比提供了更好的可伸缩性和部分容错性,SQL存储提供了丰富的查询和强大的事务保证。分布式搜索和协调服务完善了我们构建应用程序的基本工具包。 流系统应用于数据集的连续,低延迟处理,并且往往看起来更像框架而不是数据库。 分布式队列专注于消息而不是转换

模式语言

  • 构建分布式系统的一般建议
    • 来之不易的经历
    • 重复其他专家告诉我的内容
      • over beers
    • Hearsay
    • Oversimplifications
    • Cargo-culting
    • Stuff I just made up
    • YMMV

不要用分布式

  • 规则1:如果不是必须,请不要使用分布式
    • 本地系统有可靠的原语。锁、线程、队列、事务。
      • 当你迁移到分布式系统时,你必须从头开始
    • 这个问题是否很小可以在一个节点上完成?
      • 我有一个很大的数据问题
        • Softlayer将以每箱5000美元的价格租给一箱3TB的内存
        • Supermicro将以约115,000美元的价格出售6TB的盒子
      • 现代计算机很快
        • 我所知道的生产JVM HTTP服务已经可以支持50K请求/秒
          • 解析JSON时间,记日志到磁盘,推送到S3
        • 使用TCP的协议缓冲区:1000万/s的事件
      • 这项服务能否容忍单个节点的保证?
      • 如果它破了,我们可以再起来吗?
      • 手动干预可以取代分布式算法吗?

使用已经存在的分布式系统

  • 如果必须分布,可以将任务推送到其它软件?
    • 分布式数据库或者日志怎么样?
    • 可以给亚马逊付钱让他们来做吗?
    • 相反,维护费用是多少?
    • 学习使用/操作该分布式系统多少钱?

永不故障

  • 购买非常昂贵的硬件
  • 以受控方式更改软件和硬件
    • 在临时环境上部署
  • 可以构建非常可靠的网络和机器
    • 以降低成本为代价,购买更昂贵的硬件,寻找人才
    • 硬件/网络故障仍然发生,但足够罕见=>低优先级

接受故障

  • 分布式系统不仅以延迟为特征,但以经常性,部分故障为特征
  • 我们能接受这种失败并继续我们的生活吗?
    • 我们的SLA是什么?
    • 可以手动恢复吗?
    • 可以给人付钱来修复吗?
    • 保险可以承担损害吗?
    • 我们可以打电话给客户并道歉吗?
  • 听起来很傻,但是更加便宜
    • 我们永远也不能阻止100%的系统故障
    • 有意识地选择恢复高于系统的水平
    • 这就是金融公司和零售商的做法

备份

  • 备份基本是顺序一致的,但是可能丢失一个操作窗口的数据
    • 当正确完成时
      • 一些备份程序没有快照状态,导致文件系统或者数据库损坏
      • 破坏外键关系,丢失文件等...
    • 允许恢复到几分钟或者几天前
    • 但是除了故障恢复,可以让你按时间返回
      • 从逻辑故障中恢复很有用
        • 分布式数据库正确完成其任务,但是告诉数据库删除了关键数据

冗余

  • 好的,所以失败不是一个选择

  • 希望降低故障的可能性

  • 在几个节点上进行相同的状态和相同的计算

    • 我不是 主-备 的忠实信徒

      • 备机可能有冷缓存,损坏的磁盘,旧版本等

      • 备机在变为主机时往往会失败

      • 尽可能主-主

    • 我也不是只有两个副本的忠实的粉丝

      • 节点故障概率太高
      • 对于不重要的数据还可以
      • 我一般都想要3份数据副本
        • 对于重要的数据,4或者5分副本
        • 对于Paxo或者其它的多数派系统,⅗/7很常见
        • 常见的策略:Paxos跨越5个节点,3个或者4个在本地数据中心
      • 操作可以在本地节点确认后立即完成;低延迟
      • 适应单节点故障(虽然延迟会出现峰值)
      • 但是在另一个DC中仍然有一个顺序一致的备份
      • 所以如果你失去了整个DC,那么一切都不会丢失
      • 参见Camille Fournier关于ZK部署的会谈
      • 只要故障不相关,冗余就可以提高可用性
        • 失败并非不相关
        • 来自同一批次的磁盘同时失败
        • 当架顶式交换机断开时,同一机架节点出现故障
        • UPS断电时,相同DC节点发生故障
        • 查看整个EC2 AZ故障
        • 在每个节点上运行相同的错误计算将中断每个节点
      • 昂贵的查询
      • Riak list-keys
      • Cassandra doomstones
        • 级联故障
      • Thundering-herd
      • TCP incast

分片

  • 这个问题很大
  • 将问题分解成足够小的部分以适合节点
    • 不小:小零件=>高开销
    • 不太大:需要逐个从节点到节点重新平衡工作单元
    • 大约10-100个工作单位/节点是理想的
  • 理想的:工作单元相同的大小
    • 注意热点
    • 注意负载随时间变化
  • 了解你的界限
    • 压倒一个节点之前单个部分有多大?
    • 我们如何在它节点上出现之前,如何强制执行该限制?
  • 分配分片给节点
    • 在数据库中内置
    • 很好的候选,ZK,Etcd等等
    • 参见Boundary's Ordasity

独立的域

  • 分片是一般模式避免协调的一个特殊场景
    • 保持尽可能独立
      • 提高容错
      • 提高性能
      • 减少复杂性
    • 分片提高伸缩性
    • 通过CRDT避免协调
    • Flake ID:mostly time-ordered identifiers, zero-coordination
    • 部分可用:用户可以继续使用系统的一些部分
    • 处理队列:更多消费者减少昂贵事件的影响

ID结构

  • 在我们的世界中,一定需要唯一的标识
    • 在规模上,ID结构可以决定你的成败
    • 考虑下面的开销
      • 扫描
      • 排序
      • 分片
    • 顺序ID需要协调:我们可以避免吗?
      • Flake ID,UUID
    • 对于可分片,你的ID可以直接映射到一个分片吗?
    • SaaS应用:对象ID也可以编码客户ID
    • Twitter:推特ID可以编码用户ID

不变的值

  • 从不更改的数据存储起来很简单
    • 不需要协调
    • 复制和恢复开销很小
    • 在磁盘上很小的重新打包代价
  • 对于Cassandra,Riak,和LSM树DB很有用
    • 或者像Kafka的日志
  • 原因很简单:要么是存在,要么不存在
    • 消除各种事务的头痛问题
    • 非常容易缓存
  • 高可用和持久性,可调节的写入延迟
    • 低写入延迟:可以从最近的副本做应答
    • 对于地理分布尤其有价值
  • 需要垃圾回收
    • 但有很好的方法可以做到

可变值

  • 指向变值的指针
  • 指针很小!仅元数据
    • 可以在小DB中存储很多的指针
    • 对于协调服务或者关系DB,是很好的候选
  • 典型地,在系统中没有很多指针
    • 你的全部DB可以在用一个指针表示
    • Datomic only has ~5 identities
  • 对标识的强一致性操作可以由不可变的HA存储支持
    • 利用AP存储延迟和规模
    • 利用共识系统提供的小数据集的强一致性
    • 写的可用性受到标识存储的限制
      • 但是,如果你只需要顺序一致性,就可以读缓存
      • 如果您只需要序列化就可以更容易
    • 参见Rich Hickey关于Datomic架构的演讲
    • 参见Pat Helland在2013年关于Salesforce存储的Ricon West主题演讲

汇合

  • 与顺序无关的系统更容易构造和推理
  • 因此,可以避免协调
  • CRDT是汇合的,这意味着我们可以应用更新而不必等待
  • 不变的值通常是汇合的:一旦存在,就固定了
  • 流媒体系统也可以利用汇合:
    • 当你知道你已经看到了所有内容时,缓冲事件和计算+刷新
    • 发出部分结果,以便您现在可以采取操作,例如用于监视
    • 当完整数据可用时,通过加法或者最大值来合并
    • 银行分类账(大部分)是汇合的:事务顺序不影响余额
      • 但当你需要执行一个最小余额时,就不再是汇合了
      • 结合密封事件(例如当天结束)以恢复汇合
  • 参考 Aiken, Widom, & Hellerstein 1992, "Behavior of Database Production Rules"

背压

  • 交互的服务通常通过队列连接

  • 服务和队列的容量是有限的

  • 当下游服务无法处理负载时,您如何处理它?

    1. 消耗资源并爆炸
    2. 摆脱负载,开始删除请求
    3. 拒绝请求,忽略任务,应答客户端失败
    4. 给客户端背压,告诉客户端放慢速度
  • 2-4允许系统追赶并恢复

    • 但是,背压减少了必须重试的工作量
  • 背压取决于生产者的选择:组成成分

    • 减载系统的客户端被锁定在减载中

      • 他们没有办法知道系统已被冲洗过
    • 背压系统的客户端可以将背压应用到他们的客户端

      • 或卸载,依赖他们选择
    • 如果你在实现一个异步系统,一定要包括反压力
      • 你的用户会感谢你
  • 基本上:边界资源

    • 请求超时(有界时间)
    • 指数退避(有界使用)
    • 有界队列
    • 有界并发
  • 参考:Zach Tellman, "Everything Will Flow"

域模型服务

  • 问题由相互作用的逻辑部分组成
  • 每个逻辑部分具有不同的代码,性能和存储需求
  • 单体应用程序基本上是多租户系统
    • 多租户很难
    • 但通常可以在同一个过程中运行多个逻辑“服务”
  • 将您的系统划分为域的离散部分的逻辑服务模型
  • OO方法:每个名词是一种服务
    • 用户服务
    • 视频服务
    • 索引服务
  • 功能方法:每个动词是一项服务
    • 验证服务
    • 搜索服务
    • 调度/路由服务
  • 我所知道的大多数大型系统都使用混合方式
    • 名词服务是强制执行数据类型不变量的好方法
    • 动词服务是强制执行转换不变量的好方法
    • 所以有一个基本的用户服务,由Auth服务使用
  • 你画线的地方......那很棘手
    • 服务带来开销:尽可能少
    • 考虑工作单元
    • 需要独立扩展的独立服务
    • 具有严格依赖性和严格延迟预算的服务
    • 使用补充资源(例如磁盘和CPU)的协同服务
    • 手动:在渲染节点上运行memcache
    • 较新的商店:Google Borg,Mesos,Kubernetes
  • 服务应该封装和抽象
    • 尝试建造树而不是网
    • 避免让外人直接操纵服务的数据存储
  • 服务之间的协调需要特殊协议
    • 必须重新发明事务
    • 尽可能去通信
    • 萨加斯
    • 是为单节点世界编写的:我们必须在分布式环境中聪明一点
    • 事务必须是幂等的,或者一起回滚
    • Typhon / Cerberus
    • 多个数据存储的因果一致性协议

结构遵循社交空间

  • 制作软件是一种基本的社交工具
  • 自然对齐:团队或个人拥有特定服务
  • 乔·弗里曼,“无结构的暴政”
    • 责任和权力应该是明确的
    • 通过角色轮换人员以防止领地
    • 促进信息共享
    • 但不要经常旋转
    • 软件的增加成本非常高
  • 随着团队的成长,其使命和思想将正式化
  • 服务和他们的界限也是如此
  • 逐渐积累关于与世界的服务关系的假设
  • 重写以应对不断变化的外部压力
  • Tushman&Romanelli,1985:组织演变
  • 服务可以是库
  • 最初,您的所有服务应该是库
  • 完全可以依赖多个服务中的用户库
  • 具有明确界限的库很容易被提取出来成为服务
  • 社会结构管理库/服务边界
  • 由于库的用户很少,或者用户协调紧密,因此更改很容易
  • 但是在许多团队中,用户有不同的优先级,必须让他们信服
  • 为什么用户应该做一些工作来升级到新的库版本?
  • 服务强制通过定义的API弃用生命周期进行协调
    • 您还可以通过代码审查和工具对库强制执行此操作
  • 服务支持集中控制
  • 您的性能改进会立即影响每个人
  • 逐步转换为新的磁盘格式或支持数据库
  • 在一个地方使用该服务
  • 更难用库做这些事情
  • 服务有成本
  • 网络调用的故障复杂性和延迟开销
  • 服务依赖的混乱
  • 很难静态分析代码路径
  • 您认为库API版本很难
  • 额外的仪器/部署
  • 服务可以使用良好的客户端库
  • 该库可能是“打开套接字”或HTTP客户端
    • 利用HTTP标头!
    • 接受标题版本
    • 对缓存和代理的大量支持
    • Haproxy是HTTP和TCP服务的优秀路由器
  • 最终,库可能包含模拟IO
    • 服务团队负责测试服务是否提供API
    • 当已知API稳定时,每个客户都可以假设它有效
    • 无需在测试套件中进行网络呼叫
    • 大幅减少测试运行时和开发环境的复杂性

回顾

如果可能,请尝试使用单个节点而不是分布式系统。 接受一些失败是不可避免的:SLA和道歉可能具有成本效益。 为了处理灾难性故障,我们使用备份。为了提高可靠性,我们引入了冗余。 为了扩展到大的问题域,我们将问题分成片。不可变值易于存储和缓存,并且可以通过可变身份引用,允许我们在大规模上构建强大一致的系统。 随着软件的发展,不同的组件必须独立扩展,我们将库分解为不同的服务。 服务结构与团队密切配合。

生产问题

  • 不仅仅是设计考虑
  • 证明很重要,但真正的系统需要IO

您的文化支持分布式系统

  • 理解生产中的分布式系统需要具有多种角色的人员密切合作。
    • 部署
    • QA
    • 操作
  • 共性问题
    • 开发需要关注产品
    • 运行需要关注实现
    • 好的沟通可以更快地诊断问题

测试一切

  • 类型系统非常适合防止逻辑错误

    • 减少了测试负担
  • 然而,它们在预测或控制运行时性能方面并不是很好

  • 所以,需要可靠的测试套件

    • 理想情况下,您需要一个严格不同类型的测试
    • 几秒钟内运行的快速基于示例的测试
    • 可以在一夜之间运行的更彻底的基于属性的测试
    • 能够在进程中模拟整个集群
    • 控制与模拟网络的并发交织
    • 自动化的硬件故障
  • 测试分布式系统比测试本地系统要困难得多

    • 你从未听说过的大量失败模式
    • 组合状态空间
    • 错误只能表现为小/大/中间时间/空间/并发

“这很慢”

  • 杰夫霍奇斯:你会听到的最糟糕的错误是“它很慢”
  • 一直发生,很难本地化
  • 由于系统是分布式的,因此必须分析多个节点
    • 为此建立的分析工具并不多
    • Sigelman等,2010:Dapper,一种大规模分布式系统跟踪 基础设施
    • Zipkin
    • 大工具投资
  • 分析工具擅长发现CPU问题
    • 但高延迟通常是IO的标志,而不是CPU
    • 磁盘延迟
    • 网络延迟
    • GC延迟
    • 队列延迟
  • 尝试使用应用程序级指标本地化问题
    • 然后深入了解流程和操作系统性能
  • 执行相同工作的节点之间的延迟差异是一个重要信号
    • ⅓节点慢:可能是节点硬件,重新路由
    • 3/3节点缓慢:可能是逻辑错误:查看分片大小,工作负载,查询
  • 扇出工作负载放大了尾部延迟

测量一切

  • 生产中的缓慢(和彻头彻尾的错误)源于相互作用 系统
  • 为什么?因为您的全面测试套件可能验证了单一系统大多是正确的
  • 所以我们需要一种方法来了解系统在生产中做了什么
    • 关于其依赖性
    • 反过来,这可以推动新的测试
  • 在某种程度上,良好的监控就像持续测试
    • 但不是替代品:这些是不同的领域
    • 两者均可确保您的更改正常
  • 想要高频监控
    • 生产行为可以在1毫秒的规模上进行
    • TCP incast
    • 理想情况下~~ 1ms分辨率
    • 在极限情况下,Ops响应时间与观察延迟呈线性关系
    • 约1秒的端到端延迟
    • 理想情况下,毫秒级延迟,也可能是ms分辨率
    • 通常成本过高;回到1或10秒
    • 有时你可以忍受60秒
  • 对于容量规划,每小时/每日季节性更有用
  • 测量应与应用程序紧密耦合
    • 只衡量重要的事情
    • 回应请求很重要
    • 节点CPU并不重要
    • 大多数系统的关键指标
    • Apdex:在延迟SLA内使用成功的应答
    • 延迟分布:0,0.5,0.95,0.99,1
      • 百分位数,而不是平均数
      • 顺便说一句,你不能采取百分位数的均值
    • 总吞吐量
    • 队列统计
    • 其他系统延迟/吞吐量的主观体验
      • 数据库可能认为它很健康,但客户可能会觉得它很慢
      • 组合爆炸 —— 在深入故障时最好使用它
    • 您可能必须自己编写此测量
    • 投资指标库
  • 开箱即用的监控通常无法衡量真正重要的因素:您的 应用程序的行为
    • 但它在追踪问题原因方面非常有用
    • 主机指标,如CPU,磁盘等
    • 你的应用程序做了一些常见的事情(例如rails应用程序)工具,如New Relic,运作良好
  • 客户端的分片指标
    • 当用户具有不同的工作负载时很有用
    • 可以调整阈值以适应该客户端
    • 主要客户端分一些,“其余”的另一个桶
  • 超级工具:分布式跟踪(Zipkin,Dapper等)
    • 大量时间投资
    • 神秘机器
    • 从跟踪数据自动推断服务之间的因果关系
    • 识别关键路径
    • 在实施之前对新算法进行性能建模

日志

  • 记录在大规模系统上不太有用
    • 问题可能未本地化到一个节点
    • 当请求触及更多服务时,必须跟踪许多日志文件
    • 投资日志收集基础设施
      • ELK,Splunk等
    • 非结构化信息难以汇总
    • 记录结构化事件

影子流量

  • 负载测试仅在模拟负载与实际负载匹配时才有用
  • 考虑转储生产流量
    • 太棒了:使用SIGUSR1终止一个进程,它会转储五分钟的请求加载
    • 太棒了:tcpdump / tcpreplay用于请求的限制
    • 太棒了:将实时流量映射到您的暂存/ QA节点
  • 见 Envoy from Lyft

版本

  • 据我所知,协议版本控制是一个广泛存在的问题
    • 包括所有消息的版本标记
    • 包括兼容性逻辑
    • 当客户的请求无法处理时通知客户
    • 并对此进行检测,以便了解哪些系统必须升级

推广

  • 推广通常是您如何解决问题的方法
  • 花时间进行自动化,可靠的部署
    • 放大你做的其他事情
    • 节点平滑循环以防止流量中断
    • 这意味着您将同时运行多个版本的软件
      • 版本控制其丑陋的头部
    • 通知负载均衡器会退出
    • 协调以防止级联故障
  • 仅推出一小部分负载或部分用户
    • 逐渐增加新软件的用户数量
    • 当您看到错误时,可以还原或向前回滚
    • 考虑在生产环境中跟踪流量并比较旧/新版本
    • 确定新代码是否更快和正确的好方法

特性标志

  • 我们希望在部署后逐步推出变更集
  • 逐个引入功能,以了解它们对指标的影响
  • 逐步将负载从一个数据库转移到另一个数据库
  • 当推广出错时禁用功能
  • 我们希望在某些服务降级时获得部分可用性
  • 禁用代价昂贵的功能以在故障期间加速恢复
  • 使用高度可用的协调服务来确定要使用的代码路径或多久一次
  • 此服务应具有最小的依赖性
    • 不要使用主DB
  • 当出现问题时,您可以调整系统的行为
  • 协调服务停止时,失败安全

混沌工程

  • 打破生产中的东西
  • 迫使工程师适当地现在处理故障,而不是稍后对事件的响应
  • 识别关键路径中的意外依赖关系
    • “当新的统计数据服务出现故障时,需要使用API。是吗? 确定那是必要的吗?
  • 需要良好的仪表和警报,因此您可以衡量事件影响

  • 爆炸半径有限

    • 不要每五分钟核对整个数据中心
    • 但是每季度尝试一次
    • 不要破坏复制组中的许多节点
    • 一次只打破一小部分请求/用户

哦不,队列

  • 每个队列都是一个可怕的地方,可怕的错误
  • 没有节点有无限制的内存。你的队列必须有界限
  • 但有多大?没人知道
  • 在生产环境中检测你的队列以找出答案
  • 使用队列以平滑负载的波动
  • 以延迟为代价提高吞吐量
  • 如果您的负载高于容量,则没有队列可以节省您的费用
    • 当队列变满时,卸载负荷或施加背压
    • 测量这个
    • 当发生卸载负荷时,警铃响起
    • 背压作为上游延迟可见
  • 测量队列深度
    • 高深度是您需要添加节点容量的线索
    • 端到端队列延迟应小于波动时间尺度
    • 提高队列大小可能很诱人,但这是一个恶性循环
  • 所有这一切都很难。我没有你的好答案
    • 问Jeff Hodges为什么这很难:看他2013年的RICON West谈话
    • 见Zach Tellman —— 一切都会流动

回顾

运行分布式系统需要开发人员,QA和运营工程师之间的合作。静态分析和包括基于示例和属性的测试的测试套件可以帮助确保程序的正确性,但了解生产行为需要全面的测量和警报。成熟的分布式系统团队经常投资工具:流量阴影,版本控制,增量部署和功能标记。最后,队列需要特别小心。

进一步阅读

原文:https://github.com/iswade/distsys-class

翻译:王世德