一、RabbitMQ消息队列介绍

RabbitMQ是在两个独立得python程序,或其他语言交互时使用。

RabbitMQ:erlang语言 开发的。

Python中连接RabbitMQ的模块:pika 、Celery(分布式任务队列) 、haigha

可以维护很多得队列

RabbitMQ 教程官网:http://www.rabbitmq.com/getstarted.html

几个概念说明:

Broker:简单来说就是消息队列服务器实体。

Exchange:消息交换机,他制定消息按什么规则,路由到哪个队列。

Queue:消息队列载体,每个消息都会被投入一个或多个队列。

Binding:绑定,他的作用就是把exchange和queue按照路由规则绑定起来。

Routing Key:路由关键字,exchange根据这个关键字进行消息投递。

vhost:虚拟主机,一个broker里可以设多个vhost,用作不同用户得权限分离。

producer:消息生产者,就是投递消息得程序。

consumer:消息消费者,就是接受消息得程序。

channel:消息通道,在客户端得每个连接里。可以建立多个channel,每个channel代表一个会话任务。


二、RabbitMQ基本示范

1.Rabbitmq安装

ubuntu系统

install rabbitmq-server # 直接搞定


centos系统

1)Install Erlang

  1. 1Install Erlang
  2. # For EL5:
  3. rpm -Uvh http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm
  4. # For EL6:
  5. rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
  6. # For EL7:
  7. rpm -Uvh http://download.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm
  8. yum install erlang

2)Install RabbitMQ Server

rpm --import https://www.rabbitmq.com/rabbitmq-release-signing-key.asc

yum install rabbitmq-server-3.6.5-1.noarch.rpm

3)use RabbitMQ Server

chkconfig rabbitmq-server on

service rabbitmq-server stop/start

2.基本示例子

发送端producer

  1. import pika
  2. # 建立一个实例
  3. connection = pika.BlockingConnection(
  4. pika.ConnectionParameters('localhost',5672) # 默认端口5672,可不写
  5. )
  6. # 声明一个管道,在管道里发消息
  7. channel = connection.channel()
  8. # 在管道里声明queue
  9. channel.queue_declare(queue='hello')
  10. # RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
  11. channel.basic_publish(exchange='',
  12. routing_key='hello', # queue名字
  13. body='Hello World!') # 消息内容
  14. print(" [x] Sent 'Hello World!'")
  15. connection.close() # 队列关闭

接收端consumer

  1. import pika
  2. import time
  3. # 建立实例
  4. connection = pika.BlockingConnection(pick.ConnectionParameters('hocalhost'))
  5. # 声明管道
  6. channel = connection.channel()
  7. # 为什么声明了一个‘hello’队列?
  8. # 如果确定已经声明了,可以不声明。但是你不知道那个机器先运行,所以要声明两次。
  9. # 通常是先运行消费者
  10. channel.queue_declare(queue='hello')
  11. def callback(ch, method, properties, body):#四个参数为标准格式
  12. print("[x]Received %r"%body)
  13. time.sleep(15)
  14. ch.basic_ack(delivry_tay = method.delivery_tay)# 告诉生成者,消息处理完成
  15. channel.basic_consume(# 消费消息
  16. callback, # 如果收到消息,就调用callback函数来处理消息
  17. queue='hello',# 要消费的队列
  18. # no ack=True # 消息确认
  19. # 一般不写。宕机则生产者检测到发给其他消费者
  20. )
  21. print('[*]Waiting for messages.To exit press CTRL+C')
  22. channel.start_consuming() # 开始消费消息

3.RabbitMQ消息分发轮询

一个生产者多个消费者

采用轮询机制;把消息依次分发

假如消费者处理雄安熙需要15秒,如果宕机了,那这个消息处理还没有处理完,怎么处理?

(可以模拟消费端断了,分别注释和不注释no_ack=True看一下)

没有回复,就代表消息没有处理完,

上面的效果消费端断了就转到另外一个消费端去了,但是生产者怎么知道消费端断了呢?

因为生产者和消费者是通过socket连接的,socket断了,就说明消费端断开了。

