rabbitMQ(消息队列)

我们的消息队列就是一个存储容器,我们的所有数据都是通过这个容器来进行交互,彼此之间的通信都是通过这个容器来进行,A与B进行通信的时候,我们的A与B之间你负责把数据扔到这个容器里面来,而我负责把数据从里面取出来,其他的我们都不用管,你直观扔进去,我直观取出来,这就是我们的消息队列的主要作用,以及核心概念.

首先软件安装步骤,这里介绍的是windows安装,其他的mac,Linux都有命令操作,博客也很多,很方便,这里就不过多赘述了。 

在安装rabbitmq之前,需要先安装erlang,rabbitmq是基于erlang平台运行的。

erlang安装步骤:

这是官网下载erlang.exe地址:

https://www.erlang.org/downloads

 

找到适合自己电脑的平台链接,点进去下载即可。我是windows64位。下载完成,根据提示操作。

erlang下载安装完成之后,是需要配置环境变量的。win7跟win10配置的方法差不太多。这里就详细讲解太多了。总之就是把erlang的安装bin目录的路径,放到系统path里面保存即可。

这个是我找的一篇安装erlang以及配置环境变量的博客,可以参考。https://www.jb51.net/softjc/664077.html        

最后在cmd里面,敲命令:

erl -version

能看到erlang的安装版本号,就说明已经安装成功。 

 

接下来,安装rabbitmq,官网下载rabbitmq.exe地址:

https://www.rabbitmq.com/download.html

 

如图,点击蓝色椭圆圈的链接,如下是我粘贴的直接下载的url,跟上面的链接是一样的。

https://github-production-release-asset-2e65be.s3.amazonaws.com/924551/e0c22d00-d649-11ea-8278-4632e4001be0?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20200818%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20200818T034318Z&X-Amz-Expires=300&X-Amz-Signature=22419897293644dc9ef0fc91eb7cbb4de821e6a34edb06ca7ef66e295e02f906&X-Amz-SignedHeaders=host&actor_id=0&repo_id=924551&response-content-disposition=attachment%3B%20filename%3Drabbitmq-server-3.8.6.exe&response-content-type=application%2Foctet-stream

 exe下载安装过程,就根据提示点击即可。最后一步是finish,点完finish就是完成了rabbitmq的安装。

最后,安装一下操作rabbitmq的模块包

pip install pika

如上的都安装完成,下面的代码块就能直接跑起来,看到效果了。

 

消息队列简单模式

所谓的简单模式就是生产者把数据扔给rabbitMQ,然后我们的消费者从里面去把数据取出来,生产者和消费者都需要指定这个消息队列的名字,通过这个指定的名字大家可以找到彼此,因为我们的rabbitMQ里面是有很多的队列的,所以需要用队列名字加以区分.还有一点是我们的生产者和消费者他们的启动顺序是不固定的,有可能是生产者先启动,也有可能是消费者先启动,所以我们需要在两边都加上队列名字

代码如下:

 1 import pika
 2 """
 3 我们的rabbitMQ是消息队列,它实际上就是一个容器,这个容器里面装的是数据,我们的应用程序之间的通讯是靠这个容器来进行的,
 4 A程序把数据交给这个消息队列,然后B程序跟A程序交互的时候就是通过这个消息队列来进行数据交流的,
 5 A程序和B程序彼此都是通过这个介质来进行交互的.这就是我们的消息队列的本质.
 6 """
 7 
 8 # 先建立本地连接
 9 connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
