在fedmsg之上的一层可靠性层
项目描述
什么是“可靠性”?
每隔几个月,就会有人讨论 fedmsg可靠性。通常,Fedora项目中的某个团队会考虑如何通过使用总线数据或向总线发送数据来简化他们的工作流程。我们最终在IRC上进行长时间的讨论,直到问题得到解决。但那次对话在freenode的ether中丢失了,下次当不同的团队有相同的问题时,我们不得不再次进行对话。
《fedmsg》是一组Python工具(一部分是库,另一部分是框架),我们将其用于Fedora基础设施中,以使系统进程能够发布和监听消息。我们通常称其为“消息总线”,但这并不是一个好的术语;它并不能准确地描述正在发生的事情。当进程想要发布fedmsg消息时,它会调用fedmsg.publish(...),这个函数从磁盘读取一些配置,如果尚未绑定,则将套接字绑定到端口,并将消息写入该端口。当另一个进程想要消费fedmsg消息时,它会调用fedmsg.tail_messages()(或将自己注册到已经监听的fedmsg-hub守护进程),然后连接一个套接字到另一个进程绑定的端口以接收消息。
下面是一些更详细的说明:调用fedmsg.publish并不管理套接字绑定和发送。它将任务转交给zeromq,zeromq又将任务转交给它管理的多个工作线程。zeromq保留一个内部队列,其中包含它被要求发送的消息,它会尽可能地快速发送。这里的关键点在于它是“发射后不管”:一个启用了发布fedmsg消息功能的Web应用会要求fedmsg库这样做,然后离开去完成其数据库事务或其他工作。fedmsg(实际上是这里的zeromq)在有空闲时间时隐秘地发送消息。
(顺便说一句,这个过程还涉及更多内容。出站消息使用加密证书进行签名。Python数据类型被序列化。入站消息被验证和反序列化,并检查授权策略,即谁允许签发哪些消息等。我们稍后再来讨论这个问题。)
让我们引用zguide
Most people who speak of "reliability" don't really know what they mean. We can only define reliability in terms of failure. That is, if we can handle a certain set of well-defined and understood failures, then we are reliable with respect to those failures. No more, no less. So let's look at the possible causes of failure in a distributed ZeroMQ application
在实践中,我们在Fedora基础设施中不会“丢弃”消息。我们已经进行了一系列实验,证明了这一点,足以让许多其他团队对其建立信心。
然而,从理论上讲,这里存在一个竞争条件。你可能会在有人连接来监听之前启动发布服务并发布消息。它会丢失。同样,在实践中,我们已经配置了适当的延迟和重连间隔(具有指数退避),以使这个问题不会出现。
尽管如此,仍可能存在网络分区——一个监听服务可能会突然发现自己被死掉的网关或配置错误的防火墙或死掉的VPN或灾难所隔开,在所有这些情况下:消息将会丢失。
设计可靠性
zguide提供了一个关于如何考虑和接近REQ-REP套接字模式的可靠性的良好描述。然而,在过去几年里,我们选择仅使用PUB-SUB套接字模式为fedmsg。在旁边添加另一个模式(REQ-REP)看起来是错误的(它目前非常简单)。
在gilmsg中的方法是,在现有的PUB-SUB fedmsg框架之上叠加一个可靠性检查。
以下是其大致工作原理
当调用gilmsg.publish(...)时,你必须声明一个必需的接收者列表。
启动一个后台线程,该线程在整个总线上监听ACK消息。
如果在给定超时时间内没有从所有接收者那里收到ACK,则引发一个Timeout异常。
它解决的故障情况
如果预期的接收者在发布消息时离线,我们将引发一个异常,这样源进程就可以失败并警告操作员。
如果预期接收者在线,但我们遭受网络分区,发布者会大声失败。
我们可能会出现假阴性。
如果预期接收者在线,但由于单向网络故障或配置错误而无法发送ACK,发布者会大声失败,尽管消费者确实收到了消息。
我们不可能出现假阳性。如果生产者完成执行,我们可以确信消费者收到了消息。
一些细节
我们确信ACK来自我们声明的接收者,因为ACK是加密签名的。
我们仔细区分我们对已发布的消息的ACK和其他我们一无所知的消息的ACK。
我们在发布原始消息之前在生产者上启动ACK监听套接字,这是一个线程。这是必要的,以避免在发布我们的原始消息但ACK在开始监听它之前返回的情况。
为什么不使用gilmsg?
fedmsg工作得非常好。我们在Fedora基础设施中有一个长的集成消息生产者列表。成功部分归因于fedmsg有多简单。它有助于松散耦合架构的兴起,这是原始目标。当某个Fedora黑客有了一个想法,他们就可以挂钩到总线上消费消息,而无需与任何人协商。他们可以在不经过委员会的情况下产生消息。
例如:你可以在你的本地盒子上启动Bodhi2 web应用,并且它将“发布到fedmsg”,而无需启动任何其他服务或类似的东西。它将在你的笔记本电脑上发布到fedmsg,没有人会监听,它也不在乎。
相比之下,使用gilmsg启用的服务
你的生产者将声明它们所需的消费者。
随着你添加更多消费者,你将不得不修补你的生产者,导致新的工作需要指数级的代码更改。
由于它将需要其他服务对其进行响应,因此你无法在没有整个生产环境副本的情况下在自己的盒子上运行或测试gilmsg启用的服务。
你的生产服务集群变得脆弱——一个服务死亡可能会导致其他服务崩溃。
gilmsg启用的服务是紧密耦合的。
一个美容性的注释
你在线上带来的gilmsg启用的生产者/消费者对越多,总线上的垃圾邮件就越多。它可以处理吞吐量(!)但当你添加更多时,datagrepper日志将变得不那么易读。
使用gilmsg
它与fedmsg核心API向后兼容。因此,首先编写使用fedmsg的脚本。如果在未来的某个时刻你决定必须有gilmsg提供的集合保证,那么就迁移到gilmsg。
使用Python中的.publish(..)发布
import gilmsg import fedmsg.config config = fedmsg.config.load_config() gilmsg.publish( topic="whatever", msg=dict(foo="bar"), recipients=( "bodhi-bodhi-backend01.phx2.fedoraproject.org", "shell-autocloud01.phx2.fedoraproject.org", ), ack_timeout=0.25, # 0.25 seconds **config)
使用shell中的gilmsg-logger发布,超时为3秒
echo testing | gilmsg-logger --recipients shell-value01.phx2.fedoraproject.org --ack-timeout 3
将上述内容与仅使用fedmsg发布进行比较。
使用Python中的.tail_messages(..)消费
import gilmsg import fedmsg.config config = fedmsg.config.load_config() target = "org.fedoraproject.prod.compose.rawhide.complete" for name, ep, t, msg in gilmsg.tail_messages(topic=target, **config): # The ACK has already been sent at this point. print "Received", t, msg['msg_id']
使用“Hub-Consumer”方法消费
import gilmsg class MyConsumer(gilmsg.GilmsgConsumer): topic = "org.fedoraproject.prod.compose.rawhide.complete" def consume(self, message): # The ACK has already been sent at this point. print "Received", message['topic'], message['msg_id']
将上述内容与仅使用fedmsg消费进行比较。
项目详情
gilmsg-0.1.2.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | bc108b3b45a3c3c1ca517bdfccb9f9cf3e3f4f5d724accf70ecf82acfd2b5166 |
|
MD5 | 66a2ee339b03659b613bd92cfb083ae8 |
|
BLAKE2b-256 | 209dc65d5cb72af1e030256de07edd4eee14427671c29a71bdd46e7c7b56d7b4 |