1. 实现最简单的队列通信

2. producer端

  1. # !/usr/bin/env python
  2.  
  3. import pika
  4. #通过这个实例,先去建立一个socket,默认端口15672
  5. connection = pika.BlockingConnection(
  6. pika.ConnectionParameters('localhost')
  7. )
  8.  
  9. channel = connection.channel() #声明一个管道,在管道里发消息
  10.  
  11. # 在管道里面声明queue队列。
  12.  
  13. channel.queue_declare(queue='hello')
  14.  
  15. # n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
  16. #在管道里发消息
  17. channel.basic_publish(exchange='',
  18.  
  19. routing_key='hello', #队列名字
  20.  
  21. body='Hello World!') #消息
  22.  
  23. print(" [x] Sent 'Hello World!'")
  24.  
  25. connection.close() #直接把链接关闭,不需要关闭管道。

3.consumer端

  1. # _*_coding:utf-8_*_
  2.  
  3. import pika
  4.  
  5. connection = pika.BlockingConnection(pika.ConnectionParameters(
  6.  
  7. 'localhost')) #建立链接
  8.  
  9. channel = connection.channel() #建立管道
  10.  
  11. # You may ask why we declare the queue again ‒ we have already declared it in our previous code.
  12.  
  13. # We could avoid that if we were sure that the queue already exists. For example if send.py program
  14.  
  15. # was run before. But we're not yet sure which program to run first. In such cases it's a good
  16.  
  17. # practice to repeat declaring the queue in both programs.
  18. # 为什么又声明了一个‘hello’队列?
  19. # 如果确定已经声明了,可以不声明。但是你不知道那个机器先运行,所以可以再次声明,这样就不会报错。
  20. channel.queue_declare(queue='hello') #说明从哪个队列里面收消息
  21.  
  22. # ch 管道的内存对象地址
  23. def callback(ch, method, properties, body):
  24. print('---->',ch,method,properties)
  25. print(" [x] Received %r" % body)
  26.  
  27. #开始消费消息
  28. channel.basic_consume(callback, #如果收到消息,就调用callback函数来处理消息
  29. queue='hello', #表示从哪个队列里收消息
  30. no_ack=True)
  31.  
  32. print(' [*] Waiting for messages. To exit press CTRL+C')
  33.  
  34. 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中的程序

  1. # !/usr/bin/env python
  2.  
  3. import pika
  4. #通过这个实例,先去建立一个socket,默认端口15672
  5. connection = pika.BlockingConnection(
  6. pika.ConnectionParameters('localhost')
  7. )
  8.  
  9. channel = connection.channel() #声明一个管道,在管道里发消息
  10.  
  11. # 在管道里面声明queue队列。
  12.  
  13. channel.queue_declare(queue='hello1',durable=True)
  14.  
  15. # n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
  16. #在管道里发消息
  17. channel.basic_publish(exchange='',
  18.  
  19. routing_key='hello', #队列名字
  20.  
  21. body='Hello World!', #消息
  22.  
  23. properties=pika.BasicProperties(
  24. delivery_mode=2,#把消息持久化。
  25. )
  26. )
  27.  
  28. print(" [x] Sent 'Hello World!'")
  29.  
  30. connection.close() #直接把队列关闭,不需要关闭管道。

consumer.py中的程序

  1. # _*_coding:utf-8_*_
  2.  
  3. import pika
  4.  
  5. connection = pika.BlockingConnection(pika.ConnectionParameters(
  6.  
  7. 'localhost')) #建立链接
  8.  
  9. channel = connection.channel() #建立管道
  10.  
  11. # You may ask why we declare the queue again ‒ we have already declared it in our previous code.
  12.  
  13. # We could avoid that if we were sure that the queue already exists. For example if send.py program
  14.  
  15. # was run before. But we're not yet sure which program to run first. In such cases it's a good
  16.  
  17. # practice to repeat declaring the queue in both programs.
  18. # 为什么又声明了一个‘hello’队列?
  19. # 如果确定已经声明了,可以不声明。但是你不知道那个机器先运行,所以可以再次声明,这样就不会报错。
  20. channel.queue_declare(queue='hello1',durable=True) #说明从哪个队列里面收消息
  21.  
  22. #ch:管道的内存对象地址
  23. #method:包含了把消息发给谁等消息
  24. def callback(ch, method, properties, body):
  25. print(" [x] Received %r" % body)
  26. ch.basic_ack(delivery_tag=method.delivery_tag)
  27.  
  28. #开始消费消息
  29. channel.basic_consume(callback, #如果收到消息,就调用callback函数来处理消息
  30. queue='hello', #表示从哪个队列里收消息
  31. #no_ack=True #no acknowledgement,不管有没有处理完,都不会给生成者端发消息。
  32. )
  33. print(' [*] Waiting for messages. To exit press CTRL+C')
  34.  
  35. 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 发布、广播

  1. import pika
  2. import sys
  3.  
  4. connection = pika.BlockingConnection(
  5. pika.ConnectionParameters(host='localhost')
  6. )
  7.  
  8. channel = connection.channel()
  9. #注意:这里是广播,不需要声明queue
  10. channel.exchange_declare(exchange='logs', #声明广播管道
  11. type='fanout'
  12. )
  13. message = ' '.join(sys.argv[1:]) or "info: Hello World!"
  14.  
  15. channel.basic_publish(exchange='logs',
  16. routing_key='', #queue名字为空,因为是广播,所以不需要声明queue。但是这句必须有。
  17. body=message)
  18.  
  19. print(" [x] Sent %r" % message)
  20. connection.close()

