Python使用RabbitMQ(AMQP)极简例子

本文主要演示Windows下的,CentOS也提一下安装。

简介

RabbitMQ是一个异步的消息队列服务器程序,基于AMQP协议实现。客户端Client逻辑上分为消息生产者Producer和消费者ConsumerProducer生产消息Message到服务器Server,排成消息队列QueueConsumer消费Message,一些细节策略都是可以控制的。

安装

Windows下安装

首先去RabbitMQ官网下载并安装服务器,不过这之前先去Erlang官网安装Erlang/OTP支持。本文选择了otp_win64_22.0.exerabbitmq-server-3.7.17.exe。安装完毕,可以直接从开始菜单启动服务,一个简单的消息服务器Node就启动了。

image.png

如图所示,开始菜单有很多链接,命令提示符、启动、关闭等。通过命令提示符下输入命令,可以进行很多操作,如查看状态:

rabbitmqctl.bat status

以及关机:

rabbitmqctl.bat stop

CentOS下安装

这个很烦人。官网绕来绕去真让人头大。它先说有两种方法可以安装RabbitMQ

  • 第一种,通过Yum安装(推荐)。这种方法又分两小种,一种使用PackageCloud,一种使用Bintray。先看第一小种,噩梦的开始,我就没见过比这还垃圾的安装说明,这个脚本那个指示的,绕来绕去的啥也没干成。再看第二小种,链接打开就很缓慢。算了,或者这就是大佬吧。这种简单的方法,我放弃!
  • 第二种,下载rpm安装包,但是需要手动安装一些支持包,比如erlangsocatlogrotate。先装erlang,一开始就懵逼了:
    image.png

    下面明明4个选项,为啥是3个?不管了,试试第一个。他先说风凉话:RabbitMQ团队搞了个包,只包含RabbitMQ需要的组件,如果按照Erlang依赖被证明比较困难,这可能是最简单的方式。行吧,点进去一个erlang的github,一点指示都没有,怎么简单了?我只看到一个命令yum install erlang,难道是它?(读者先别装!)这么简单,那让我点进来干什么?直接告诉我不就完了。不管了,装完了,erl测试没问题,应该是装上了吧,只不过版本是R16B03-1,版本很旧,这是后话。
[root@master ~]# erl
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.4  (abort with ^G)
1> 
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution

继续,到哪来着?还要装socat,简单。

yum -y install socat

还有logrotate,发现已经装了。

image.png

好了,所谓的手动依赖都装完了,该是下载安装rpm了,点击跳转到下载处,发现这个。
image.png

它说某些情况,下载安装包手动安装可能更简单。吐血三升!先推荐第一种,这会儿又说第二种可能还行,行了,所有的安装方法都比较简单,听你的。不管了,下载吧。

wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.7.18/rabbitmq-server-3.7.18-1.el7.noarch.rpm

耐心下载完毕。结果安装失败,说erlang的版本不对。


image.png

奇怪,按照github的说明,应该默认就是最新版本的。没办法,于是去github的release里下载了一个版本:


image.png

继续安装:
yum -y install erlang-22.1-1.el7.x86_64.rpm

报错冲突,也算是意料之中。

image.png

搜了下,最方便的方法就是指定卸载老版本,必须特别精准,否则删不干净。至于怎么找到这个名称,我也没想出来,多组合试试吧。

yum remove erlang-erts-R16B-03.18.el7.x86_64

然后重新安装新版本。

yum -y install erlang-22.1-1.el7.x86_64.rpm

继续安装rabbitMQ:

yum -y install rabbitmq-server-3.7.18-1.el7.noarch.rpm

搞定!添加开机启动、启动、查看状态,素质三连。

systemctl enable rabbitmq-server.service
systemctl start rabbitmq-server.service
systemctl status rabbitmq-server.service

查看下当前用户:

[root@master ~]# rabbitmqctl list_users
Listing users ...
user    tags
guest   [administrator]

添加个用户,并设为管理员(添加标签即可)。

[root@master ~]# rabbitmqctl add_user admin admin
Adding user "admin" ...
[root@master ~]# rabbitmqctl set_user_tags admin administrator
Setting tags for user "admin" to [administrator] ...
[root@master ~]# rabbitmqctl list_users                       
Listing users ...
user    tags
admin   [administrator]
guest   [administrator]

配置vhost '/'下的权限,并查看权限:

rabbitmqctl set_permissions -p '/' admin '.*' '.*' '.*'
rabbitmqctl list_permissions

其实这些,都可以去后台管理配置。打开后台管理:

rabbitmq-plugins enable rabbitmq_management

然后就可以去ip:15672访问管理,记住把云服务的安全组规则配置好,如果有iptables这样的防火墙,也要把端口打开。

好了,CentOS就说这么多吧,总结就是:

  • 去github下载新版本的erlang,yum安装
  • 安装socat,logrotate
  • 下载RabbitMQ并安装。
    差评官网安装指南。

管理

在命令提示符下,启用管理插件:

rabbitmq-plugins enable rabbitmq_management

