Rabbitmq

Rabbitmq

1.安装

https://blog.csdn.net/xc_zhou/article/details/80851726

2.使用

https://www.cnblogs.com/wt11/p/5970297.html
https://www.cnblogs.com/pangguoping/p/5720134.html

  • 生产者
import pika
import sys

username = 'wt'   #指定远程rabbitmq的用户名密码
pwd = '111111'
user_pwd = pika.PlainCredentials(username, pwd)
s_conn = pika.BlockingConnection(pika.ConnectionParameters('192.168.1.240', credentials=user_pwd))#创建连接
chan = s_conn.channel()  #在连接上创建一个频道

chan.queue_declare(queue='hello') #声明一个队列,生产者和消费者都要声明一个相同的队列,用来防止万一某一方挂了,另一方能正常运行
chan.basic_publish(exchange='',  #交换机
                   routing_key='hello',#路由键,写明将消息发往哪个队列,本例是将消息发往队列hello
                   body='hello world')#生产者要发送的消息
print("[生产者] send 'hello world")

s_conn.close()#当生产者发送完消息后,可选择关闭连接


输出:
[生产者] send 'hello world
  • 消费者
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# auth : pangguoping

import pika

# ########################## 消费者 ##########################
credentials = pika.PlainCredentials('admin', 'admin')
# 连接到rabbitmq服务器
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.1.103',5672,'/',credentials))
channel = connection.channel()

# 声明消息队列,消息将在这个队列中进行传递。如果队列不存在,则创建
channel.queue_declare(queue='hello')


# 定义一个回调函数来处理,这边的回调函数就是将信息打印出来。
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)


# 告诉rabbitmq使用callback来接收信息
channel.basic_consume(callback,
                      queue='hello',
                      no_ack=True)
 # no_ack=True表示在回调函数中不需要发送确认标识

print(' [*] Waiting for messages. To exit press CTRL+C')

# 开始接收信息,并进入阻塞状态,队列里有信息才会调用callback进行处理。按ctrl+c退出。
channel.start_consuming()

注:当消费者是多个,生产者是一个时,会轮流叫消息分配给每个消费者


1.acknowledgment 消息不丢失的方法,使消息安全接收

  • 消费者端
channel.basic_consume(callback,
                      queue='hello',
                      no_ack=False)
ch.basic_ack(delivery_tag=method.delivery_tag)  # 主要使用此代码,告诉生成者我已收到该消息

no_ack=False(默认为False,即必须有确认标识),在回调函数consumer_callback中,未收到确认标识,那么,RabbitMQ会重新将发生该消息给这个消费者,如果消费者挂掉了,那么就会将消息发送给下一个人。

2.消息持久化

为了防止在发送消息时服务器突然宕机等意外使得消息丢失,必须要做消息持久化,使消息永久保存在硬盘上

1.要做消息持久化,首先要队列持久化,使用durable=True

# 声明queue
channel.queue_declare(queue='task_queue',durable=True)

注意,当我们在生成者声明队列后,消费端声明同名队列时,参数必须保持一致

至于为什么要在生成和消费端各声明一次队列?
# 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.
channel.queue_declare(queue='hello')

# 主要是为了防止意外,比如生产者还没有发送消息(没有声明队列),服务器就启动了,此时如果在服务器端不声明,那么队列就还没有创建,就会发生意外。

2.队列持久化之后,宕机只会保持队列,还必须做消息持久化,以保存队列中的消息

