横向扩展聊天日志工作者
Scaling chat log workers horizontally
我已经考虑了很多,但无法想出一个令我满意的解决方案。
基本上这就是问题所在:将 100k+ 聊天(有些慢,有些快)记录到 cassandra 中。所以保存 userId、channelId、timestamp 和消息。
Cassandra 已经支持开箱即用的水平缩放,我这里没问题。
现在我读取这些聊天的软件是通过 TCP (IRC) 进行的。前 1k 个频道通常每秒 300 条消息,而根据我的实验,1 个 IRC 连接无法处理。
我现在想要构建的是记录器的多个实例(使用 Docker/Kubernetes)并在它们之间分担负载。所以理想情况下,如果我有 4 个工人和 1k 个聊天(示例)。他们每人至少加入 250 个频道。我说至少是因为我想要可选的冗余,这样我就可以在同一个聊天中有 2 个记录器,以确保没有消息丢失。
没有重复的问题,因为所有消息都有唯一的 ID。
现在我将如何最好地动态共享工作人员之间加入的当前频道。我想避免拥有主人或控制点。添加更多工作人员也应该很容易,然后减少其他工作人员的负担。
关于这种行为有什么好的文章吗?也许已经定义好的概念或协议?就像我说的,我想避开另一个中央控制点,所以没有 rabbitmq、redis 或其他任何东西。
编辑:我研究过 Raft 共识算法之类的东西,但我认为它没有意义,因为我不希望我的客户就共享状态达成一致,而是在他们之间划分状态 "equally".
我认为在这种情况下寻找现有算法的描述可能不是很有用:问题不够复杂和通用,不值得发表。
如上所述,可以通过使用 Cassandra 本身作为调解器并在工作人员之间共享聊天频道分配信息来解决问题。
因此(微不足道的部分)频道将具有 ID 和分配的工作人员 ID,加上可选的冗余情况 - 所需的工作人员数量(2 个或您想要处理此聊天的任何数量的工作人员)。 Worker 在将自己分配给频道之前会检查是否已经有足够的受让人。如果是这样将继续到下一个频道。如果不是,则将自己分配给频道。这是选项之一(或者,您可以让工作人员持有通道 ID,但由于冗余很少见,因此这种方式似乎更简单)。工作人员将有他们可以处理的频道限制,并且不会尝试通过分配更多频道来超过它。
现在只需要处理同一个channel分配的worker过多,超标耗尽worker capacity的情况,监控所有同一个channel。否则,如果它们同时启动,通道分配的工作人员可能会超出需要。即使在所描述的情况下不太可能造成真正的问题(只是比要求的冗余多一点),您可以通过优先处理工作人员来解决这个问题。就像在加拿大雇用学校老师一样,BC 是按资历进行的 - 最年长的人先找到工作,除了在这里它是由工人自己自愿完成的,而不是学校管理部门。这意味着每个 worker 都必须检查所有分配给它的通道,如果此时 worker 的数量超过需要,将检查它是否在所有 assignees 中具有最小的优先级。如果是,它将辞职 - 删除自身并停止处理该频道。
这需要为工人分配不同的优先级,这可以在生成它们时轻松实现,只需将每个工人设置为下一个序列号(最老的优先级最高,或者 v.v 如果您担心老的、可能快要死的工人承担了所有的工作,并且更喜欢新的工作人员在还新鲜的时候承担更多的工作)。更详细地说,这也可以通过使用 Cassandra Lightweight transactions as described in one of the answers here (the one by AlonL) 来完成。只有少数(你提到了 ~4)工作人员应该工作,并且其他答案中提到的关于扩展的担忧对于一些整数优先级来说没什么大不了的。此外,要求工作人员在初始化时自行分配一个随机的 32 位整数优先级,而不是顺序编号分配,实际上没有发生冲突的机会,因此循环 "until no collisions" 应该在第一次迭代时退出(这将使第二次迭代很少需要显式测试的代码路径)。
诀窍基本上是限制需要同步的数据量,并将监管的负担放在工作人员身上。不需要共识算法,因为没有太多的复杂性,我们也没有处理大量潜在的欺诈工人,试图在更高级的同行之前获得分配。
我唯一要提的问题是,如果通道脱机导致工作人员停止处理,则可能存在隐式工作人员轮换。下次频道上线时,您将获得不同的工作人员分配。
我已经考虑了很多,但无法想出一个令我满意的解决方案。
基本上这就是问题所在:将 100k+ 聊天(有些慢,有些快)记录到 cassandra 中。所以保存 userId、channelId、timestamp 和消息。
Cassandra 已经支持开箱即用的水平缩放,我这里没问题。
现在我读取这些聊天的软件是通过 TCP (IRC) 进行的。前 1k 个频道通常每秒 300 条消息,而根据我的实验,1 个 IRC 连接无法处理。
我现在想要构建的是记录器的多个实例(使用 Docker/Kubernetes)并在它们之间分担负载。所以理想情况下,如果我有 4 个工人和 1k 个聊天(示例)。他们每人至少加入 250 个频道。我说至少是因为我想要可选的冗余,这样我就可以在同一个聊天中有 2 个记录器,以确保没有消息丢失。 没有重复的问题,因为所有消息都有唯一的 ID。
现在我将如何最好地动态共享工作人员之间加入的当前频道。我想避免拥有主人或控制点。添加更多工作人员也应该很容易,然后减少其他工作人员的负担。
关于这种行为有什么好的文章吗?也许已经定义好的概念或协议?就像我说的,我想避开另一个中央控制点,所以没有 rabbitmq、redis 或其他任何东西。
编辑:我研究过 Raft 共识算法之类的东西,但我认为它没有意义,因为我不希望我的客户就共享状态达成一致,而是在他们之间划分状态 "equally".
我认为在这种情况下寻找现有算法的描述可能不是很有用:问题不够复杂和通用,不值得发表。
如上所述,可以通过使用 Cassandra 本身作为调解器并在工作人员之间共享聊天频道分配信息来解决问题。
因此(微不足道的部分)频道将具有 ID 和分配的工作人员 ID,加上可选的冗余情况 - 所需的工作人员数量(2 个或您想要处理此聊天的任何数量的工作人员)。 Worker 在将自己分配给频道之前会检查是否已经有足够的受让人。如果是这样将继续到下一个频道。如果不是,则将自己分配给频道。这是选项之一(或者,您可以让工作人员持有通道 ID,但由于冗余很少见,因此这种方式似乎更简单)。工作人员将有他们可以处理的频道限制,并且不会尝试通过分配更多频道来超过它。
现在只需要处理同一个channel分配的worker过多,超标耗尽worker capacity的情况,监控所有同一个channel。否则,如果它们同时启动,通道分配的工作人员可能会超出需要。即使在所描述的情况下不太可能造成真正的问题(只是比要求的冗余多一点),您可以通过优先处理工作人员来解决这个问题。就像在加拿大雇用学校老师一样,BC 是按资历进行的 - 最年长的人先找到工作,除了在这里它是由工人自己自愿完成的,而不是学校管理部门。这意味着每个 worker 都必须检查所有分配给它的通道,如果此时 worker 的数量超过需要,将检查它是否在所有 assignees 中具有最小的优先级。如果是,它将辞职 - 删除自身并停止处理该频道。
这需要为工人分配不同的优先级,这可以在生成它们时轻松实现,只需将每个工人设置为下一个序列号(最老的优先级最高,或者 v.v 如果您担心老的、可能快要死的工人承担了所有的工作,并且更喜欢新的工作人员在还新鲜的时候承担更多的工作)。更详细地说,这也可以通过使用 Cassandra Lightweight transactions as described in one of the answers here (the one by AlonL) 来完成。只有少数(你提到了 ~4)工作人员应该工作,并且其他答案中提到的关于扩展的担忧对于一些整数优先级来说没什么大不了的。此外,要求工作人员在初始化时自行分配一个随机的 32 位整数优先级,而不是顺序编号分配,实际上没有发生冲突的机会,因此循环 "until no collisions" 应该在第一次迭代时退出(这将使第二次迭代很少需要显式测试的代码路径)。
诀窍基本上是限制需要同步的数据量,并将监管的负担放在工作人员身上。不需要共识算法,因为没有太多的复杂性,我们也没有处理大量潜在的欺诈工人,试图在更高级的同行之前获得分配。
我唯一要提的问题是,如果通道脱机导致工作人员停止处理,则可能存在隐式工作人员轮换。下次频道上线时,您将获得不同的工作人员分配。