上面的模式只是依次分发,实际情况是机器配置不一样。怎么设置类似权重的操作?

RabbitMQ怎么办呢,RabbitMQ做了简单的处理就能实现公平的分发。

就是RabbitMQ给消费者发消息的时候检测下消费者里的消息数量,如果超过指定值(比如1条),就不给你发了。

只需要在消费者端,channel.basic_consume前加上就可以了。

channel.basic_qos(prefetch_count=1)# 类似权重,按能力分发,如果有一个消息,就不在给你发

channel.basic_consume( # 消费消息

三、Rabbit MQ消息持久化(durable、properties)

1.RabbitMQ相关命令

rabbitmqctl list_queues # 查看当前queue数量及queue里消息数量

2.消息持久化

如果队列里还有消息,RabbitMQ服务端宕机了呢?消息还在不在?

把RabbitMQ服务重启,看一下消息在不在。

上面的情况下,宕机了,消息就久了,下面看看如何把消息持久化。

每次声明队列的时候,都加上durable,注意每个队列都得写,客户端、服务端声明的时候都得写。

在管道里声明queue

channel.queue_declare(queue='hello2', durable=True)

测试结果发现,只是把队列持久化了,但队列里的消息没了。

durable的作用只是把队列持久化。离消息持久话还差一步:

发送端发送消息时,加上properties

  1. import pika
  2. import sys
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. host='localhost'))
  5. channel = connection.channel()
  6. channel.exchange_declare(exchange='direct_logs',
  7. type='direct')
  8. # 重要程度级别,这里默认定义为 info
  9. severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
  10. message = ' '.join(sys.argv[2:]) or 'Hello World!'
  11. channel.basic_publish(exchange='direct_logs',
  12. routing_key=severity,
  13. body=message)
  14. print(" [x] Sent %r:%r" % (severity, message))
  15. connection.close()

接收端subscriber

  1. import pika
  2. import sys
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. host='localhost'))
  5. channel = connection.channel()
  6. channel.exchange_declare(exchange='topic_logs',
  7. type='topic')
  8. routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
  9. message = ' '.join(sys.argv[2:]) or 'Hello World!'
  10. channel.basic_publish(exchange='topic_logs',
  11. routing_key=routing_key,
  12. body=message)
  13. print(" [x] Sent %r:%r" % (routing_key, message))
  14. connection.close()

接收端 consumer

  1. import pika
  2. import time
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. 'localhost'))
  5. channel = connection.channel()
  6. channel.queue_declare(queue='hello2', durable=True)
  7. def callback(ch, method, properties, body):
  8. print(" [x] Received %r" % body)
  9. time.sleep(10)
  10. ch.basic_ack(delivery_tag = method.delivery_tag) # 告诉生产者,消息处理完成
  11. channel.basic_qos(prefetch_count=1) # 类似权重,按能力分发,如果有一个消息,就不在给你发
  12. channel.basic_consume( # 消费消息
  13. callback, # 如果收到消息,就调用callback
  14. queue='hello2',
  15. # no_ack=True # 一般不写,处理完接收处理结果。宕机则发给其他消费者
  16. )
  17. print(' [*] Waiting for messages. To exit press CTRL+C')
  18. channel.start_consuming()

四、RabbitMQ广播模式(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里度消息

  1. |------------------------|
  2. | /—— queue <—|—> consumer1
  3. producer —|—exchange1 <bind |
  4. \ | \— queue <—|—> consumer2
  5. \-|-exchange2 …… |
  6. |------------------------|

发送端 publisher 发布、广播

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

接收端 subscriber 订阅

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

注意:广播,是实时的,收不到就没了,消息不会存下来,类似收音机。

2.direct 有选择的接收消息

接收者可以过滤消息,只收我想要的消息

发送端publisher

  1. import pika
  2. import sys
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. host='localhost'))
  5. channel = connection.channel()
  6. channel.exchange_declare(exchange='direct_logs',
  7. type='direct')
  8. # 重要程度级别,这里默认定义为 info
  9. severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
  10. message = ' '.join(sys.argv[2:]) or 'Hello World!'
  11. channel.basic_publish(exchange='direct_logs',
  12. routing_key=severity,
  13. body=message)
  14. print(" [x] Sent %r:%r" % (severity, message))
  15. connection.close()

