内容分发

内容分发网络(CDN)

分发树

基本思路

graph LR
subgraph CDN节点
B
C
D
end
subgraph 全球客户
C1
C2
C3
C4
C5
C6
C7
C8
C9
end
A(CDN源服务器) -.-> |CDN节点|B(悉尼)
A -.-> |CDN节点|C(波士顿)
A -.-> |CDN节点|D(阿姆斯特丹)
B --> |全球客户|C1
B --> |全球客户|C2
C --> |全球客户|C3
C --> |全球客户|C4
C --> |全球客户|C5
C --> |全球客户|C6
D --> |全球客户|C7
D --> |全球客户|C8
D --> |全球客户|C9

设置CDN节点为web代理(实际不奏效)

  • 不同客户可能会使用不同代理
  • 从安全角度考虑,缓存机制通常不能跨组织共享
  • 可能存在多个CDN,客户端只能使用一个代理缓存,如何选择?
  • 客户端本身已经使用了代理,不会再自行配置web代理用于CDN用途

镜像(久经考验)

  • 源服务器网页上包含了指向不同镜像点的显式链接,通常告诉用户这些镜像点的位置
  • 典型应用是把一个大的软件包放在不同位置的镜像点上
  • 缺点:还是依靠用户做内容的分发(因为镜像点实际上是不同的web站点)

DNS重定向

  • 客服了上述两种方法的困难
  • 核心设计:域名服务器同时作为CDN节点运行

流程

  • DNS将www.google.com解析为一个IP地址,DNS查找过程不变
  • 请求域名服务器解析解析www.google.com时,域名服务器不是为每个请求返回相同的IP地址,而是根据发出请求的客户端IP地址返回最靠近客户端的CDN节点的IP地址

对等网络(P2P)

  • 核心技术:BitTorrent(最著名的协议)、DHT算法(分布式)
  • P2P网络的一个重大挑战在于当前面临各种各样的用户,而且用户具有不同的下载和上传容量,如何更好的使用带宽
  • P2P系统中,不可能有单个提供商具有监测整个系统的能力,但并不意味着P2P系统提供隐私保护,因为用户在一定程度上相互信任 (这不就是传说中的去中心化区块链核心技术吗!原来早就有了!)

BitTorrent

问题

  1. 一个对等用户如何找到具有自己想下载内容其他对等用户
  2. 对等用户们如何复制(上传)内容以便为大家提供高速下载
  3. 对等用户们如何相互鼓励上传内容给他人同时为自己下载内容

种子文件

  • 包含内容

    • 跟踪器,是一个服务器,将对等用户引导到种子文件内容
    • 大小相等片或**块(chunk)**清单,给出内容哈希
  • 种子文件维护着主动上传和下载该内容的所有其他对等用户列表(用户集群)

  • 当一个群首次形成时,有些用户必须拥有组成内容的所有块,这些对等用户就是所谓的种子(seeder)

集群维持

  • CDN节点是专门为用户提供内容而设置的,P2P节点不是。只下载不提供上传帮助的节点过多,系统将无法正常工作
  • 当前正在与一个节点交换块的对等节点称为非阻塞
  • BitTorrent奖励表现出良好上传行为的对等节点
  • 每个对等节点随机采样其他对等节点,给他们上传块同时检索他们的块,只与少数最高下载性能的对等节点们继续交易块。同时随机尝试其他对等节点,给新加入节点获得初始块的机会

核心难题

  • 跟踪器必须是分布式的,而传统思路下是集中式的
  • 需要一个完全分布式且表现良好的P2P索引
  • 由此引入Chord、CAN、Pastry、Tapestry、Kademlia(已被实际使用,称为分布式哈希表)等解决方案

DHT

结构

  • DHT需要在节点之间的通信采用一个正规结构。传统P2P系统中对等节点之间可以使用任意连接
  • DHT称为结构化的P2P网络

完全分布式且表现良好的P2P索引

  • 每个节点只需保留少量有关其他节点的信息(即保持最新索引代价不昂贵)
  • 每个节点可快速查看索引中表现
  • 每个节点可以同时使用索引

Chord

  • 总索引是所有用户集群的列表,计算机加入用户集群下载
  • 查询索引的关键字是内容的种子文件描述
  • 总索引被广播到DHT的n个参与节点上
  • 关键:在一个虚拟空间中使用标识符来导航索引而不是使用节点的IP或内容名称
  • 基本概念:哈希环、一致性哈希

哈希环

  • 由于不知节点标识符是否指向实际存在节点,只能线性查找,而线性查找效率低,故考虑维持一定的跳跃性查找数据结构(此处原则:每个节点保持足够的路由信息,以便它可以在常数跳数下从本地路由请求(到要访问的数据项)到适当的节点)
  • Chord解决方案是维持一个$start = k+2^m$的指取表数据结构,start为标识符,start=>value为该指向的实际节点
  • Redis的基础数据结构中包括有跳表,也有类似$2^m$的跳跃概念,但可以设定的更加随机一些

