---------------rabbitmq的基础使用---------------

查看消息队列信息

在安装好rabbitmq-server之后,启动服务

使用命令rabbitmqctl list_queues查看消息队列列表

[root@hostname rabbitmq]# rabbitmqctl list_queues
Listing queues

当前的队列为空

向rabbitmq发送消息

使用pika进行通信链接

import pika
# 认证的参数(用户名和密码)
credentials = pika.PlainCredentials('ceshi', 'ceshi123')
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters('localhost', 5672, '/', credentials))
# 生成句柄
channel = conn.channel()
# 创建队列
channel.queue_declare(queue='hello118')
#exchange为分发消息区域
#routing_key为要指定的消息队列
#body为发送的内容
channel.basic_publish(exchange='',
routing_key='hello118',
body='Hello World!')
print(" [x] Sent 'Hello World!'")
conn.close()

在rabbitmq服务器使用命令查看消息队列

[root@hostname rabbitmq]# rabbitmqctl list_queues
Listing queues
hello118 1

可以看出,hello118队列里现在有一条消息

从rabbitmq接收消息

import pika
import time
# 参数认证
credentials = pika.PlainCredentials("ceshi", "ceshi123")
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters("localhost", 5672, "/", credentials))
# 创建句柄
channel = conn.channel()
# 声明一个接收消息的队列(这样如果发送端在发送前如果检测到这个消息队列,就不在单独创建了,而直接使用)
channel.queue_declare(queue="hello118")
#拿取消息后的回调函数
def callback(ch, method, properties, body):
time.sleep(15)
print("[info] Received %s" % body)
#no_ack=True,一旦从队列里拿到消息,这条消息就从队列里消失掉
channel.basic_consume(
callback, queue="hello118", no_ack=True
)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

此时,先启动消息发送者,然后再分别启动3个消息接收者,通过发送者多发送几条消息,你会发现,这几条消息会被依次分配到各个消息接收者身上

安全接收

从上边的例子中,接收者在拿到消息后,执行回调函数,在睡眠15秒的时候,如果突然中途退出了,或者挂掉了,那么消息队列里不存在这条消息了,但是这条消息实时上并未被接收者处理。着当然不是我们所希望的那样。

对于一条消息,接收者拿到,有可能半路就给挂掉(跑路)了,那么这条消息我们认为并未处理完成,仍然应该存在于消息队列里,换句话说我们希望的是当接收者接收到消息,处理完后给一个反馈,然后再把这条消息从队列里清除掉。

这里我们在回调函数里,加一个消息确认的操作

def callback(ch, method, properties, body):
print("[info] Received %s" % body)
time.sleep(20)
print("done")
#确认消息处理完毕(这样rabbitmq才会从消息队列里清除掉这条消息)
ch.basic_ack(delivery_tag=method.delivery_tag)

并且,在channel.basic_consume方法里不能再设置no_ack=True,因为no_ack=True的意思就是说,拿走消息,消息队列立即清除。

channel.basic_consume(
callback, queue="xinqueue"
)

完整版接收端代码:

import pika
import time
# 参数认证
credentials = pika.PlainCredentials("ceshi", "ceshi123")
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters("localhost", 5672, "/", credentials))
# 创建句柄
channel = conn.channel()
# 声明一个接收消息的队列(这样如果发送端在发送前如果检测到这个消息队列,就不在单独创建了,而直接使用)
channel.queue_declare(queue="ceshi")
# 拿取消息后的回调函数
def callback(ch, method, properties, body):
print("[info] Received %s" % body)
#方便模拟终端操作
time.sleep(20)
print("done")
#确认消息处理完毕(这样rabbitmq才会从消息队列里清除掉这条消息)
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_consume(
callback, queue="ceshi"
)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

验证一下,先通过发送者往队列ceshi里发送一条消息(”wo shi ceshi “),现在先用接收者1接收,接着用接收者2接收。发现:

  1. 开始20秒计时
  2. 接收者1接收到消息:wo shi ceshi
  3. 接收者2等待接收指令
  4. 在rabbitmq上查看消息队列ceshi里的消息并未被删除
  5. 中断(ctrl+c)接收者1的链接后,接收者2马上接收到消息wo shi ceshi
  6. 20秒结束,在rabbitmq上查看消息队列ceshi里的消息已经被删除