10 channel = connection.channel()
11 # channel.queue_declare(queue='hello')
12 
13 # 给消息队列命名为hello,queue就是给消息队列命名
14 channel.queue_declare(queue='hello')
15 # channel.queue_declare(queue='hello', durable=True)
16 channel.basic_publish(
17     exchange='',
18     routing_key='hello',  # 这里是通过消息队列的名字去查找到该消息队列
19     body='so what and ?',)  # 这里是消息队列里面的具体内容,消息队列里面会有很多队列,一个队列对应着一个内容,类似于键值对的方式
20     # properties=pika.BasicProperties(delivery_mode=2,))
21 """
22 这里的BasicProperties是基本化操作,我们的durable是跟下面的properties参数联合使用的,
23 它是使我们的数据持久化的一种操作,我们的customer如果崩坏的话,这个参数的设置是让数据留存,
24 待到我们的customer恢复之后,数据依然存在,如果不设置这个参数的话,我们的customer一旦崩坏,
25 所有数据就会荡然无存,就好比我们留在缓存里面的数据一旦切断电源就会消失一样的.
26 """
27 print(" [x] Sent 'so what'")
28 connection.close()  # 关闭连接
producer
 1 import pika
 2 
 3 connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
 4 channel = connection.channel()
 5 channel.queue_declare(queue='hello')  # 我们的生产者和消费者两个程序就类似于是socket不过我们的socket是有服务端和客户端的,
 6 # 有开启的先后顺序的,而我们的这个消息队列是没有的,我们不知道是A先把数据扔进到队列里面去,还是B先从里面去取出数据,所以我们的A和B就需要商量好
 7 # 彼此统一一个队列名,这样不论是谁先跟队列交互,都会找到同一个队列,不会找乱了,如果是A要取数据,找到这个队列数据没有在里面,那么就会一直等待,
 8 # 等到B把数据扔进来之后再取,彼此有这样一个默契才能保证交互顺利进行,所以就需要生产者和消费者都把队列的名字都写出来,
 9 
10 
11 def callback(ch, method, properties, body):
12     print(" [x] Received %r" % body)
13     import time
14     time.sleep(10)
15     print('ok')
16 
17     ch.basic_ack(delivery_tag=method.delivery_tag)
18 
19 
20 """
21 这里是消息队列里面的,简单方式,我们的程序从上往下加载,先加载上面的函数,然后先不执行它,
22 接着加载下面的channel实例化对象,然后走到括号里面的callback回调函数,然后才执行callback回调函数
23 里面的内容也就是我们上面定义的那个函数.它里面有两个参数,queue是指定我们的消息队列的队列名字,
24 然后no_ack这个参数,它主要的使用场景是,当我们的消费者遇到各种情况(比如:its channel is closed,
25 connection is closed,or TCP connection is lost)挂掉了,那么,RabbitMQ会重新将该任务添加到队列中,用以避免数据的丢失.
26 如果我们因为数据的丢失把no_ack的值改成了False,那么就需要发送应答给rabbitmq,消息处理完毕.
27 另外补充一点,我们的def callback回调函数里面需要加上一句话ch.basic_ack(delivery_tag=method.delivery_tag)
28 """
29 channel.basic_qos(prefetch_count=1)
30 # channel.basic_consume(callback,queue='hello',no_ack=True)
31 channel.basic_consume(callback, queue='hello', no_ack=False)
32 print(' [*] Waiting for messages.To exit press CTRL+C')
33 channel.start_consuming()
customer

 

我们的exchange交换机里面生产者和消费者程序启动顺序是有先后之分的,先把消费者启动,然后再启动生产者,

 

exchange模式下的fanout类型======>订阅分发

我们的exchange是交换机的意思,我们的简单模式里面就是一个生产者把数据放到rabbitMQ里面,然后一个消费者从里面把它取出来,但是我们的exchange里面就不是简单的一个生产者和一个消费者的关系了,我们的exchange模式下的fanout类型里面,我们生产者把数据造出来,然后放到我们的rabbitMQ里面的exchange这个交换机里面去,这个时候我们的消费者可以出现多个,每一个消费者创建都会在我们的rabbitMQ里面创建一个消息队列,一个消费者跟自己绑定一个消息队列,然后每一个跟消费者绑定好的消息队列就从我们的exchange交换机里面去获取数据,每一个消费者所绑定的消息队列都可以从交换机里面取到数据,如果交换机里面有数据的话,如果没有就一直hold住等着交换机给数据,一份数据可以通过这样的形式被复制成多份,有几个消费者就有几份数据,保证每一个消费者都能拿到数据,这就是我们的fanout类型所做的事情.我们把消费者程序脚本多运行几次就可以多生成一些消费者