接收端 subscriber 订阅

  1. import pika
  2.  
  3. connection = pika.BlockingConnection(
  4. pika.ConnectionParameters(host='localhost')
  5. )
  6.  
  7. channel = connection.channel()
  8.  
  9. channel.exchange_declare(exchange='logs',
  10.  
  11. type='fanout')
  12.  
  13. result = channel.queue_declare(exclusive=True)
  14. # exclusive-排它的,唯一的。不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
  15. #随机生成,自动删除。queue对象的名字叫result。
  16.  
  17. queue_name = result.method.queue
  18.  
  19. channel.queue_bind(exchange='logs', #queue绑定到转发器上。
  20. queue=queue_name)
  21.  
  22. print(' [*] Waiting for logs. To exit press CTRL+C')
  23.  
  24. def callback(ch, method, properties, body):
  25. print(" [x] %r" % body)
  26.  
  27. channel.basic_consume(callback,
  28.  
  29. queue=queue_name,
  30.  
  31. no_ack=True)
  32.  
  33. channel.start_consuming()

可能因为程序版本的问题,没法使用type关键词。否则会报错,可以把下面的注释掉,直接按照顺序写参数即可正常运行。

  1. #channel.exchange_declare(exchange='logs', #声明广播管道
  2. # type='fanout'
  3. # )
  4. channel.exchange_declare('logs', #声明广播管道
  5. 'fanout'
  6. )

2)有选择地接收消息 (exchange type=direct),接收者可以过滤消息,只收我想要的消息.

当运行服务器端,后运行客户端以后,发现报错了,因为没有带参数。

想带参数的话,只能用cmd了。

再启动一个客户端

服务器端启动,默认用info模式发,看效果。该收的收到了,不该收的没收到。

用cmd启动服务器端,带参数的,发1条warning消息。测试效果:

效果如下:

测试结果一切良好。

14. 细致的消息过滤广播模式。现在是收所有的info,所有的warning,所有的error。想区分哪个警告是哪个客户端发来的,就需要更细致的消息过滤了。分别代表1.“所有包含orange的”;2-“所有以rabbit结尾的”; 3-"所有以lazy开头的”.类似于一个动态的匹配。

server端:

  1. import pika
  2. import sys
  3.  
  4. connection = pika.BlockingConnection(
  5. pika.ConnectionParameters(host='localhost')
  6. )
  7.  
  8. channel = connection.channel()
  9. #channel.exchange_declare(exchange='topic_logs',
  10. # type='topic')
  11. channel = connection.channel()
  12. channel.exchange_declare('topic_logs',
  13. 'topic')
  14.  
  15. #可以自己填要发的消息,也可以使用默认的,但是格式是XXX.info的格式。
  16. routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
  17.  
  18. message = ' '.join(sys.argv[2:]) or 'Hello World!'
  19.  
  20. channel.basic_publish(exchange='topic_logs',
  21. routing_key=routing_key,
  22. body=message)
  23.  
  24. print(" [x] Sent %r:%r" % (routing_key, message))
  25.  
  26. connection.close()

客户端

  1. import pika
  2. import sys
  3.  
  4. connection = pika.BlockingConnection(
  5. pika.ConnectionParameters(host='localhost')
  6. )
  7.  
  8. channel = connection.channel()
  9. #channel.exchange_declare(exchange='topic_logs',
  10. # type='topic')
  11. channel.exchange_declare('topic_logs',
  12. 'topic')
  13.  
  14. result = channel.queue_declare(exclusive=True)
  15.  
  16. queue_name = result.method.queue
  17.  
  18. binding_keys = sys.argv[1:]
  19.  
  20. if not binding_keys:
  21. sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0])
  22. sys.exit(1)
  23.  
  24. for binding_key in binding_keys:
  25. channel.queue_bind(exchange='topic_logs',
  26. queue=queue_name,
  27. routing_key=binding_key)
  28.  
  29. print(' [*] Waiting for logs. To exit press CTRL+C')
  30.  
  31. def callback(ch, method, properties, body):
  32. print(" [x] %r:%r" % (method.routing_key, body))
  33.  
  34. channel.basic_consume(callback,
  35. queue=queue_name,
  36. no_ack=True)
  37.  
  38. channel.start_consuming()