从上边可以看出,当消息被接收者1接收到后(还未反馈处理结果),rabbitmq将刚刚那条消息给暂时保存起来,别的接收者是不可再接收此消息的,当接收者1中断操作,没有反馈后,rabbitmq又将消息返回到消息队列里,并被接收者2给拿到了

消息持久化

那如果在这个过程中rabbitmq宕机了,不但消息会丢失,而且消息队列都根本没有了。

执行 services rabbitmq-server restart ,再执行rabbitmqctl list_queues,发现消息队列里都没有了

[root@hostname wys]# rabbitmqctl list_queues
Listing queues

那如何做到消息不丢失呢,这就需要消息持久化操作。在消息持久化之前必须要做到队列持久化,因为队列没了,消息肯定就不可能存在

要做队列持久化,只需要在声明队列的时候设置durable=True

channel.queue_declare(queue="xinqueue", durable=True)

这里在接收者设置durable=True,那在发送端,也必须声明持续化

验证一下,通过发送端发送一条消息,然后执行 services rabbitmq-server restart,再执行rabbitmqctl list_queues,发现消息队列里
xinqueue存在。说明 durable=True设置生效。

但是,现在来看刚刚发送的那条消息是不在了,接下来要做的就是消息持久化,需要在发消息的时候设置另外一个参数properties

# exchange为分发消息区域
# routing_key为要指定的消息队列
# body为发送的内容
channel.basic_publish(exchange='',
routing_key='xinqueue',
body='Hello 222!',
properties=pika.BasicProperties(
delivery_mode=2 #使消息持久化
)
)

验证一下,现在通过发送端发送一条消息,然后执行 services rabbitmq-server restart,再执行rabbitmqctl list_queues,发现消息队列里xinqueue存在。而且队列里的数据也存在,说明设置生效

现在发布这一阶段完整版本代码:

发送端:

import pika
# 认证的参数(用户名和密码)
credentials = pika.PlainCredentials('ceshi', 'ceshi123')
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters('localhost', 5672, '/', credentials))
# 生成句柄
channel = conn.channel()
# 创建队列
# durable=True意思是持续化这个队列(不会因为宕机就会消失掉)
channel.queue_declare(queue='xinqueue', durable=True)
# exchange为分发消息区域
# routing_key为要指定的消息队列
# body为发送的内容
channel.basic_publish(exchange='',
routing_key='xinqueue',
body='Hello 222!',
properties=pika.BasicProperties(
delivery_mode=2 # 消息持久化(不会因为宕机而消失)
)
)
print(" [x] Sent 'Hello World!'")
conn.close()

接收端

import pika
import time
# 参数认证
credentials = pika.PlainCredentials("ceshi", "ceshi123")
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters("localhost", 5672, "/", credentials))
# 创建句柄
channel = conn.channel()
# 声明一个接收消息的队列(这样如果发送端在发送前如果检测到这个消息队列,就不在单独创建了,而直接使用)
# durable=True意思是持续化这个队列(不会因为宕机就会消失掉)
channel.queue_declare(queue="xinqueue",durable=True)
# 拿取消息后的回调函数
def callback(ch, method, properties, body):
print("[info] Received %s" % body)
time.sleep(20)
print("done")
#确认消息处理完毕(这样rabbitmq才会从消息队列里清除掉这条消息)
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_consume(
callback, queue="xinqueue"
)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

持久化操作其实就是将这些信息存放到硬盘上了

注意,消息接收端可以声明消息队列,因为如果是接收端先启动,而发送端未启动,那么接收端检测不到队列xinqueue就会报错,如果我们在接收端也声明了这个消息队列xinqueue,发送端在后启动后,检测到已经有xinqueue这个队列了,就不会再单独创建一个xinqueue队列,而是直接使用就可以。

消息公平分发