channel.basic_publish(exchange='',
                      routing_key='task_queue',
                      body=message,
                      properties=pika.BasicProperties(
                          delivery_mode=2,  # make message persistent
                      )

在生产者端加入参数properties以便让消息持久化

3.RabbitMq消息的发布与订阅

RabbitMQ的发布与订阅,借助于交换机(Exchange)来实现。

交换机的工作原理:消息发送端先将消息发送给交换机,交换机再将消息发送到绑定的消息队列,而后每个接收端(consumer)都能从各自的消息队列里接收到信息。

Exchange有三种工作模式,分别为:Fanout, Direct, Topic

1. Fanout:向所有订阅消息(实质是bind绑定到该交换机上的queue队列)发消息
  • 生成者
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# auth : pangguoping
# rabbitmq 发布者
import pika

credentials = pika.PlainCredentials('admin', 'admin')
#链接rabbit服务器(localhost是本机,如果是其他服务器请修改为ip地址)
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.1.103',5672,'/',credentials))
channel = connection.channel()
# 定义交换机,exchange表示交换机名称,type表示类型
channel.exchange_declare(exchange='logs_fanout',
                         type='fanout')

message = 'Hello Python'
# 将消息发送到交换机
channel.basic_publish(exchange='logs_fanout',  # 指定exchange
                      routing_key='',  # fanout下不需要配置,配置了也不会生效
                      body=message)
print(" [x] Sent %r" % message)
connection.close()
  • 消费者
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# auth : pangguoping

#  rabbitmq 订阅者
import pika

credentials = pika.PlainCredentials('admin', 'admin')
#链接rabbit服务器(localhost是本机,如果是其他服务器请修改为ip地址)
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.1.103',5672,'/',credentials))
channel = connection.channel()

# 定义交换机,进行exchange声明,exchange表示交换机名称,type表示类型
channel.exchange_declare(exchange='logs_fanout',
                         type='fanout')

# 随机创建队列
result = channel.queue_declare(exclusive=True)  # exclusive=True表示建立临时队列,当consumer关闭后,该队列就会被删除
queue_name = result.method.queue
# 将队列与exchange进行绑定
channel.queue_bind(exchange='logs_fanout',
                   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()

注意:这个时候必须先启动消费者,即订阅者。因为随机队列是在consumer启动的时候随机生成的,并且进行绑定的。producer仅仅是发送至exchange,并不直接与随机队列进行通信。

使用广播时,每一个消费者都会单独创建一个队列,
exclusive=True是保证每个消费者创建的队列名不重复

# 随机创建队列
result = channel.queue_declare(exclusive=True)  # exclusive=True表示建立临时队列,当consumer关闭后,该队列就会被删除

2.direct 分组订阅

image

任何发送到Direct Exchange的消息都会被转发到routing_key中指定的Queue:

  • 消费者
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# auth : pangguoping
# 消费者
import pika

credentials = pika.PlainCredentials('admin', 'admin')
#链接rabbit服务器(localhost是本机,如果是其他服务器请修改为ip地址)
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.1.103',5672,'/',credentials))
channel = connection.channel()
# 定义exchange和类型
channel.exchange_declare(exchange='direct_test',
                         type='direct')

# 生成随机队列
result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue
severities = ['error', ]
# 将随机队列与routing_key关键字以及exchange进行绑定
for severity in severities:
    channel.queue_bind(exchange='direct_test',
                       queue=queue_name,
                       routing_key=severity)
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()
  • 生成者
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# auth : pangguoping
# 发布者
import pika

credentials = pika.PlainCredentials('admin', 'admin')
#链接rabbit服务器(localhost是本机,如果是其他服务器请修改为ip地址)
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.1.103',5672,'/',credentials))
channel = connection.channel()
# 定义交换机名称及类型
channel.exchange_declare(exchange='direct_test',
                         type='direct')

severity = 'info'
message = '123'
# 发布消息至交换机direct_test,且发布的消息携带的关键字routing_key是info
channel.basic_publish(exchange='direct_test',
                      routing_key=severity,
                      body=message)
print(" [x] Sent %r:%r" % (severity, message))
connection.close()

routing_key配合exchange交换机来使用,severity严重程度就代表每次发送的组名,当生产者的routing_key是info,那么只有同名交换机的routing_key是info的消费者才能接受到

当然一个接收机也可以同时定义接收info 和 error等不同级别的信息,只需要循环遍历serverity将queue绑定Exchange的不同级别即可

for severity in severities:
    channel.queue_bind(exchange='direct_test',
                       queue=queue_name,
                       routing_key=severity)

3.topic 话题订阅

相当于direct的升级办,可以使用通配符进行更加细致的分组

路由键模糊匹配,其实是路由键(routing_key)的扩展,就是可以使用正则表达式,和常用的正则表示式不同,这里的话“#”表示所有、全部的意思;“*”只匹配到一个词。
image

  • 举例
    比如有四台接收者
*.django.*
# 表示接收所有
mysql.*
mysql.error.*

当生产者的分组是

mysql.error

接收到的有
# 表示接收所有
mysql.*
mysql.error.123

接收到的有
# 表示接收所有
mysql.error.*
mysql.django.3421

接收到的有
# 表示接收所有
*.django.*
  • 生产者
import pika
import sys

credentials = pika.PlainCredentials('name', 'pwd')
connection = pika.BlockingConnection(pika.ConnectionParameters(
    'localhost',credentials=credentials))
channel = connection.channel()


channel.exchange_declare(exchange='topic_logs',exchange_type='topic')

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

credentials = pika.PlainCredentials('name', 'pwd')
connection = pika.BlockingConnection(pika.ConnectionParameters(
    'localhost',credentials=credentials))
channel = connection.channel()


channel.exchange_declare(exchange='topic_logs', exchange_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='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(queue=queue_name,
                      on_message_callback=callback
                      )

channel.start_consuming()

不同的只有exchange的类型从direct变成了topic

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值