Gnocchi 7、Gnocchi中的水平扩展

1 聚合层
在聚合层中的数据处理是分发的:
一个metricd 服务可以对至少一个sack(并行处理单元)负责。而职责不是独占的。
例如不同的服务因为副本的原因可以对相同的sack负责。它自动带来了锁的概念,
例如当进有一个服务在给定时间处理sack的情况。

2 Coordinator
正如在许多类似的分布式架构,负载分发是通过coordinator来实现的。
每个数据处理器在配置步骤的时候连接到它,正如下面的代码一样:

# gnocchi.cli.metricd.MetricProcessBase#_configure
def _configure(self):
    member_id = "%s.%s.%s" % (socket.gethostname(),
                              self.worker_id,
                              # NOTE(jd) Still use a uuid here so we're
                              # sure there's no conflict in case of
                              # crash/restart
                              str(uuid.uuid4()))
    self.coord = get_coordinator_and_start(member_id,
                                            self.conf.coordination_url)

coordinator并不是Gnocchi中的一部分。它是被Tooz库提供的,并被包含在Openstack平台中。
正如你可以在上面的这段话中了解到,coordinator是通过配置项解决的。然而,如果
它并没有提供的,它会被设置为index storage的url。
处理进程连接到coordinator来获取待处理的sacks的锁:

# locking from gnocchi.cli.metricd.MetricProcessor#_run_job
for s in sacks:
    # TODO(gordc): support delay release lock so we don't
    # process a sack right after another process
    lock = self.incoming.get_sack_lock(self.coord, s)
    if not lock.acquire(blocking=False):
        continue

coordinator的另一个职责是群组管理。每个结点建立连接到一个coordinator,
这样就成为一个组的成员了。如下面代码所示:

# gnocchi.cli.metricd.MetricProcessor#_configure
def _configure(self):
    super(MetricProcessor, self)._configure()
 
    # create fallback in case paritioning fails or assigned no tasks
    self.fallback_tasks = list(
        six.moves.range(self.incoming.NUM_SACKS))
    try:
        self.partitioner = self.coord.join_partitioned_group(
            self.GROUP_ID, partitions=200)


metricd处理器通过发送心跳来通知coordinator它们仍然存活。
幸运地是,这个可以通过start_heart=True参数在建立连接地时候自动完成。

3 Partitioner
如果你仔细观察了MetricProcessor类地初始化步骤,你会注意到
join_partitioned_group方法返回了一个对象叫做partitioner。
它用于分配sacks到指定地结点,正如下面代码所示:

# gnocchi.cli.metricd.MetricProcessor#_get_sacks_to_process
def _get_sacks_to_process(self):
    try:
        self.coord.run_watchers()
        if (not self._tasks or
                self.group_state != self.partitioner.ring.nodes):
            self.group_state = self.partitioner.ring.nodes.copy()
            self._tasks = [
                i for i in six.moves.range(self.incoming.NUM_SACKS)
                if self.partitioner.belongs_to_self(
                    i, replicas=self.conf.metricd.processing_replicas)]

正如注意到地,在查询所有sacks(这里表示为_tasks字段)之前processor调用了run_watchers方法。
run_watchers调用确保了partitioner是在组中所有可能改变的情形下仍然是最新的。
它将会调用tooz.partitioner.Partitioner类中正确的回调函数。

_on_member_join(event) 会在组中有新的成员加入或者已经分配的对象改变的时候被调用
_on_member_leave(event) 上面的想反面,也会影响对象的。

Gnocchi,sacks分发时基于一致性哈希环的原理,在这篇文章:
https://www.waitingforcode.com/general-big-data/dynamo-paper-consistent-hashing/read
中展示了更多的细节。Gnocchi也使用基于Tooz库的实现,tooz.hashring.HashRing。

很明显,重复发明轮子并定义一个新的coordination和资源共享机制是有反面效果的。
起初,gnocchi时用了Tooz库提供的解决方案。正如我们在第一节了解到的,
分布式系统特性在处理层是可见的,该处理层就是一个node负责不同的sacks。
所有协调都被Tooz的coordinator实现了。
在coordinator的主要职责中,我们可以区分锁和群组管理。
它被叫做partitioner实现了,partitioner就是我们所知道的对象(即gnocchi中的sacks),
这些对象属于群组中的结点。

以上翻译自:
https://www.waitingforcode.com/time-series/horizontal-scalability-gnocchi/read
 

你可能感兴趣的:(gnocchi)