如果Rabbit只管按顺序把消息发到各个消费者身上,不考虑消费者负载的话,很可能出现,一个机器配置不高的消费者那里堆积了很多消息处理不完,同时配置高的消费者却一直很轻松。为解决此问题,可以在各个消费者端,配置perfetch=1,意思就是告诉RabbitMQ在我这个消费者当前消息还没处理完的时候就不要再给我发新消息了。

这里主要是在接收端设置channel.basic_qos(prefetch_count=1)

import pika
import time
# 参数认证
credentials = pika.PlainCredentials("ceshi", "ceshi123")
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters("localhost", 5672, "/", credentials))
# 创建句柄
channel = conn.channel()
# 声明一个接收消息的队列(这样如果发送端在发送前如果检测到这个消息队列,就不在单独创建了,而直接使用)
# durable=True意思是持续化这个队列(不会因为宕机就会消失掉)
channel.queue_declare(queue="xinqueue",durable=True)
# 拿取消息后的回调函数
def callback(ch, method, properties, body):
print("[info] Received %s" % body)
time.sleep(20)
print("done")
#确认消息处理完毕(这样rabbitmq才会从消息队列里清除掉这条消息)
ch.basic_ack(delivery_tag=method.delivery_tag)
#如果我消息没处理完就不要再给我分配消息了
channel.basic_qos(prefetch_count=1)
channel.basic_consume(
callback, queue="xinqueue"
)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

消息发布\订阅

之前的例子都基本都是1对1的消息发送和接收,即消息只能发送到指定的queue里,但有些时候你想让你的消息被所有的Queue收到,类似广播的效果,这时候就要用到exchange了,

Exchange在定义的时候是有类型的,以决定到底是哪些Queue符合条件,可以接收消息,起到一个过滤和分配的作用

三种模式:

fanout: 所有bind到此exchange的queue都可以接收消息

direct: 通过routingKey和exchange决定的那个唯一的queue可以接收消息

topic:所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息

  • 表达式符号说明:#代表一个或多个字符,代表任何字符 例:#.a会匹配a.a,aa.a,aaa.a等;.a会匹配a.a,b.a,c.a等
  • 使用RoutingKey为#,Exchange Type为topic的时候相当于使用fanout 

广播(exchange type=fanout)

发送端代码:

import pika
import sys
# 认证的参数(用户名和密码)
credentials = pika.PlainCredentials('ceshi', 'ceshi123')
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters('localhost', 5672, '/', credentials))
# 生成句柄
channel = conn.channel()
# 创建exchange
channel.exchange_declare(exchange='exfanout',
type='fanout')
# exchange为分发消息区域
# routing_key为要指定的消息队列,在这里以exchange转发器的形式发消息,这里就不需要写了,
# body为发送的内容
message = ' '.join(sys.argv[1:]) or "info: Hello World!"
channel.basic_publish(exchange='exfanout',
routing_key='',
body='Hello 222!',
)
print(" [x] Sent %r" % message)
conn.close()

接收端代码:

import pika
import time
# 参数认证
credentials = pika.PlainCredentials("ceshi", "ceshi123")
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters("localhost", 5672, "/", credentials))
# 创建句柄
channel = conn.channel()
# 声明一个消息转发器
# type=fanout(类型为广播模式)
channel.exchange_declare(exchange='exfanout',
type='fanout')
result = channel.queue_declare(exclusive=True) # 不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = result.method.queue
channel.queue_bind(exchange='exfanout',
queue=queue_name)
# 拿取消息后的回调函数
def callback(ch, method, properties, body):
print("[info] Received %s" % body)
# time.sleep(20)
print("done")
channel.basic_consume(
callback, queue=queue_name, no_ack=True
)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

验证一下:打开两个接收者,接收者1和接收者2,再通过发送端发送消息

  1. 打开两个接收者后,在rabbitmq上查看到两个随机消息队列,并且都没有消息
  2. 在发送端发送消息后,接收者1和接收者2都收到消息了

有选择的接收消息(exchange type=direct)

RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。

发送端代码:

import pika
import sys
# 认证的参数(用户名和密码)
credentials = pika.PlainCredentials('ceshi', 'ceshi123')
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters('localhost', 5672, '/', credentials))
# 生成句柄
channel = conn.channel()
# 创建exchange
channel.exchange_declare(exchange='exdirect',
type='direct')
# exchange为分发消息区域
# routing_key为要指定的消息队列,在这里可以自定义一些关键字,消息将被发往含这些关键字的消息队列
# body为发送的内容
severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'
channel.basic_publish(exchange='exdirect',
routing_key=severity,
body=message)
print(" [x] Sent %r" % message)
conn.close()

接收端代码:

import pika
import time
import sys
# 参数认证
credentials = pika.PlainCredentials("ceshi", "ceshi123")
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters("localhost", 5672, "/", credentials))
# 创建句柄
channel = conn.channel()
# 声明一个消息转发器
# type=direct(类型为组播)
channel.exchange_declare(exchange='exdirect',
type='direct')
result = channel.queue_declare(exclusive=True) # 不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = result.method.queue
channel.queue_bind(exchange='exdirect',
queue=queue_name)
severities = sys.argv[1:]
if not severities:
sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
sys.exit(1)
#逐个进行队列的绑定
for severity in severities:
channel.queue_bind(exchange='exdirect',
queue=queue_name,
routing_key=severity)
# 拿取消息后的回调函数
def callback(ch, method, properties, body):
print("[info] Received %s" % body)
# time.sleep(20)
print("done")
channel.basic_consume(
callback, queue=queue_name, no_ack=True
)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

验证一下:打开两个接收者,

接收者1

python consumer_direct.py info

接收者2

python consumer_direct.py info

发送者,当执行python producer_direct.py info ‘hahahah’时,只有接收者1收到消息

发送者,当执行python producer_direct.py error ‘you are error’时,只有接收者2收到消息

更细致的消息过滤

发送者代码:

import pika
import sys
# 认证的参数(用户名和密码)
credentials = pika.PlainCredentials('ceshi', 'ceshi123')
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters('localhost', 5672, '/', credentials))
# 生成句柄
channel = conn.channel()
# 创建exchange,指定类型为topic
channel.exchange_declare(exchange='extopic',
type='topic')
routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'
# exchange为分发消息区域
# routing_key为要指定的消息队列,
# body为发送的内容
channel.basic_publish(exchange='extopic',
routing_key=routing_key,
body=message)
print(" [x] Sent %r" % message)
conn.close()

接收者代码:

import pika
import time
import sys
# 参数认证
credentials = pika.PlainCredentials("ceshi", "ceshi123")
# 创建链接
conn = pika.BlockingConnection(pika.ConnectionParameters("localhost", 5672, "/", credentials))
# 创建句柄
channel = conn.channel()
# 声明一个消息转发器
# type=direct(类型为topic)
channel.exchange_declare(exchange='extopic',
type='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='extopic',
queue=queue_name,
routing_key=binding_key)
# 拿取消息后的回调函数
def callback(ch, method, properties, body):
print("[info] Received %s" % body)
# time.sleep(20)
print("done")
channel.basic_consume(
callback, queue=queue_name, no_ack=True
)
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

验证一下:打开两个接收者,

接收者1

# 接收所有消息
python consumer_topic.py #

接收者2

# 接收以.error结尾的队列的消息
python consumer_direct.py #.error

发送者,当执行python producer_direct.py sds ‘hahahah’时,只有接收者1收到消息

发送者,当执行python producer_direct.py er.error ‘you are error’时,两个都能接收到消息