接收端subscriber

  1. import pika
  2. import sys
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. host='localhost'))
  5. channel = connection.channel()
  6. channel.exchange_declare(exchange='direct_logs',
  7. type='direct')
  8. result = channel.queue_declare(exclusive=True)
  9. queue_name = result.method.queue
  10. # 获取运行脚本所有的参数
  11. severities = sys.argv[1:]
  12. if not severities:
  13. sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
  14. sys.exit(1)
  15. # 循环列表去绑定
  16. for severity in severities:
  17. channel.queue_bind(exchange='direct_logs',
  18. queue=queue_name,
  19. routing_key=severity)
  20. print(' [*] Waiting for logs. To exit press CTRL+C')
  21. def callback(ch, method, properties, body):
  22. print(" [x] %r:%r" % (method.routing_key, body))
  23. channel.basic_consume(callback,
  24. queue=queue_name,
  25. no_ack=True)
  26. channel.start_consuming()

运行接收端,指定接收级别的参数,例:

python direct_sonsumer.py info warning

python direct_sonsumer.py warning error

3.topic 更细致的过滤

比如把error中,apache和mysql的分别或取出来

发送端publisher

  1. import pika
  2. import sys
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. host='localhost'))
  5. channel = connection.channel()
  6. channel.exchange_declare(exchange='topic_logs',
  7. type='topic')
  8. routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
  9. message = ' '.join(sys.argv[2:]) or 'Hello World!'
  10. channel.basic_publish(exchange='topic_logs',
  11. routing_key=routing_key,
  12. body=message)
  13. print(" [x] Sent %r:%r" % (routing_key, message))
  14. connection.close()

接收端 subscriber

  1. import pika
  2. import sys
  3. connection = pika.BlockingConnection(pika.ConnectionParameters(
  4. host='localhost'))
  5. channel = connection.channel()
  6. channel.exchange_declare(exchange='direct_logs',
  7. type='direct')
  8. result = channel.queue_declare(exclusive=True)
  9. queue_name = result.method.queue
  10. # 获取运行脚本所有的参数
  11. severities = sys.argv[1:]
  12. if not severities:
  13. sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
  14. sys.exit(1)
  15. # 循环列表去绑定
  16. for severity in severities:
  17. channel.queue_bind(exchange='direct_logs',
  18. queue=queue_name,
  19. routing_key=severity)
  20. print(' [*] Waiting for logs. To exit press CTRL+C')
  21. def callback(ch, method, properties, body):
  22. print(" [x] %r:%r" % (method.routing_key, body))
  23. channel.basic_consume(callback,
  24. queue=queue_name,
  25. no_ack=True)
  26. channel.start_consuming()

运行接收端,指定接收哪些消息,例:

  1. python topic_sonsumer.py *.info
  2. python topic_sonsumer.py *.error mysql.*
  3. python topic_sonsumer.py '#' # 接收所有消息
  4. # 接收所有的 logs run:
  5. # python receive_logs_topic.py "#"
  6. # To receive all logs from the facility "kern":
  7. # python receive_logs_topic.py "kern.*"
  8. # Or if you want to hear only about "critical" logs:
  9. # python receive_logs_topic.py "*.critical"
  10. # You can create multiple bindings:
  11. # python receive_logs_topic.py "kern.*" "*.critical"
  12. # And to emit a log with a routing key "kern.critical" type:
  13. # python emit_log_topic.py "kern.critical" "A critical kernel error"

4.RabbitMQ RPC 实现(Remote procedure call)

不知道你有没有发现,上面的流都是单向的,如果远程的机器执行完返回结果,就实现不了了。

如果返回,这种模式叫什么呢,RPC(远程过程调用),snmp就是典型的RPC

RabbitMQ能不能返回呢,怎么返回呢?既是发送端又是接收端。