浏览器输入http://localhost:15672/即可访问后台管理页面,默认可以使用guest/guest登录(guest只能在localhost登录),这是管理员权限,建议删掉这个用户或者至少在管理页面改个密码。具体步骤是在Admin分页添加一个新的用户,输入信息,赋予admin权限标签,然后点击这个用户,在permission栏把访问虚拟主机/的权限给加上,然后就可以删除掉原先的guest了。

image.png

如下命令可查看用户列表及权限:

rabbitmqctl.bat list_users

试用

服务器可以只有一个节点Node(或叫代理Broker),也可以是集群,本文使用单节点。另外,服务器至少有一个虚拟主机Virtual Host,这是一个逻辑概念,可以添加Virtual Host用于不同后台用户的权限控制(待研究),本文仅使用/这一个Virtual Host,跟服务器概念基本等价。

image.png

Virtual Host下可以有多个交换机Exchange和消息队列Queue,每个Queue可以添加一个或多个Binding路由规则将其绑定到Exchange上。Binding根据ExchangeType,以及路由键Routing Key来决定消息从Exchange传到哪个Queue
image.png

概念有点多,试一下流程:

  • Exchanges分页新建交换机test_ex,类型Typedirect,意思是路由键必须完全相同,才会将消息转到符合规则的Queue。另外还有比如分发fanout类型,忽视路由键,只要绑定就转发到队列;主题topic模型,支持通配符(#、*)匹配路由键,很方便。可以使用如下命令查看交换机:
rabbitmqctl.bat list_exchanges
image.png
  • Queues分页新建一个队列test_queue,建完后点击该队列,继续添加一条binding规则,与test_ex以路由键test.msg绑定在一起。注意:建立交换机或队列的时候,都有个Durability选项,这个是选择是否持久化,也就是重启后还在不在,binding只能在持久化属性相同的通道和队列之间绑定。可以使用如下命令查看绑定规则:
rabbitmqctl.bat list_bindings
image.png

image.png
  • 回到Exchanges分页,点击test_exPublish message栏输入并发布一条消息Hello World!Deliver mode用来控制消息是否持久化,为了速度,一般不持久化消息。

    image.png

  • 回到Queues分页,点击test_queue,然后获取消息,成功!

    image.png

Python

首先安装pika模块:

pip install pika

写一个消息生产客户端producer.py,代码如下:

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
    'localhost', 
    credentials=pika.credentials.PlainCredentials('admin', 'yourpassword'))) # 建立连接,默认是guest:guest可省略,由于之前guest被删掉了,所以必须给出参数
channel = connection.channel() # 初始化通道

channel.exchange_declare(
    'test_ex',
    durable=True) # 如果没有,则新建交换机;如果已有,则忽略,但参数要一致
channel.basic_publish(
    exchange='test_ex', 
    routing_key='test.msg',
    body='Hello World from Python!') # 发布消息,如果不指定交换机,就会使用默认交换机,不建议这样
    
channel.close() # 好像可以省略
connection.close()

这时已经可以再管理后台获取消息了,如图:
image.png

当然也可以写一个消息接收端consumer.py,代码如下:

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
    'localhost', 
    credentials=pika.credentials.PlainCredentials('admin', 'admin'))) # 建立连接,默认是guest:guest可省略,由于之前guest被删掉了,所以必须给出参数
channel = connection.channel() # 初始化通道

# 以下可在管理后台操作
channel.exchange_declare(
    'test_ex',
    durable=True)
channel.queue_declare(
    'test_queue',
    durable=True)
channel.queue_bind(
    queue='test_queue',
    exchange='test_ex',
    routing_key='test.msg')

# 接受消息回调函数
def callback(ch, method, properties, body):
    print(f'Received {body}')

channel.basic_consume(queue='test_queue', on_message_callback=callback)
print('Consuming...')
channel.start_consuming()

确认消息

发现上述代码在收到消息后,并不会删除消息,类似管理后台的requeue true,也就是消息在接收后,又原路放回去了。

image.png

注意,requeue的消息在下次被接受,会有一个redelivered标志位值为真,在管理后台也能看出来:
image.png

那怎么在接受消息就删除呢?修改参数auto_ack即可:

channel.basic_consume(
    queue='test_queue', 
    on_message_callback=callback, 
    auto_ack=True)

还使用basic_get()主动获取排在第一的消息,并自动或手动发送ack确认消息收到。

# 有消息就返回(method, properties, body),否则(None, None, None)
# 自动发送ack
msg = channel.basic_get('test_queue', True) 
# 手动发送ack,从msg的method获取delivery_tag
msg = channel.basic_get('test_queue') 
channel.basic_ack(msg[0].delivery_tag)

另外,可以通过设置服务质量QoS来防止接收端积压消息,代码如下:

channel.basic_qos(prefetch_count=1)
channel.basic_consume(
    queue='test_queue', 
    on_message_callback=callback)

这样,客户端每次只预取1个消息,如果不发送ack,服务端是不会继续派发消息的。


够用了。

你可能感兴趣的:(Python使用RabbitMQ(AMQP)极简例子)