Hadoop(2.x)云计算生态系统

分发

Zookeeper目录

接下来讨论zookeeper用于在使用者和代理直接进行协调的结构和算法。

记法

当一个路径中的元素是用[xyz]这种形式表示的时,其意思是, xyz的值并不固定而且实际上xyz的每种可能的值都有一个zookpeer z节点(znode)。例如,/topics/[topic]表示了一个名为/topics的目录,其中包含的子目录同话题对应,一个话题一个目录并且目录名即为话题的名称。也可以给出数字范围,例如[0...5],表示的是子目录0、1、2、3、4。箭头->用于给出z节点的内容。例如/hello -> world表示的是一个名称为/hello的z节点,包含的值为"world"。

代理节点的注册

/brokers/ids/[0...N] --> host:port (ephemeral node)

上面是所有出现的代理节点的列表,列表中每一项都提供了一个具有唯一性的逻辑代理id,用于让使用者能够识别代理的身份(这个必须在配置中给出)。在启动时,代理节点就要用/brokers/ids下列出的逻辑代理id创建一个z节点,并在自己注册到系统中。使用逻辑代理id的目的是,可以让我们在不影响数据使用者的情况下就能把一个代理搬到另一台不同的物理机器上。试图用已在使用中的代理id(比如说,两个服务器配置成了同一个代理id)进行注册会导致发生错误。

因为代理是以非长久性z节点的方式注册的,所以这个注册过程是动态的,当代理关闭或宕机后注册信息就会消失(至此要数据使用者,该代理不再有效)。

代理话题的注册

/brokers/topics/[topic]/[0...N] --> nPartions (ephemeral node)

每个代理会都要注册在某话题之下,注册后它会维护并保存该话题的分区总数。

使用者和使用者小组

为了对数据的使用进行负载均衡并记录使用者使用的每个代理上的每个分区上的偏移量,所有话题的使用者都要在Zookeeper中进行注册。

多个使用者可以组成一个小组共同使用一个单个的话题。同一小组内的每个使用者共享同一个给定的group_id。比如说,如果某个使用者负责用三台机器进行某某处理过程,你就可以为这组使用者分配一个叫做“某某”的id。这个小组id是在使用者的配置文件中指定的,并且这就是你告诉使用者它到底属于哪个组的方法。

小组内的使用者要尽量公正地划分出分区,每个分区仅为小组内的一个使用者所使用。

使用者ID的注册

除了小组内的所有使用者都要共享一个group_id之外,每个使用者为了要同其它使用者区别开来,还要有一个非永久性的、具有唯一性的consumer_id(采用hostname:uuid的形式)。 consumer_id要在以下的目录中进行注册。 /consumers/[group_id]/ids/[consumer_id] --> {"topic1": #streams, ..., "topicN": #streams} (ephemeral node)

小组内的每个使用者都要在它所属的小组中进行注册并采用consumer_id创建一个z节点。z节点的值包含了一个的map。 consumer_id只是用来识别小组内活跃的每个使用者。使用者建立的z节点是个临时性的节点,因此如果这个使用者进程终止了,注册信息也将随之消失。

数据使用者偏移追踪

数据使用者跟踪他们在每个分区中耗用的最大偏移量。这个值被存储在一个Zookeeper(分布式协调系统)目录中。 /consumers/[group_id]/offsets/[topic]/[broker_id-partition_id] --> offset_counter_value ((persistent node)

分区拥有者注册表

每个代理分区都被分配给了指定使用者小组中的单个数据使用者。数据使用者必须在耗用给定分区前确立对其的所有权。要确立其所有权,数据使用者需要将其 id 写入到特定代理分区中的一个临时节点(ephemeral node)中。 /consumers/[group_id]/owners/[topic]/[broker_id-partition_id] --> consumer_node_id (ephemeral node)

代理节点的注册

代理节点之间基本上都是相互独立的,因此它们只需要发布它们拥有的信息。当有新的代理加入进来时,它会将自己注册到代理节点注册目录中,写下它的主机名和端口。代理还要将已有话题的列表和它们的逻辑分区注册到代理话题注册表中。在代理上生成新话题时,需要动态的对话题进行注册。

使用者注册算法

当使用者启动时,它要做以下这些事情:

  1. 将自己注册到它属小组下的使用者id注册表。
  2. 注册一个监视使用者id列的表变化情况(有新的使用者加入或者任何现有使用者的离开)的变化监视器。(每个变化都会触发一次对发生变化的使用者所属的小组内的所有使用者进行负载均衡。)
  3. 主次一个监视代理id注册表的变化情况(有新的代理加入或者任何现有的代理的离开)的变化监视器。(每个变化都会触发一次对所有小组内的所有使用者负载均衡。)
  4. 如果使用者使用某话题过滤器创建了一个消息流,它还要注册一个监视代理话题变化情况(添加了新话题)的变化监视器。(每个变化都会触发一次对所有可用话题的评估,以找出话题过滤器过滤出哪些话题。新过滤出来的话题将触发一次对该使用者所在的小组内所有的使用者负载均衡。)
  5. 迫使自己在小组内进行重新负载均衡。

使用者重新负载均衡的算法

使用者重新复杂均衡的算法可用让小组内的所有使用者对哪个使用者使用哪些分区达成一致意见。使用者重新负载均衡的动作每次添加或移除代理以及同一小组内的使用者时被触发。对于一个给定的话题和一个给定的使用者小组,代理分区是在小组内的所有使用者中进行平均划分的。一个分区总是由一个单个的使用者使用。这种设计方案简化了实施过程。假设我们运行多个使用者以并发的方式同时使用同一个分区,那么在该分区上就会形成争用(contention)的情况,这样一来就需要某种形式的锁定机制。如果使用者的个数比分区多,就会出现有写使用者根本得不到数据的情况。在重新进行负载均衡的过程中,我们按照尽量减少每个使用者需要连接的代理的个数的方式,尝尝试着将分区分配给使用者。

每个使用者在重新进行负载均衡时需要做下列的事情:

  1. 针对Ci所订阅的每个话题T
  2. 将PT设为生产话题T的所有分区
  3. 将CG设为小组内同Ci 一样使用话题T的所有使用者
  4. 对PT进行排序(让同一个代理上的各分区挨在一起)
  5. 对CG进行排序
  6. 将i设为Ci在CG中的索引值并让N = size(PT)/size(CG)
  7. 将从iN到(i+1)N - 1的分区分配给使用者Ci
  8. 将Ci当前所拥有的分区从分区拥有者注册表中删除
  9. 将新分配的分区加入到分区拥有者注册表中 (我们可能需要多次尝试才能让原先的分区拥有者释放其拥有权)

在触发了一个使用者要重新进行负载均衡时,同一小组内的其它使用者也会几乎在同时被触发重新进行负载均衡。