Neutron各组件之间的通信主要是通过RabbitMQ实现的,所以为了更好的学习Neutron,首先需要先熟悉RabbitMQ的常规用法。学习过Java或者写过Web应用的开发人员相信对MQ耳熟能详。在Web应用的世界中,MQ的使用实在太普遍了。常见的MQ系统有ActiveMQ,Kafka,Qpid,redis,ZeroMQ,RabbitMQ等等。在这些MQ实现中,有些严格遵守了AMQP标准,有些则自成一派。
接下来让我们认识一下RabbitMQ的特点。
-
除了Qpid之外,RabbitMQ是唯一实现了AMQP标准的代理服务器。
-
RabbitMQ基于Erlang实现,所以RabbitMQ集群非常简单。
-
RabbitMQ相对更可靠,更能防止崩溃。
让我们使用RabbitMQ实现发送“hello world”消息,并使用服务端打印消息:
-
安装RabbitMQ
以centos 7系统为例,在线安装RabbitMQ
yum install rabbitmq-server
-
启动RabbitMQ
systemctl start rabbitmq-server
-
编写"hello world"生产者,并保存为producer.py
import pika,sys credentials = pika.PlainCredentials('guest','guest') conn_params = pika.ConnectionParameters('192.168.90.26',credentials= credentials) conn_broker = pika.BlockingConnection(conn_params) channel = conn_broker.channel() channel.exchange_declare(exchange='hello-exchange',type='direct',passive=False,durable=True,auto_delete=False) msg = sys.argv[1] msg_props = pika.BasicProperties() msg_props.content_type = 'text/plain' channel.basic_publish(body=msg,exchange='hello-exchange',properties=msg_props,routing_key='hola')
使用pika模块与RabbitMQ建立通信。第二到第四行用于获取RabbitMQ的信道channel,然后通过channel声明交换器,类型为direct,且可持久化,不能自动删除队列。最后一行调用channel的basic_publish方法发送消息msg到hello-exchange交换器和路由key是hola的队列中。
-
编写“hello world”消费者,并保存为consumer.py
import pika credentials = pika.PlainCredentials('guest','guest') conn_params = pika.ConnectionParameters('192.168.90.26',credentials= credentials) conn_broker = pika.BlockingConnection(conn_params) channel = conn_broker.channel() channel.exchange_declare(exchange='hello-exchange',type='direct',passive=False,durable=True,auto_delete=False) channel.queue_declare(queue='hello-queue') channel.queue_bind(queue='hello-queue',exchange='hello-exchange',routing_key='hola') def msg(channel,method,haeder,body): channel.basic_ack(delivery_tag=method.delivery_tag) if body == 'quit': channel.basic_cancel(consumer_tag='hello-consumer') channel.stop_consuming() else: print body channel.basic_consume(msg,queue='hello-queue',consumer_tag='hello-consumer') channel.start_consuming()
前六行与生产者相同,第七行使用channel声明一个叫hello-queue的队列,并通过叫hola的路由key把hello-queue队列和hello-exchange交换器绑定。倒数第二行是订阅队列。
-
启动消费者
python consumer.py
-
执行生产者
python producer.py 'hello world'
-
查看消费者进程打印结果
以上就是一个利用RabbitMQ实现的打印“hello world”的消息队列服务。
上述内容频频出现交换器和绑定等概念。其实,交换器和绑定这些概念都来自于AMQP规范。消息投递的流程是这样的:首先消息被发送到交换器,然后根据确切的规则RabbitMQ把消息投递到消息队列,最后消息队列中的消息被订阅的消费者消费。其中,这些规则被称为路由键(routing key)。当我们把消息发送到MQ时,消息将会拥有一个路由键--即便是空的--RabbitMQ也会将其和绑定的路由键进行匹配。如果相匹配,那么消息将会被投递到该队列。如果不匹配任何绑定模式,那么消息进入“黑洞”。
通过在交换器和队列之间添加绑定动作,AMQP可以适应更多的应用场景,例如发布/订阅或者多播等等。RabbitMQ可以根据路由键把消息从交换器路由到队列,也可以把消息投递到多个队列。这些功能可以通过不同的类型的交换器实现。AMQP规范定义了四种交换器类型:direct,fanout,topic和header。
-
direct类型
direct类型是最简单的类型:如果路由键匹配,那么消息就被投递到对应的队列。
-
fanout类型
fanout交换器会把消息投递到所有附加在此交换器上的队列,类似于消息广播。
-
topic类型
topic交换器能够把不同路由键的消息投递到相同的队列。原因主要是因为topic类型的路由键能够使用正则表达式。凡是匹配该正则表达式的消息的路由键都会被路由到相同的队列。
-
header类型
header交换器允许匹配AMQP消息的header而非路由键。除此之外和direct交换器完全一致。
另外,RabbitMQ还支持多租户模式。实现方式是采用虚拟主机vhost。每一个vhost本质上是一个mini版的RabbitMQ服务器,拥有自己的队列、交换器和绑定等等。更重要的是,它拥有自己的权限机制。因此,RabbitMQ服务器能够运行众多的应用程序,而不必担心应用之间相互干扰。
RabbitMQ默认vhost是/,查看方式如下:
[root@controller ~]# rabbitmqctl list_vhosts
Listing vhosts
/
添加vhost:
[root@controller ~]# rabbitmqctl add_vhost neutron
Creating vhost "neutron"
[root@controller ~]# rabbitmqctl list_vhosts
Listing vhosts
/
neutron
使用vhost:
conn_params = pika.ConnectionParameters('192.168.90.26',credentials= credentials, virtual_host='neutron')
RabbitMQ的虚拟机概念类似于Linux中的命名空间,对不同资源进行了隔离。
小结
RabbitMQ还有许多有用的特性,例如:可持久化,类似于访问控制列表(ACL)风格的权限系统,天然支持分布式系统调用等等。理解以上这些特性,能够帮助我们快速理清Neutron各组件之间的通信模式,对分析Neutron源码事半功倍。
如果对云计算感兴趣,可以关注我的微信公众号: