RabbitMQ 消息队列

 

RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。

MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消 息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。

 

什么叫消息队列

消息(Message)是指在应用间传送的数据。消息可以非常简单,比如只包含文本字符串,也可以更复杂,可能包含嵌入对象。

消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,由消息系统来确保消息的可靠传递。消息发布者只管把消息发布到 MQ 中而不用管谁来取,消息使用者只管从 MQ 中取消息而不管是谁发布的。这样发布者和使用者都不用知道对方的存在。

 

为何用消息队列

从上面的描述中可以看出消息队列是一种应用间的异步协作机制,那什么时候需要使用 MQ 呢?

以常见的订单系统为例,用户点击【下单】按钮之后的业务逻辑可能包括:扣减库存、生成相应单据、发红包、发短信通知。在业务发展初期这些逻辑可能放在一起同步执行,随着业务的发展订单量增长,需要提升系统服务的性能,这时可以将一些不需要立即生效的操作拆分出来异步执行,比如发放红包、发短信通知等。这种场景下就可以用 MQ ,在下单的主流程(比如扣减库存、生成相应单据)完成之后发送一条消息到 MQ 让主流程快速完结,而由另外的单独线程拉取MQ的消息(或者由 MQ 推送消息),当发现 MQ 中有发红包或发短信之类的消息时,执行相应的业务逻辑。

以上是用于业务解耦的情况,其它常见场景包括最终一致性、广播、错峰流控等等。

个人认为:消息队列就是为了让我们的让我们的处理机制的效率提升 就是根据不同的需求来进行处理不同的机制  而不是一起执行进而提高了整个程序的效率,并且可以我们的消息放入队列之后就不管后面的使用者了 我继续执行我的程序 后面的程序如何使用我就不管了 也是大大提升了程序的效率

应用场景

 

 

Rabbitmq优点:

  异步,异步解决信息  

  解耦性 

  降峰  # 可以限制你请求进来信息

1、为什么要使用消息队列?

分析:一个用消息队列的人,不知道为啥用,这就有点尴尬。没有复习这点,很容易被问蒙,然后就开始胡扯了。
回答:这个问题,咱只答三个最主要的应用场景(不可否认还有其他的,但是只答三个主要的),即以下六个字:解耦、异步、削峰

(1)解耦

传统模式:

传统模式的缺点

  • 系统间耦合性太强,如上图所示,系统A在代码中直接调用系统B和系统C的代码,如果将来D系统接入,系统A还需要修改代码,过于麻烦!

中间件模式:

中间件模式的的优点

  • 将消息写入消息队列,需要消息的系统自己从消息队列中订阅,从而系统A不需要做任何修改。
(2)异步

传统模式:

传统模式的缺点

  • 一些非必要的业务逻辑以同步的方式运行,太耗费时间。

中间件模式:

中间件模式的的优点

  • 将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度
(3)削峰

传统模式

传统模式的缺点

  • 并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常

中间件模式:

中间件模式的的优点

    • 系统A慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。在生产中,这个短暂的高峰期积压是允许的。

 

RabbitMQ 

RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。

rabbitMQ是一款基于AMQP协议的消息中间件,它能够在应用之间提供可靠的消息传输。在易用性,扩展性,高可用性上表现优秀。使用消息中间件利于应用之间的解耦,生产者(客户端)无需知道消费者(服务端)的存在。而且两端可以使用不同的语言编写,大大提供了灵活性。

 

中文文档

 

rabbitMQ安装

windows下安装:
因为rbbitmq是Erlang写的

安装Erlang

下载地址:https://www.erlang.org/downloads

安装RabbitMQ

下载地址:https://www.rabbitmq.com/

安装完毕切换到你的安装目录下的sbin目录:D:\tool\RabbitMQ\rabbit\rabbitmq_server-3.7.8\sbin
然后执行  rabbitmqctl status

当出现一大串的不知名的字符串的时候就是可以的了

 

linux

for Linux:

安装配置epel源
   $ rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
 
安装erlang
   $ yum -y install erlang
 
安装RabbitMQ
   $ yum -y install rabbitmq-server
注意:service rabbitmq-server start/stop

 

mac:

for Mac:

bogon:~ yuan$ brew install rabbitmq
bogon:~ yuan$ export PATH=$PATH:/usr/local/sbin
bogon:~ yuan$ rabbitmq-server

 

 

 

rabbitMQ工作模型

1、简单模式:

简单模式的使用的时候要注意两边队列的名字是必须相同的,并且两边都要创建队列 因为万一是消费者先打开消息队列的要先创建 一个 然后去取  虽然没有内容 但是也要有去取的地方 ,就是不确定哪一方先去队列中取值 ,所以我们要双方都要绑定队列  还有就是回掉函数是在接收到信息之后 自动执行的