如图:使用QQ截图,

 

exchange_fanout====>代码如下:

 1 # 生产者
 2 #!/usr/bin/env python
 3 import pika
 4 import sys
 5 
 6 connection = pika.BlockingConnection(pika.ConnectionParameters(
 7         host='localhost'))
 8 channel = connection.channel()
 9 
10 
11 # 在rabbitMQ中创建一个名为logs的交换机
12 channel.exchange_declare(exchange='logs',
13                          exchange_type='fanout')
14 
15 
16 
17 message ="Hello World!"
18 channel.basic_publish(exchange='logs',
19                       routing_key='',
20                       body=message)
21 print(" [x] Sent %r" % message)
22 connection.close()
producer
 1 # 消费者
 2 #!/usr/bin/env python
 3 import pika
 4 
 5 connection = pika.BlockingConnection(pika.ConnectionParameters(
 6         host='localhost'))
 7 channel = connection.channel()
 8 
 9 
10 
11 # 在rabbitMQ中创建一个名为logs的交换机
12 channel.exchange_declare(exchange='logs',
13                          exchange_type='fanout')
14 
15 
16 # 创建队列,名字是一个随机字符串
17 result = channel.queue_declare(exclusive=True)  # 这个exclusive这个参数=True是随机生成字符串,然后把这个随机的字符串赋值给
18 # 如下的变量,queue_name就是存储我们的队列名字用的
19 queue_name = result.method.queue
20 
21 
22 # 用queue_name绑定exchange为logs的交换机
23 channel.queue_bind(exchange='logs',
24                    queue=queue_name)
25 
26 print(' [*] Waiting for logs. To exit press CTRL+C')
27 
28 def callback(ch, method, properties, body):
29     print(" [x] %r" % body)
30 
31 channel.basic_consume(callback,
32                       queue=queue_name,
33                       no_ack=True)
34 
35 channel.start_consuming()
customer

 

 

exchange还有两种类型...

type=direct,根据关键字判定应该将数据发送至指定队列,具体点说就是我们的生产者会把数据放到我们的rabbitMQ里面的exchange交换机里面去,然后我们每产生一个消费者就会相应的在rabbitMQ里面创建一个消息队列,我们的每一个队列都会包含一个关键字,我们的交换机里面的关键字和消息队列里面的关键字要匹配上之后才会执行里面的方法,

代码如下:

 1 # 生产者
 2 #!/usr/bin/env python
 3 import pika
 4 import sys
 5 connection = pika.BlockingConnection(pika.ConnectionParameters(
 6         host='localhost'))
 7 channel = connection.channel()
 8 
 9 
10 # 在rabbitMQ中创建一个名为logs的交换机
11 channel.exchange_declare(exchange='direct_logs',
12                          exchange_type='direct')
13 
14 
15 
16 message ="Hello World!"
17 channel.basic_publish(exchange='direct_logs',
18                       routing_key='warning',
19                       body=message)
20 print(" [x] Sent %r" % message)
21 connection.close()
producer_direct
 1 # #!/usr/bin/env python
 2 import pika
 3 import sys
 4 
 5 connection = pika.BlockingConnection(pika.ConnectionParameters(
 6     host='localhost'))
 7 channel = connection.channel()
 8 
 9 channel.exchange_declare(exchange='direct_logs',
10                          exchange_type='direct')
11 
12 result = channel.queue_declare(exclusive=True)
13 queue_name = result.method.queue
14 
15 channel.queue_bind(exchange='direct_logs',
16                    queue=queue_name,
17                    routing_key="error")
18 
19 
20 channel.queue_bind(exchange='direct_logs',
21                    queue=queue_name,
22                    routing_key="warning")
23 #
24 channel.queue_bind(exchange='direct_logs',
25                    queue=queue_name,
26                    routing_key="info")
27 
28 print(' [*] Waiting for logs. To exit press CTRL+C')
29 
30 
31 def callback(ch, method, properties, body):
32     print(" [x] %r:%r" % (method.routing_key, body))
33 
34 
35 channel.basic_consume(callback,
36                       queue=queue_name,
37                       no_ack=True)
38 
39 channel.start_consuming()
customer_direct

 

 

 

 1 producer.py代码
 2 
 3 import pika
 4 import sys
 5 
 6 connection = pika.BlockingConnection(pika.ConnectionParameters(
 7         host='localhost'))
 8 channel = connection.channel()
 9 