测试效果:

python topic_consumer.py # 代表什么都收。

书写接收内容的规则如下:

RabbitMQ 使用详细介绍的更多相关文章

  1. rabbitMQ概念详细介绍

    1. 历史 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有 ...

  2. RabbitMQ消息队列(一): Detailed Introduction 详细介绍

     http://blog.csdn.net/anzhsoft/article/details/19563091 RabbitMQ消息队列(一): Detailed Introduction 详细介绍 ...

  3. RabbitMQ学习总结(1)——基础概念详细介绍

    一.基础概念详细介绍 1.引言 你是否遇到过两个(多个)系统间需要通过定时任务来同步某些数据?你是否在为异构系统的不同进程间相互调用.通讯的问题而苦恼.挣扎?如果是,那么恭喜你,消息服务让你可以很轻松 ...

  4. RabbitMQ基础概念详细介绍

    http://blog.csdn.net/column/details/rabbitmq.html 转至:http://www.ostest.cn/archives/497 引言 你是否遇到过两个(多 ...

  5. 柯南君:看大数据时代下的IT架构(2)消息队列之RabbitMQ-基础概念详细介绍

    一.基础概念详细介绍 1.引言 你是否遇到过两个(多个)系统间需要通过定时任务来同步某些数据?你是否在为异构系统的不同进程间相互调用.通讯的问题而苦恼.挣扎?如果是,那么恭喜你,消息服务让你可以很轻松 ...

  6. 一、Rabbitmq的简单介绍

    以下只是本人从零学习过程的整理 部分内容参考地址:https://www.cnblogs.com/ysocean/p/9240877.html 1.RabbitMQ的概念 RabbitMQ是实现了高级 ...

  7. [No0000A7]批处理经常用到的变量及批处理>NUL详细介绍

    绝对路径是指调用绝对的程序位置的路径,例如: start C:\Windows\test.exe 相对路径是文件改变路径以后还会按照变量的路径所在位置去调用,例如: start %WINDIR%\te ...

  8. linux配置网卡IP地址命令详细介绍及一些常用网络配置命令

    linux配置网卡IP地址命令详细介绍及一些常用网络配置命令2010-- 个评论 收藏 我要投稿 Linux命令行下配置IP地址不像图形界面下那么方 便,完全需要我们手动配置,下面就给大家介绍几种配置 ...

  9. _MSC_VER详细介绍

    _MSC_VER详细介绍 转自:http://www.cnblogs.com/braver/articles/2064817.html _MSC_VER是微软的预编译控制. _MSC_VER可以分解为 ...

随机推荐

  1. Tomcat安装部署和安全加固优化以及反向代理应用

    1.Tomcat介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共同 ...

  2. Android开源的精美日历控件,热插拔设计的万能自定义UI

    Android开源的精美日历控件,热插拔设计的万能自定义UI UI框架应该逻辑与界面实现分离,该日历控件使用了热插拔的设计 ,简单几步即可实现你需要的UI效果,热插拔的思想是你提供你的实现,我提供我的 ...

  3. Javascript 初学笔记

    变量作用域 自 ES2015 起,JS 引入let 和 const 关键词定义变量的块作用域(Block Scope). var 仅支持全局作用域(Global Scope)和函数作用域(Functi ...

  4. python基础知识-04-字符串列表元组

    python其他知识目录 内存,cpu,硬盘,解释器 实时翻译 编译器 :一次性翻译python2,3 除法,2不能得小数,3能得小数 1.字符串操作 1.1字符串操作startswith start ...

  5. 全国城市一卡通一级TSM平台业务架构及意义

    [导读]TSM平台是一种具有鲜明行业属性的平台,因此,各行业都建立了本行业的TSM平台.为促进城市一卡通行业移动支付的快速发展,住房和城乡建设部也建立了全国城市一卡通行业一级TSM平台. 作为住建部标 ...

  6. 常用 php server

    php编程中经常需要用到一些服务器的一些资料,我把常用的用高亮的方式贴出来,其余的放在后面.方便以后查阅     复制代码代码如下: $_SERVER['HTTP_ACCEPT_LANGUAGE']/ ...

  7. daterangepicker时间段插件

    1.序言: daterangepicker是Bootstrap的一个时间组件,使用很方便 用于选择日期范围的JavaScript组件. 设计用于Bootstrap CSS框架. 它最初是为了改善报表而 ...

  8. HDU 5655 CA Loves Stick 水题

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5656 CA Loves Stick Accepts: 381   Submissions: 3204 ...

  9. Alpha事后诸葛(团队)

    [设想和目标] Q1:我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? "小葵日记"是为了解决18-30岁年轻用户在记录生活时希望得到一美体验友好 ...

  10. lintcode-411-格雷编码

    411-格雷编码 格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个二进制的差异. 给定一个非负整数 n ,表示该代码中所有二进制的总数,请找出其格雷编码顺序.一个格雷编码顺序必须以 0 ...