但是接收端返回消息怎么返回?可以发送到发过来的queue里么?不可以。

返回时,再建立一个queue,把结果发送新的queue里

为了服务端返回的queue不写死,在客户端给服务端发指令的的时候,同时带一条消息说,你结果返回给哪个queue

RPC client

  1. import pika
  2. import uuid
  3. import time
  4. class FibonacciRpcClient(object):
  5. def __init__(self):
  6. self.connection = pika.BlockingConnection(pika.ConnectionParameters(
  7. host='localhost'))
  8. self.channel = self.connection.channel()
  9. result = self.channel.queue_declare(exclusive=True)
  10. self.callback_queue = result.method.queue
  11. self.channel.basic_consume(self.on_response, # 只要一收到消息就调用on_response
  12. no_ack=True,
  13. queue=self.callback_queue) # 收这个queue的消息
  14. def on_response(self, ch, method, props, body): # 必须四个参数
  15. # 如果收到的ID和本机生成的相同,则返回的结果就是我想要的指令返回的结果
  16. if self.corr_id == props.correlation_id:
  17. self.response = body
  18. def call(self, n):
  19. self.response = None # 初始self.response为None
  20. self.corr_id = str(uuid.uuid4()) # 随机唯一字符串
  21. self.channel.basic_publish(
  22. exchange='',
  23. routing_key='rpc_queue', # 发消息到rpc_queue
  24. properties=pika.BasicProperties( # 消息持久化
  25. reply_to = self.callback_queue, # 让服务端命令结果返回到callback_queue
  26. correlation_id = self.corr_id, # 把随机uuid同时发给服务器
  27. ),
  28. body=str(n)
  29. )
  30. while self.response is None: # 当没有数据,就一直循环
  31. # 启动后,on_response函数接到消息,self.response 值就不为空了
  32. self.connection.process_data_events() # 非阻塞版的start_consuming()
  33. # print("no msg……")
  34. # time.sleep(0.5)
  35. # 收到消息就调用on_response
  36. return int(self.response)
  37. if __name__ == '__main__':
  38. fibonacci_rpc = FibonacciRpcClient()
  39. print(" [x] Requesting fib(7)")
  40. response = fibonacci_rpc.call(7)
  41. print(" [.] Got %r" % response)

RPC server

  1. import pika
  2. import time
  3. def fib(n):
  4. if n == 0:
  5. return 0
  6. elif n == 1:
  7. return 1
  8. else:
  9. return fib(n-1) + fib(n-2)
  10. def on_request(ch, method, props, body):
  11. n = int(body)
  12. print(" [.] fib(%s)" % n)
  13. response = fib(n)
  14. ch.basic_publish(
  15. exchange='', # 把执行结果发回给客户端
  16. routing_key=props.reply_to, # 客户端要求返回想用的queue
  17. # 返回客户端发过来的correction_id 为了让客户端验证消息一致性
  18. properties=pika.BasicProperties(correlation_id = props.correlation_id),
  19. body=str(response)
  20. )
  21. ch.basic_ack(delivery_tag = method.delivery_tag) # 任务完成,告诉客户端
  22. if __name__ == '__main__':
  23. connection = pika.BlockingConnection(pika.ConnectionParameters(
  24. host='localhost'))
  25. channel = connection.channel()
  26. channel.queue_declare(queue='rpc_queue') # 声明一个rpc_queue ,
  27. channel.basic_qos(prefetch_count=1)
  28. # 在rpc_queue里收消息,收到消息就调用on_request
  29. channel.basic_consume(on_request, queue='rpc_queue')
  30. print(" [x] Awaiting RPC requests")
  31. channel.start_consuming()

