RabbitMQ 使用详细介绍
1. 实现最简单的队列通信
2. producer端
- # !/usr/bin/env python
- import pika
- #通过这个实例,先去建立一个socket,默认端口15672
- connection = pika.BlockingConnection(
- pika.ConnectionParameters('localhost')
- )
- channel = connection.channel() #声明一个管道,在管道里发消息
- # 在管道里面声明queue队列。
- channel.queue_declare(queue='hello')
- # n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
- #在管道里发消息
- channel.basic_publish(exchange='',
- routing_key='hello', #队列名字
- body='Hello World!') #消息
- print(" [x] Sent 'Hello World!'")
- connection.close() #直接把链接关闭,不需要关闭管道。
3.consumer端
- # _*_coding:utf-8_*_
- import pika
- connection = pika.BlockingConnection(pika.ConnectionParameters(
- 'localhost')) #建立链接
- channel = connection.channel() #建立管道
- # You may ask why we declare the queue again ‒ we have already declared it in our previous code.
- # We could avoid that if we were sure that the queue already exists. For example if send.py program
- # was run before. But we're not yet sure which program to run first. In such cases it's a good
- # practice to repeat declaring the queue in both programs.
- # 为什么又声明了一个‘hello’队列?
- # 如果确定已经声明了,可以不声明。但是你不知道那个机器先运行,所以可以再次声明,这样就不会报错。
- channel.queue_declare(queue='hello') #说明从哪个队列里面收消息
- # ch 管道的内存对象地址
- def callback(ch, method, properties, body):
- print('---->',ch,method,properties)
- print(" [x] Received %r" % body)
- #开始消费消息
- channel.basic_consume(callback, #如果收到消息,就调用callback函数来处理消息
- queue='hello', #表示从哪个队列里收消息
- no_ack=True)
- print(' [*] Waiting for messages. To exit press CTRL+C')
- channel.start_consuming() #之前的只是声明了语法,这条语句才是真正开始收数据
4.运行
5. 上面实现的实例是一个生产者,一个消费者模型。下面尝试建立一个生产者,多个消费者的模型。
启动3个客户端, consumer1,consumer2, consumer3。再启动producer,producer发的第1条消息被consumer1收到了,producer发的第2条消息被consumer2收到了,producer发的第3条消息被consumer3收到了,可以看出一对多情况下,默认采用的是轮询机制。
6. 如果消费者收到消息以后,处理的过程中down机了。当生产者收到消费者端发来的消息处理完的确认函以后,生产者才会把消息从队列中删除。
如果处理的过程中突然down机了,客户端就没法发确认函了。生产者就认为没有处理完。
consumer.py 中 no_ack=True # no acknowledge 不管有没有处理完,都不会给生成者端发消息.
一般都会注释掉这句,#no_ack=True.意思就是处理完了,请给我一个确认函。然后服务器就把把这个任务从队列里面删除掉。
- 假如消费者处理消息需要15秒,如果当机了,那这个消息处理明显还没处理完,怎么处理? (可以模拟消费端断了,分别注释和不注释 no_ack=True 看一下) 你没给我回复确认,就代表消息没处理完。
上面的效果消费端断了就转到另外一个消费端去了,但是生产者怎么知道消费端断了呢? 因为生产者和消费者是通过socket连接的,socket断了,就说明消费端断开了。
- 如果服务器端发了一个任务给消费者1,消费者1在处理的过程中,突然down机了(socket断了)。服务器端没有收到确认函,则这个任务还存在于队列中,就会发给消费者2进行处理,直至处理完毕为止。通过这种机制,就会保证消息会被全部处理完。
7. 在安装目录下的 sbin文件夹下,有一个rabbitmqctl的文件,它是管理rabbitMQ的一个工具。
rabbitmqctl.bat list_queues 用这条命令可以查看当前有几个队列,以及每个队列里面存在的消息个数。
8. 客户端处理完消息以后,必须主动跟服务器端确认。否则如果客户端处理完消息以后,又拿着这条消息去干别的事情的话,服务器端一直收不到确认函,就不合理了。
9.如果队列里面还有一条消息,此时服务器端down机了,会发生什么呢?
尝试down掉服务器端机器,rum中输入services.msc,然后找到这个程序,右击点停止。
然后重启,测试效果:发现队列都丢了。因为队列是保存在内存中的。
10. 为了保证队列不再丢失,在每次声明队列的时候,参数中加上durable=True 这句,服务器端和客户端都需要写上这句。把队列持久化了,但是里面的消息丢失了。如果想要消息也保留住,需要在生产者端加下面一句。
11. 测试上面的效果
插播一句,有可能会遇到以下问题
原因:这是因为已经定义的队列,再次定义是无效的,这就是幂次原理。RabbitMQ不允许重新定义一个已有的队列信息,也就是说不允许修改已经存在的队列的参数。如果你非要这样做,只会返回异常。所以做测试的时候,可以使用不同的队列进行比较。不能在同一个队列中做修改。
producer.py中的程序
- # !/usr/bin/env python
- import pika
- #通过这个实例,先去建立一个socket,默认端口15672
- connection = pika.BlockingConnection(
- pika.ConnectionParameters('localhost')
- )
- channel = connection.channel() #声明一个管道,在管道里发消息
- # 在管道里面声明queue队列。
- channel.queue_declare(queue='hello1',durable=True)
- # n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
- #在管道里发消息
- channel.basic_publish(exchange='',
- routing_key='hello', #队列名字
- body='Hello World!', #消息
- properties=pika.BasicProperties(
- delivery_mode=2,#把消息持久化。
- )
- )
- print(" [x] Sent 'Hello World!'")
- connection.close() #直接把队列关闭,不需要关闭管道。
consumer.py中的程序
- # _*_coding:utf-8_*_
- import pika
- connection = pika.BlockingConnection(pika.ConnectionParameters(
- 'localhost')) #建立链接
- channel = connection.channel() #建立管道
- # You may ask why we declare the queue again ‒ we have already declared it in our previous code.
- # We could avoid that if we were sure that the queue already exists. For example if send.py program
- # was run before. But we're not yet sure which program to run first. In such cases it's a good
- # practice to repeat declaring the queue in both programs.
- # 为什么又声明了一个‘hello’队列?
- # 如果确定已经声明了,可以不声明。但是你不知道那个机器先运行,所以可以再次声明,这样就不会报错。
- channel.queue_declare(queue='hello1',durable=True) #说明从哪个队列里面收消息
- #ch:管道的内存对象地址
- #method:包含了把消息发给谁等消息
- def callback(ch, method, properties, body):
- print(" [x] Received %r" % body)
- ch.basic_ack(delivery_tag=method.delivery_tag)
- #开始消费消息
- channel.basic_consume(callback, #如果收到消息,就调用callback函数来处理消息
- queue='hello', #表示从哪个队列里收消息
- #no_ack=True #no acknowledgement,不管有没有处理完,都不会给生成者端发消息。
- )
- print(' [*] Waiting for messages. To exit press CTRL+C')
- channel.start_consuming() #之前的只是声明了语法,这条语句才是真正开始收数据
12. 上面的例子是1个生产者,3个消费者。平均分发消息。下面引入权重分配任务。----能者多劳
服务器端给客户端发消息的时候,如果当期队列中有1条那么,那么就不给你发了。
只需要在消费者端加一句话:channel.basic_qos(prefetch_count=1)
做模拟的时候,可以写2个消费者,其中1个sleep30秒-代表性能较差的机器。生产者一直不停发消息,当性能较差的机器一直没有处理完任务的时候,所有的任务都跑到了另外一个消费者处。
13, 做一个广播的实例,一个生产者发消息,所有的消费者都能收到。需要用到exchange。exchange类似于一个转发器。
exchange必须精确的知道收到的消息要发给谁。exchange的类型决定了怎么处理, 类型有以下几种:
- fanout: 所有绑定到此exchange的queue都可以接收消息
- direct: 通过routingKey和exchange决定的那个唯一的queue可以接收消息
- topic: 所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息。
1)fanout 纯广播、all
需要queue和exchange绑定,因为消费者不是和exchange直连的,消费者是连在queue上,queue绑定在exchange上,消费者只会在queu里读消息.消息是实时的,如果生产者发的时候,消费者错过了,那就再也收不到这条消息了。类似于收音机的模型。
生产者的关键代码:
消费者的关键代码:
发送端 publisher 发布、广播
- import pika
- import sys
- connection = pika.BlockingConnection(
- pika.ConnectionParameters(host='localhost')
- )
- channel = connection.channel()
- #注意:这里是广播,不需要声明queue
- channel.exchange_declare(exchange='logs', #声明广播管道
- type='fanout'
- )
- message = ' '.join(sys.argv[1:]) or "info: Hello World!"
- channel.basic_publish(exchange='logs',
- routing_key='', #queue名字为空,因为是广播,所以不需要声明queue。但是这句必须有。
- body=message)
- print(" [x] Sent %r" % message)
- connection.close()
接收端 subscriber 订阅
- import pika
- connection = pika.BlockingConnection(
- pika.ConnectionParameters(host='localhost')
- )
- channel = connection.channel()
- channel.exchange_declare(exchange='logs',
- type='fanout')
- result = channel.queue_declare(exclusive=True)
- # exclusive-排它的,唯一的。不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
- #随机生成,自动删除。queue对象的名字叫result。
- queue_name = result.method.queue
- channel.queue_bind(exchange='logs', #queue绑定到转发器上。
- queue=queue_name)
- print(' [*] Waiting for logs. To exit press CTRL+C')
- def callback(ch, method, properties, body):
- print(" [x] %r" % body)
- channel.basic_consume(callback,
- queue=queue_name,
- no_ack=True)
- channel.start_consuming()
可能因为程序版本的问题,没法使用type关键词。否则会报错,可以把下面的注释掉,直接按照顺序写参数即可正常运行。
- #channel.exchange_declare(exchange='logs', #声明广播管道
- # type='fanout'
- # )
- channel.exchange_declare('logs', #声明广播管道
- 'fanout'
- )
2)有选择地接收消息 (exchange type=direct),接收者可以过滤消息,只收我想要的消息.
当运行服务器端,后运行客户端以后,发现报错了,因为没有带参数。
想带参数的话,只能用cmd了。
再启动一个客户端
服务器端启动,默认用info模式发,看效果。该收的收到了,不该收的没收到。
用cmd启动服务器端,带参数的,发1条warning消息。测试效果:
效果如下:
测试结果一切良好。
14. 细致的消息过滤广播模式。现在是收所有的info,所有的warning,所有的error。想区分哪个警告是哪个客户端发来的,就需要更细致的消息过滤了。分别代表1.“所有包含orange的”;2-“所有以rabbit结尾的”; 3-"所有以lazy开头的”.类似于一个动态的匹配。
server端:
- import pika
- import sys
- connection = pika.BlockingConnection(
- pika.ConnectionParameters(host='localhost')
- )
- channel = connection.channel()
- #channel.exchange_declare(exchange='topic_logs',
- # type='topic')
- channel = connection.channel()
- channel.exchange_declare('topic_logs',
- 'topic')
- #可以自己填要发的消息,也可以使用默认的,但是格式是XXX.info的格式。
- routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
- message = ' '.join(sys.argv[2:]) or 'Hello World!'
- channel.basic_publish(exchange='topic_logs',
- routing_key=routing_key,
- body=message)
- print(" [x] Sent %r:%r" % (routing_key, message))
- connection.close()
客户端
- import pika
- import sys
- connection = pika.BlockingConnection(
- pika.ConnectionParameters(host='localhost')
- )
- channel = connection.channel()
- #channel.exchange_declare(exchange='topic_logs',
- # type='topic')
- channel.exchange_declare('topic_logs',
- 'topic')
- result = channel.queue_declare(exclusive=True)
- queue_name = result.method.queue
- binding_keys = sys.argv[1:]
- if not binding_keys:
- sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0])
- sys.exit(1)
- for binding_key in binding_keys:
- channel.queue_bind(exchange='topic_logs',
- queue=queue_name,
- routing_key=binding_key)
- print(' [*] Waiting for logs. To exit press CTRL+C')
- def callback(ch, method, properties, body):
- print(" [x] %r:%r" % (method.routing_key, body))
- channel.basic_consume(callback,
- queue=queue_name,
- no_ack=True)
- channel.start_consuming()
测试效果:
python topic_consumer.py # 代表什么都收。
书写接收内容的规则如下:
RabbitMQ 使用详细介绍的更多相关文章
- rabbitMQ概念详细介绍
1. 历史 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有 ...
- RabbitMQ消息队列(一): Detailed Introduction 详细介绍
http://blog.csdn.net/anzhsoft/article/details/19563091 RabbitMQ消息队列(一): Detailed Introduction 详细介绍 ...
- RabbitMQ学习总结(1)——基础概念详细介绍
一.基础概念详细介绍 1.引言 你是否遇到过两个(多个)系统间需要通过定时任务来同步某些数据?你是否在为异构系统的不同进程间相互调用.通讯的问题而苦恼.挣扎?如果是,那么恭喜你,消息服务让你可以很轻松 ...
- RabbitMQ基础概念详细介绍
http://blog.csdn.net/column/details/rabbitmq.html 转至:http://www.ostest.cn/archives/497 引言 你是否遇到过两个(多 ...
- 柯南君:看大数据时代下的IT架构(2)消息队列之RabbitMQ-基础概念详细介绍
一.基础概念详细介绍 1.引言 你是否遇到过两个(多个)系统间需要通过定时任务来同步某些数据?你是否在为异构系统的不同进程间相互调用.通讯的问题而苦恼.挣扎?如果是,那么恭喜你,消息服务让你可以很轻松 ...
- 一、Rabbitmq的简单介绍
以下只是本人从零学习过程的整理 部分内容参考地址:https://www.cnblogs.com/ysocean/p/9240877.html 1.RabbitMQ的概念 RabbitMQ是实现了高级 ...
- [No0000A7]批处理经常用到的变量及批处理>NUL详细介绍
绝对路径是指调用绝对的程序位置的路径,例如: start C:\Windows\test.exe 相对路径是文件改变路径以后还会按照变量的路径所在位置去调用,例如: start %WINDIR%\te ...
- linux配置网卡IP地址命令详细介绍及一些常用网络配置命令
linux配置网卡IP地址命令详细介绍及一些常用网络配置命令2010-- 个评论 收藏 我要投稿 Linux命令行下配置IP地址不像图形界面下那么方 便,完全需要我们手动配置,下面就给大家介绍几种配置 ...
- _MSC_VER详细介绍
_MSC_VER详细介绍 转自:http://www.cnblogs.com/braver/articles/2064817.html _MSC_VER是微软的预编译控制. _MSC_VER可以分解为 ...
随机推荐
- Tomcat安装部署和安全加固优化以及反向代理应用
1.Tomcat介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共同 ...
- Android开源的精美日历控件,热插拔设计的万能自定义UI
Android开源的精美日历控件,热插拔设计的万能自定义UI UI框架应该逻辑与界面实现分离,该日历控件使用了热插拔的设计 ,简单几步即可实现你需要的UI效果,热插拔的思想是你提供你的实现,我提供我的 ...
- Javascript 初学笔记
变量作用域 自 ES2015 起,JS 引入let 和 const 关键词定义变量的块作用域(Block Scope). var 仅支持全局作用域(Global Scope)和函数作用域(Functi ...
- python基础知识-04-字符串列表元组
python其他知识目录 内存,cpu,硬盘,解释器 实时翻译 编译器 :一次性翻译python2,3 除法,2不能得小数,3能得小数 1.字符串操作 1.1字符串操作startswith start ...
- 全国城市一卡通一级TSM平台业务架构及意义
[导读]TSM平台是一种具有鲜明行业属性的平台,因此,各行业都建立了本行业的TSM平台.为促进城市一卡通行业移动支付的快速发展,住房和城乡建设部也建立了全国城市一卡通行业一级TSM平台. 作为住建部标 ...
- 常用 php server
php编程中经常需要用到一些服务器的一些资料,我把常用的用高亮的方式贴出来,其余的放在后面.方便以后查阅 复制代码代码如下: $_SERVER['HTTP_ACCEPT_LANGUAGE']/ ...
- daterangepicker时间段插件
1.序言: daterangepicker是Bootstrap的一个时间组件,使用很方便 用于选择日期范围的JavaScript组件. 设计用于Bootstrap CSS框架. 它最初是为了改善报表而 ...
- HDU 5655 CA Loves Stick 水题
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5656 CA Loves Stick Accepts: 381 Submissions: 3204 ...
- Alpha事后诸葛(团队)
[设想和目标] Q1:我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? "小葵日记"是为了解决18-30岁年轻用户在记录生活时希望得到一美体验友好 ...
- lintcode-411-格雷编码
411-格雷编码 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个二进制的差异. 给定一个非负整数 n ,表示该代码中所有二进制的总数,请找出其格雷编码顺序.一个格雷编码顺序必须以 0 ...