其他

  • DHT在其他分布式技术中也有广泛的应用

Amazon Dynamo

前置操作

背景

  • 需要存储对象较小(通常小于1MB)
  • Dynamo不提供任何数据隔离(Isolation)保证,只允许单一的关键更新
  • 许多服务只需要主键访问数据存储
  • 没有横跨多个数据项的操作,(大部分)也不需要关系方案
  • 理论基础:当网络故障时,强一致性和高可用性不可能性同时实现

原则目标

  • 与其不能确定答案的正确性与否,不如让该数据一直不可用直到它绝对正确时
  • Dynamo的目标是一个**“永远可写”(always writable)的数据存储(即数据存储的“写”是高可用**)
  • 设计原则:增量可扩展性、对称性、去中心化(对称性的延伸)、异质性(负载分配与服务器性能有关)
  • 零跳(zero-hop)的DHT,每个节点维护足够的路由信息从而直接从本地将请求路由到相应的节点
  • 提供最终一致性,从而允许更新操作可以异步传播到所有副本

其他系统

OceanStore

  • 允许并发更新的同时避免广域锁定(wide-area locking)固有的许多问题
  • 对于并发更新,经过一系列处理决定一个最终的更新顺序,并原子化的进行更新操作

技术点

  • 种子节点: 防止逻辑分裂(刚加入的节点A、B彼此都不知道彼此的存在,需要“初始化”信息)
  • 所有的节点,最终都会和种子节点协调成员关系

协调

  • 何时协调冲突:将协调冲突的复杂性推给“读”,以确保“写”永远不会拒绝。
  • 谁协调冲突:交给客户端基于最适合的客户体验决定(不通过数据存储来解决,因为数据存储能使用的策略通常较简单)
  • 为保证永远可写,在最新版本数据不可用时,操作可能会被加到旧版本之上,修改后结果当作一个新的且不可改变的数据版本,协调得到新版本
  • 版本分支可能发生在同一对象并发更新成功与失败的同时出现的情况,这种情况下系统没有凭据决定哪个版本,所以客户端必须执行协调,将多个分支演化后的数据崩塌(collapse)成一个合并的版本(语义协调),因此会出现类似“购物车删除了又出现”的情况
  • 单一地点序列化所有的写的做法会导致负荷分配不均

向量钟

  • 捕捉同一对象不同版本的因果关系
  • 因为将处理一致问题的复杂性推给了写操作,所以由read操作发现的发现向量钟描述不同版本存在冲突
  • 当向量时钟中(节点,计数器)对的数目达到一个阈值(如10),最早的一对将从时钟中删除

负载均衡方案

  • 服务端自带一个节点作为负载节点
  • √ 由客户端完成负载功能,客户端在先前的读操作中获得了集群一个节点包含的集群状态信息,根据信息直接路由请求到适当的协调程序节点
    • 优点是消除了负载平衡器额外的开销以及网络一跳
    • 缺点:客户端需要状态机(服务端集群节点相关信息),且需要实时更新状态机,由此又带来额外的网络通信开销

Merkle树

  • 每个节点为它承载的每个key范围(由一个虚拟节点覆盖 key 集合)维护一个单独的MerkleTree
  • 多个节点有一个key的信息,因此节点间交换key的Merkle树信息以检测是否发生变化

故障检测

  • 某种程度上说明了为什么不需要中心控制节点(按常规思路,需要有一个控制中心负责故障节点的处理):在没有客户端请求推动两个节点之间流量的情况下,节点双方并不真正需要知道对方是否可以访问或可以响应。
  • 显式的节点加入和离开的方法排除了对一个失败状态的全局性视图的需要。这是因为节点是是可以通过节点的显式加入和离开的方法知道节点永久性(permanent)增加和删除,而短暂的(temporary)节点失效是由独立的节点在他们不能与其他节点通信时发现的(当转发请求时)

实现具体

  • 组件:请求协调、成员、故障检测、本地持久化引擎

NWR模型

  • 低的W和R值会增加不一致性的风险,因为写请求被视为成功并返回到客户端,即使他们还未被大多数副本处理。
  • N的值决定了每个对象的耐久性。低的W和R值会增加不一致性的风险,因为写请求被视为成功并返回到客户端,即使他们还未被大多数副本处理。
  • 为了减少耐用性风险,一些方案中更细化的写操作要求协调员选择N个副本中的一个执行“持久写”,写操作的性能不会因为单一副本持久化写而受到影响(因为协调员不需要等待他)

客户端/服务端处理

  • 一个客户端驱动的协调方式的重要优势是不再需要一个负载平衡器来均匀分布客户的负载。公平的负载分布隐含地由近乎平均的分配key到存储节点的方式来保证的。

参考

Written with StackEdit.