python 使用 RabbitMQ的更多相关文章

  1. Python操作RabbitMQ

    RabbitMQ介绍 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现的产品,RabbitMQ是一个消息代理,从“生产者”接收消息并传递消 ...

  2. 用 Python、 RabbitMQ 和 Nameko 实现微服务

    用 Python. RabbitMQ 和 Nameko 实现微服务 原创 07-17 17:57 首页 Linux中国 "微服务是一股新浪潮" - 现如今,将项目拆分成多个独立的. ...

  3. python之RabbitMQ

    一.安装RabbitMQ 1. 安装erlang 1 2 3 4 tar xf otp_src_18.3.tar.gz cd otp_src_18.3 ./configure --prefix=/ma ...

  4. Python之路【第九篇】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy   Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用 ...

  5. python - 操作RabbitMQ

    python - 操作RabbitMQ     介绍 RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.他遵循Mozilla Public License开源协议.MQ全称为Mess ...

  6. 文成小盆友python-num12 Redis发布与订阅补充,python操作rabbitMQ

    本篇主要内容: redis发布与订阅补充 python操作rabbitMQ 一,redis 发布与订阅补充 如下一个简单的监控模型,通过这个模式所有的收听者都能收听到一份数据. 用代码来实现一个red ...

  7. Python之路第十二天,高级(4)-Python操作rabbitMQ

    rabbitMQ RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.他遵循Mozilla Public License开源协议. MQ全称为Message Queue, 消息队列(M ...

  8. Python与RabbitMQ交互

    RabbitMQ 消息队列 成熟的中间件RabbitMQ.ZeroMQ.ActiveMQ等等 RabbitMQ使用erlang语言开发,使用RabbitMQ前要安装erlang语言 RabbitMQ允 ...

  9. python中RabbitMQ的使用(安装和简单教程)

    1,简介 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现的产品,RabbitMQ是一个消息代理,从"生产者"接收消息 ...

  10. Python之RabbitMQ的使用

    今天总结一下Python关于Rabbitmq的使用 RabbitMQ官网说明,其实也是一种队列,那和前面说的线程queue和进程queue有什么区别呢? 线程queue只能在同一个进程下进行数据交互 ...

随机推荐

  1. luogu 1593

    $Answer = A ^ B $ 的因子之和 将 $A$ 进行质因数分解$A = p_1 ^ {a_1} P_2 ^ {a_2} p_3 ^ {a_3} \cdots p_k ^ {a_k}$ $A ...

  2. linux系列(二十四):du命令

    1.命令格式 du [选项][文件] 2.命令功能 显示每个文件和目录的磁盘使用空间. 3.命令参数 -a或-all 显示目录中个别文件的大小. -b或-bytes 显示目录或文件大小时,以byte为 ...

  3. SSD检测几个小细节

    目录 一. 抛砖引玉的Faster-RCNN 1.1 候选框的作用 1.2 下采样问题 二. SSD细节理解 2.1 六个LOSS 2.2 Anchor生成细节 2.3 Encode&& ...

  4. manjaro 滚动更新后无法开机,Failed to start load kernel modules,nvidia驱动导致

    今天滚动后无法开机,启动时显示Faild to start load kernel modules,卡在后面无法进入登录界面 systemctl status systemd-modules-load ...

  5. c++ ros 计算两点距离

    #include <iostream> /* puts, printf */ #include <time.h> /* time_t, struct tm, time, loc ...

  6. Hadoop zookeeper hbase spark phoenix (HA)搭建过程

    环境介绍: 系统:centos7 软件包: apache-phoenix-4.14.0-HBase-1.4-bin.tar.gz  下载链接:http://mirror.bit.edu.cn/apac ...

  7. ICEM-轴(周期复制网格)

    原视频下载地址:https://yunpan.cn/cqMnfpqQQdZZI  访问密码 802b

  8. idea JRebel

    JRebel 链接:https://pan.baidu.com/s/11LI0RkPtrfEWQENns6cWAA 提取码:ndsu settings -> plugins -> inst ...

  9. Get Argument Values From Linq Expression

    Get Argument Values From Linq Expression If you even find yourself unpacking an expression in C#, yo ...

  10. Json序列化指定输出字段 忽略属性

    DataContract 服务契约定义了远程访问对象和可供调用的方法,数据契约则是服务端和客户端之间要传送的自定义数据类型. 一旦声明一个类型为DataContract,那么该类型就可以被序列化在服务 ...