10 channel.exchange_declare(exchange='direct_logs',
11                          exchange_type='direct')
12 # 重要程度级别,这里默认定义为 info
13 severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
14 message = ' '.join(sys.argv[2:]) or 'Hello World!'
15 
16 print(severity,"-----",message)
17 channel.basic_publish(exchange='direct_logs',
18                       routing_key=severity,
19                       body=message)
20 print(" [x] Sent %r:%r" % (severity, message))
21 connection.close()
producer_direct
 1 consumer.py代码
 2 
 3 import pika
 4 import sys
 5 
 6 connection = pika.BlockingConnection(pika.ConnectionParameters(
 7         host='localhost'))
 8 channel = connection.channel()
 9 
10 channel.exchange_declare(exchange='direct_logs',
11                          exchange_type='direct')
12 
13 result = channel.queue_declare(exclusive=True)
14 queue_name = result.method.queue
15 # 获取运行脚本所有的参数
16 severities = sys.argv[1:]
17 print(severities)
18 if not severities:
19     sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
20     sys.exit(1)
21 # 循环列表去绑定
22 for severity in severities:
23     channel.queue_bind(exchange='direct_logs',
24                        queue=queue_name,
25                        routing_key=severity)
26 
27 print(' [*] Waiting for logs. To exit press CTRL+C')
28 
29 def callback(ch, method, properties, body):
30     print(" [x] %r:%r" % (method.routing_key, body))
31 
32 channel.basic_consume(callback,
33                       queue=queue_name,
34                       no_ack=True)
35 
36 channel.start_consuming()
customer_direct

 

测试结果:

 1 测试如下: 
 2 producer端
 3 
 4 E:\python\py_dev\python\django_demo>python producer.py info
 5 info ----- Hello World!
 6  [x] Sent 'info':'Hello World!'
 7 1
 8 2
 9 3
10 4
11 consumer端
12 
13 E:\python\py_dev\python\django_demo>python consumer.py info
14 ['info']
15  [*] Waiting for logs. To exit press CTRL+C
16  [x] 'info':b'Hello World!'
测试结果如下

 

 

 

type=topic,让队列绑定几个模糊的关键字,之后生产者将数据发送到exchange,然后exchange将传入"路由值"和 ”关键字“进行匹配,匹配成功,则将数据发送到指定队列。然后我们指定的队列里面就会有数据,这一次的数据交互就完成了.

代码如下:

 1 模糊匹配topic
 2     # 表示可以匹配 0 个 或 多个 单词
 3     *  表示只能匹配 一个 单词
 4 1
 5 2
 6 生产端
 7 
 8 import pika
 9 import sys
10 
11 connection = pika.BlockingConnection(pika.ConnectionParameters(
12         host='localhost'))
13 channel = connection.channel()
14 
15 channel.exchange_declare(exchange='topic_logs',
16                          exchange_type='topic')
17 
18 routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
19 message = ' '.join(sys.argv[2:]) or 'Hello World!'
20 channel.basic_publish(exchange='topic_logs',
21                       routing_key=routing_key,
22                       body=message)
23 print(" [x] Sent %r:%r" % (routing_key, message))
24 connection.close()
producer_topic
 1 消费端
 2 
 3 import pika
 4 import sys
 5 
 6 connection = pika.BlockingConnection(pika.ConnectionParameters(
 7         host='localhost'))
 8 channel = connection.channel()
 9 