# ######################### 生产者 #########################
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host="localhost")) # 和RabbitMQ进行连接
channel = connection.channel() # 创建RabbitMQ消息对象

# 声明一个队列 队列名字可以随意指定
channel.queue_declare(queue = "nice")

# 插入数据
channel.basic_publish(exchange="", # 简单模式不用填写 这个代表交换机
routing_key = "nice", # 队列
body = "Hello laowang" # 要发送的信息
)

print(" [x] Sent 'Hello laowang!'")
connection.close() # 执行这个队列

 

 

# ########################## 消费者 ##########################

import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) # 连接到RabbitMQ
channel = connection.channel() #创建消息队列对象
# 声明一个队列 这个队列的名字可以随意定义
channel.queue_declare(queue = "nice")


#确定回掉函数
def callback(ch,method,properties,body): #body是你从消息队列中拿到信息
print("Received %r"%body)


channel.basic_consume(callback,
queue = "nice", #队列的名字
no_ack = True #这个是用来做应答的
)

print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming() # 执行你的队列
 

 

 

相关参数:

no-ack = False,如果消费者遇到情况(its channel is closed, connection is closed, or TCP connection is lost)挂掉了,那么,RabbitMQ会重新将该任务添加到队列中。

  • 回调函数中的ch.basic_ack(delivery_tag=method.delivery_tag)
  • basic_comsume中的no_ack=False

也就是as_ack 代表的是应答机制,如果等于True是无应到 的关于False是有应答,应答的是

当你接收到信息之后执行回掉函数的时候必须要做一个应答:ch.basic_ack(delivery_tag = method.delivery_tag) 这个应答是告诉消息队列你接收到信息了

 

#####消费者#####

import
pika connection = pika.BlockingConnection(pika.ConnectionParameters( host='10.211.55.4')) channel = connection.channel() channel.queue_declare(queue='hello') def callback(ch, method, properties, body): print(" [x] Received %r" % body) import time time.sleep(10) print 'ok' ch.basic_ack(delivery_tag = method.delivery_tag) #应答 告诉消息队列你收到了信息 channel.basic_consume(callback, queue='hello', no_ack=False) # 必须在回掉函数中做应答 print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()

 

 

  durable  :消息不丢失 消息的持久化

也就是如果你的RabbitMQ服务器宕机之后 那么在队列中的数据还没有进行提取的信息就会丢失,那么我们进行了durable = True之后就会再开机之后这些没有提取的信息还在其中不会丢失

持久化在生产者这边生成队列的时候设置durable = True  然后走在插入数据的时候修改模式 ,然后消费者只在生成队列的时候进行durable = True即可

####生产者####

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))  # 和RabbitMQ进行连接
channel = connection.channel()  # 创建RabbitMQ消息对象

# 声明一个队列 队列名字可以随意指定
channel.queue_declare(queue = "nice",durable = True)   # 消息持久化

# 插入数据
channel.basic_publish(exchange="",  # 简单模式不用填写 这个代表交换机
                      routing_key = "nice",  # 队列
                      body = "Hello laowang",  # 要发送的信息
                      properties=pika.BasicProperties(  # 持久化必须要修改的模式
                          delivery_mode=2, # make message persistent
                      ))

print(" [x] Sent 'Hello laowang!'")
connection.close() # 执行这个队列



####消费者####

import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))  # 连接到RabbitMQ
channel = connection.channel()  #创建消息队列对象
# 声明一个队列 这个队列的名字可以随意定义
channel.queue_declare(queue = "nice",durable=True)  # 持久化


#确定回掉函数
def callback(ch,method,properties,body):  #body是你从消息队列中拿到信息
    print("Received %r"%body)


channel.basic_consume(callback,
                      queue = "nice",  #队列的名字
                      no_ack = True   #这个是用来表示你的队列是否进行内容的清空
                      )

print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()  # 执行你的队列

 

 消息获取顺序

默认消息队列里的数据是按照顺序被消费者拿走,例如:消费者1 去队列中获取 奇数 序列的任务,消费者1去队列中获取 偶数 序列的任务。

如果我们还设置排队的话 万一第一个消费者取走 要等20分众再取那对于后面的太不公平了

channel.basic_qos(prefetch_count=1) 表示谁来谁取,不再按照奇偶数排列

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4'))
channel = connection.channel()

# make message persistent
channel.queue_declare(queue='hello')


def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    import time
    time.sleep(10)
    print 'ok'
    ch.basic_ack(delivery_tag = method.delivery_tag)

