首先给出一些参考文献。
OpenStack 关于消息队列的官方文档,
AMQP and Nova, http://docs.openstack.org/developer/nova/devref/rpc.html, 第一张图有些问题,和我看的源码不一致,大概是版本没有更新吧。
Kombu Documentation OpenStack里调用RabbitMQ的一个类似框架的东西, http://kombu.me/index.html
OpenStack Nova internals of instance launching, http://www.laurentluce.com/posts/openstack-nova-internals-of-instance-launching, 关于OpenStack启动实例过程的一篇文章,我觉得对理解消息队列很有帮助
rabbitmq 主页, http://www.rabbitmq.com/
一位大牛的博客, http://blog.csdn.net/lynn_kong,他的博客对我帮助很大,中文资料里他对消息队列的解读应该是最全面也最到位的。我的这篇文章也就是狗尾续貂吧。我希望能从不一样的角度写一些东西,尽力吧~
关于RabbitMQ的特性,交换机(exchange)、队列(queue)、消息(message)等基本概念就不在这里唠叨了,网上百度都有。
如果要关注OpenStack的消息队列,首先要看的就是nova\openstack\common\rpc下面的文件:
impl_kombu.py就是主要执行消息调用的。另外,我们也可以看到OpenStack也支持例如zmq或者qpid。前不久我还看到国外有公司在考虑用0MQ作为OpenStack消息队列的实现。
注意在_init_.py的最后有一句:_RPCIMPL = importutils.import_module(CONF.rpc_backend),会通过配置文件调用具体的消息队列实现。同时我们也能看到里面使用了代理这个设计模式proxy.py。
下面具体分析一下impl_kombu.py里面的内容,openstack的消息队列全部用了里面的方法或者类
里面有一下几个类:
class ConsumerBase(object),基类,注意其初始化时会自动生成一个queue,还包含了consume方法,用来接收消息。其中就有message = self.channel.message_to_python(raw_message),message就能转换为python语句了。
class Direct/Topic/FanoutConsumer(ConsumerBase),子类
class Publisher(object),基类,因为Publisher和ConsumerBase new的方式不同,所以和ConsumerBase还是有区别的。比如需要事先给出routing key。。。然后包含了send方法,发布消息就是这么来的。
Direct/Topic/Fanout/NotifyPublisher(Publisher)
Connection(object)其实挺重要的这个类,每次都会用到这个类。比如发布消息,就会调用里面的publisher_send方法,可以看到他每次都会new出一个publisher类,然后用上面提到的send方法发布消息。
下面先直观地看一看OpenStack里面消息队列的情况吧,上图!
交换器
Consumer
队列情况
环境说明,直接用devstack自动化安装的,g版本。
我简单说一下哪些地方会用到消息队列吧,之前那篇关于实例启动过程的文章中已经提到一些了。
首先,nova/service.py中Service在使用start方法时,也就是启动nova时(其他组件cinder,quantum...也类似)都会先建立一个connection:self.conn = rpc.create_connection(new=True), 然后生成三个consumer: self.conn.create_consumer(self.topic, rpc_dispatcher, fanout=False)
node_topic = '%s.%s' % (self.topic, self.host)
self.conn.create_consumer(node_topic, rpc_dispatcher, fanout=False)
self.conn.create_consumer(self.topic, rpc_dispatcher, fanout=True)
运行self.conn.consume_in_thread()来监听是否有消息传入。也就是说启动每个组件时都会生成三个consumer,同时也就生成了三个队列。
另外,诸如nova/compute/rpcapi.py,nova/scheduler/rpcapi.py等很多地方都会用到cast或者call方法来发布消息(文件名大多为rpcapi.py),最终调用kombu里面的publisher_send方法把消息发布出去。
总结:
可以说OpenStack使用这种MOM模式的消息队列机制无疑是一个聪明的选择。其松耦合性以及动态可扩展性都非常符合开源云的要求。无论是开发还是运行,都会带了很多好处。唯一的缺点就是它是一个single point failure,如果RabbitMQ出错了,那整个OpenStack也就无法运行了。虽然RabbitMQ有一些持久化的机制,但其实效果一般,还影响效率。一般的做法是用HA(RabbitMQ官网提到了这样HA机制,我记得网上也有一两篇文章说过),做一个mirror,用两个RabbitMQ来保证系统的可靠性。我们下一步的计划希望能找出RabbitMQ错误的模式,并评估RabbitMQ在使用以及不使用HA时的可靠性。