10 channel.exchange_declare(exchange='topic_logs',
11                          exchange_type='topic')
12 
13 result = channel.queue_declare(exclusive=True)
14 queue_name = result.method.queue
15 
16 binding_keys = sys.argv[1:]
17 if not binding_keys:
18     print("--binding_keys----")
19     sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0])
20     sys.exit(1)
21 
22 for binding_key in binding_keys:
23     channel.queue_bind(exchange='topic_logs',
24                        queue=queue_name,
25                        routing_key=binding_key)
26 
27 print(' [*] Waiting for logs. To exit press CTRL+C')
28 
29 def callback(ch, method, properties, body):
30     print(" [x] %r:%r" % (method.routing_key, body))
31 
32 channel.basic_consume(callback,
33                       queue=queue_name,
34                       no_ack=True)
35 
36 channel.start_consuming()
customer_topic

 

测试结果如下:

 1 生产者
 2 
 3 E:\python\py_dev\python\django_demo>python producer.py old.pyer
 4  [x] Sent 'old.pyer':'Hello World!'
 5 1
 6 2
 7 3
 8 消费者
 9 
10 E:\python\py_dev\python\django_demo>python consumer.py old.#
11  [*] Waiting for logs. To exit press CTRL+C
12  [x] 'old.pyer':b'Hello World!'
测试结果

 

 

关于我们的exchange交换机的topic类型测试代码,再来一份:

 1 # 生产者
 2 #!/usr/bin/env python
 3 import pika
 4 import sys
 5 
 6 connection = pika.BlockingConnection(pika.ConnectionParameters(
 7         host='localhost'))
 8 channel = connection.channel()
 9 
10 
11 # 在rabbitMQ中创建一个名为logs的交换机
12 channel.exchange_declare(exchange='topic_logs',
13                          exchange_type='topic')
14 
15 
16 message ="Hello World!"
17 channel.basic_publish(exchange='topic_logs',
18                       routing_key='old.egon',
19                       body=message)
20 print(" [x] Sent %r" % message)
21 connection.close()
22 
23 
24 # 执行结果如下:
25 # [x] Sent 'Hello World!'
producer_topic

 

 1 #!/usr/bin/env python
 2 import pika
 3 import sys
 4 
 5 connection = pika.BlockingConnection(pika.ConnectionParameters(
 6         host='localhost'))
 7 channel = connection.channel()
 8 
 9 channel.exchange_declare(exchange='topic_logs',
10                          exchange_type='topic')
11 
12 result = channel.queue_declare(exclusive=True)
13 queue_name = result.method.queue
14 
15 
16 channel.queue_bind(exchange='topic_logs',
17                    queue=queue_name,
18                    routing_key="old.*")
19 
20 
21 # channel.queue_bind(exchange='topic_logs',
22 #                    queue=queue_name,
23 #                    routing_key="warning")
24 #  只有符合模糊查询的正则匹配条件才可以执行相应的逻辑代码
25 
26 
27 print(' [*] Waiting for logs. To exit press CTRL+C')
28 
29 
30 def callback(ch, method, properties, body):
31     print(" [x] %r:%r" % (method.routing_key, body))
32 
33 
34 channel.basic_consume(callback,
35                       queue=queue_name,
36                       no_ack=True)
37 
38 channel.start_consuming()
39 
40 # 执行结果如下:
41 # [*] Waiting for logs. To exit press CTRL+C
42 # [x] 'old.egon':b'Hello World!'
customer_topic

 

posted @ 2018-04-01 23:09  dream-子皿  阅读(242)  评论(0)    收藏  举报