Rabbitmq----基础使用的更多相关文章

  1. RabbitMQ基础知识

    RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...

  2. 转:RabbitMQ基础知识

    RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...

  3. RabbitMQ基础知识详解

    什么是MQ? MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取队列中 ...

  4. RabbitMQ基础教程之基本使用篇

    RabbitMQ基础教程之基本使用篇 最近因为工作原因使用到RabbitMQ,之前也接触过其他的mq消息中间件,从实际使用感觉来看,却不太一样,正好趁着周末,可以好好看一下RabbitMQ的相关知识点 ...

  5. RabbitMq基础教程之基本概念

    RabbitMq基础教程之基本概念 RabbitMQ是一个消息队列,和Kafka以及阿里的ActiveMQ从属性来讲,干的都是一回事.消息队列的主要目的实现消息的生产者和消费者之间的解耦,支持多应用之 ...

  6. 转 RabbitMQ 基础概念及 Spring 的配置和使用 推荐好文 举例讲解

    从不知道到了解—RabbitMQ 基础概念及 Spring 的配置和使用 原理同上 请求地址:http://localhost:8080/home?type=3&routing_key=myO ...

  7. RabbitMQ基础组件和SpringBoot整合RabbitMQ简单示例

    交换器(Exchange) 交换器就像路由器,我们先是把消息发到交换器,然后交换器再根据绑定键(binding key)和生产者发送消息时的路由键routingKey, 按照交换类型Exchange ...

  8. C#RabbitMQ基础学习笔记

    RabbitMQ基础学习笔记(C#代码示例) 一.定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开发).MQ是一种应用程序对应用程序的通信方法. ...

  9. RabbitMQ基础教程之Spring&JavaConfig使用篇

    RabbitMQ基础教程之Spring使用篇 相关博文,推荐查看: RabbitMq基础教程之安装与测试 RabbitMq基础教程之基本概念 RabbitMQ基础教程之基本使用篇 RabbitMQ基础 ...

  10. RabbitMQ基础教程之使用进阶篇

    RabbitMQ基础教程之使用进阶篇 相关博文,推荐查看: RabbitMq基础教程之安装与测试 RabbitMq基础教程之基本概念 RabbitMQ基础教程之基本使用篇 I. 背景 前一篇基本使用篇 ...

随机推荐

  1. [USACO09Open] Tower of Hay 干草塔

    为了调整电灯亮度,贝西要用干草包堆出一座塔,然后爬到牛棚顶去把灯泡换掉.干草包会从传送带上运来,共会出现N包干草,第i包干草的宽度是W i ,高度和长度统一为1.干草塔要从底层开始铺建.贝西会选择最先 ...

  2. lintcode671 循环单词

    循环单词   The words are same rotate words if rotate the word to the right by loop, and get another. Cou ...

  3. 【CSV数据文件】

    文件参数化设置方法

  4. Python3 小工具-MAC泛洪

    from scapy.all import * import optparse def attack(interface): pkt=Ether(src=RandMAC(),dst=RandMAC() ...

  5. 从SDN鼻祖Nicira到VMware NSX 网络虚拟化平台的简单探讨

    以前的大二层技术,一般是在物理网络底层使用IS-IS路由技术,再在此基础之上,实现数据中心网络的二层扩展,如公有的Trill.SPB技术和Cisco私有的OTV.Fabricpath技术:前沿一些的网 ...

  6. 自测之Lesson6:文件I/O

    题目:区分文件I/O和标准I/O. 区别: ①首先两者一个显著的不同点在于,标准I/O默认采用了缓冲机制,比如调用fopen函数,不仅打开一个文件,而且建立了一个缓冲区(读写模式下将建立两个缓冲区), ...

  7. Ubuntu 配置 ftp freemind adb

    . 1. 配置apt-get源 配置过程 : sudo vim /etc/profile 命令, 在后面添加下面的内容; 刷新配置文件 : source /etc/profie 命令; 刷新源 : s ...

  8. Delegate(QLabel和QComboBox)

    一.最终效果 二.实现思路 1.createEditor()中create两个控件,分别是QLabel和QComboBox,将其添加到一个widget中,然后返回该widget: 2.setEdito ...

  9. 解决python中文编码错误问题

    对于初学者而言,编码问题或许还没有没重视起来,但是编码问题是中文开发者必须面对的.今天来看下python开发中如何解决编码问题.注意:本篇讲的是最常见的一种编码问题,其他编码问题,如json函数引起的 ...

  10. spring框架(1)— 依赖注入

    依赖注入 spring核心容器就是一个超级大工厂,所以的对象(数据源.hibernate SessionFactory等基础性资源)都会被当做spring核心容器的管理对象——spring把容器中的一 ...