channel.basic_qos(prefetch_count=1)  # 谁来谁取不在进行限制

channel.basic_consume(callback,
                      queue='hello',
                      no_ack=False)

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

 

 

exchange模型

发送给多个队列

发布订阅和简单的消息队列区别在于,发布订阅会将消息发送给所有的订阅者,而消息队列中的数据被消费一次便消失。所以,RabbitMQ实现发布和订阅时,会为每一个订阅者创建一个队列,而发布者发布消息时,会将消息放置在所有相关队列中。

exchange type = fanout

 

也就是我们的以前的简单模式就是这个消息只可以被一个消费者取走 那么其他的要是想用就不行了,那么fanout就是解决这个问题 可以让多人都可以拿取到

 

####生产者####

import pika


connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel() # 创建RabbitMq对象

# 声明一个log的交换机 然后它的模式是fanout分发模式
channel.exchange_declare(exchange = "log",
                         exchange_type = "fanout"
                         )

# 定义消息
message = "info: hello World"

# 插入信息
channel.basic_publish(exchange = "log",
                      routing_key="", # 这个时候队列就不管了
                      body = message
                      )
print(" [x] Sent %r" % message)
connection.close()



###消费者####


import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

# 声明一个交换机 模式为fanout分发模式
channel.exchange_declare(exchange = "log",
                         exchange_type = "fanout"
                         )

result = channel.queue_declare(exclusive=True)  # 创建一个随机队列
queue_name = result.method.queue  # 取到随机队列的名字

# 将队列绑定到交换机上
channel.queue_bind(exchange='log',
                   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()

上面其实就是给队列加了一个交换机,然后这个交换机就绑定所有的创建的队列 然后生产者以后的信息 都会先发送到这个交换机中 然后 消费者创建自己的队列 然后队列在进行绑定在这个交换机上,然后信息从交换机出来的时候就会每一个队列都发送一份就达到了,一份消息可以发送给多个队列

 

关键字发送

 

 我们上面的发送信息是每一个绑定这个交换机的队列都发送了,那么有的万一不需要的话怎么办呢 这个时候就出来的一个神奇的关键字发送,改变你的分发模式fanout改为direct

 exchange type = direct

之前事例,发送消息时明确指定某个队列并向其中发送消息,RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。

####生产者###

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

# 声明一个名为direct_logs的交换机
channel.exchange_declare(exchange='direct_logs',
                         exchange_type='direct')

# 定义的消息
message ="today is a bad day"

# 插入信息
channel.basic_publish(exchange='direct_logs',
                      routing_key='info',  # 指定的给你的队列绑定的时候这个关键字发送消息
                      body=message)
print(" [x] Sent %r" % message)
connection.close()



###消费者###


import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='direct_logs',
                         exchange_type='direct')

result = channel.queue_declare(exclusive=True)  # 生成随机队列
queue_name = result.method.queue  # 获取队列名字

for i in ["error","warning","info"]:  # 循环你定义的关键字   只有队列全部绑定了这三个关键字 才可以接收消息
    channel.queue_bind(exchange='direct_logs',
                       queue=queue_name,
                       routing_key=i)  # 把交换机和队列绑定的时候根据关键字来绑定

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()

 

如果收发的消息是根据你的生产者插入值的关键字来决定的

 

模糊匹配

 

 

 

exchange type = topic

发送者路由值              队列中
old.boy.python          old.*  -- 不匹配
old.boy.python          old.#  -- 匹配

在topic类型下,可以让队列绑定几个模糊的关键字,之后发送者将数据发送到exchange,exchange将传入”路由值“和 ”关键字“进行匹配,匹配成功,则将数据发送到指定队列。

  • # 表示可以匹配 0 个 或 多个 单词
  • *  表示只能匹配 一个 单词
###生产者###


import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

# 声明一个名为topic_logs的交换机
channel.exchange_declare(exchange='topic_logs',
                         exchange_type='topic')

# 定义的消息
message ="bad today"
# 差值
channel.basic_publish(exchange='topic_logs',
                      routing_key='zz.apple.aa',  # 绑定队列的规则
                      body=message)
print(" [x] Sent %r" % message)
connection.close()


###消费者###


import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()

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

result = channel.queue_declare(exclusive=True)  # 生成随机队列
queue_name = result.method.queue  # 获取队列的名字

channel.queue_bind(exchange='topic_logs',
                   queue=queue_name,
                   routing_key="*.apple.*")  # 队列和交换机绑定的时候模糊匹配

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()

也就是按照你的生产者中的绑定的规则 然后消费者中绑定的时候在进行匹配

 

 

转载于:https://www.cnblogs.com/zhaoyunlong/p/9769255.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值