rabbitmq学习笔记-----分发机制

  • 应用场景

  当有Consumer需要大量的运算时,RabbitMQ Server需要一定的分发机制来balance每个Consumer的load。试想一下,对于web application来说,在一个很多的HTTP request里是没有时间来处理复杂的运算的,只能通过后台的一些工作线程来完成

  • 要实现的分发机制图

  • 循环分发

  send.py代码

  

复制代码
from amqplib import client_0_8 as amqp
import sys
##创建con和chanel
con = amqp.Connection(host="192.168.1.188:32769", userid="guest", password="guest", virtual_host="/")
chan = con.channel()
####创建queue
chan.queue_declare(queue="hello")
####创建消息
massage = ' '.join(sys.argv[1:])
msg = amqp.Message(massage)
###发送
chan.basic_publish(msg,exchange='',routing_key="hello")

####关闭
chan.close()
con.close()
复制代码

  receive.py代码

复制代码
from amqplib import client_0_8 as amqp
import time
##创建con和chanel
con = amqp.Connection(host="192.168.1.188:32769", userid="guest", password="guest", virtual_host="/")
chan = con.channel()
####创建queue
chan.queue_declare(queue="hello")
######定义回调函数
def recv_callback(msg):
    print('Received: ' + msg.body + ' from channel #' + str(msg.channel.channel_id))

######开始消费、consume
chan.basic_consume(queue="hello",callback=recv_callback,consumer_tag="testtag",no_ack=True)

#####开始监听
while True:
    chan.wait()

######取消消费
chan.basic_cancle("testtag")



###关闭连接
chan.close()
con.close()
复制代码

运行send.py文件

运行comsume1

运行comsume2

结论:

  默认情况下,RabbitMQ 会顺序的分发每个Message。当每个queue收到ack后,会将该Message删除,然后将下一个Message分发到下一个Consumer。这种分发方式叫做round-robin

  • Message acknowledgment 消息确认

  每个Consumer可能需要一段时间才能处理完收到的数据。如果在这个过程中,Consumer出错了,异常退出了,而数据还没有处理完成,那么非常不幸,这段数据就丢失了。因为我们采用no-ack的方式进行确认,也就是说,每次Consumer接到数据后,而不管是否处理完成,RabbitMQ Server会立即把这个Message标记为完成,然后从queue中删除了

  如果一个Consumer异常退出了,它处理的数据能够被另外的Consumer处理,这样数据在这种情况下就不会丢失了(注意是这种情况下)

  为了保证数据不被丢失,RabbitMQ支持消息确认机制,即acknowledgments。为了保证数据能被正确处理而不仅仅是被Consumer收到,那么我们不能采用no-ack。而应该是在处理完数据后发送ack。

  在处理数据后发送的ack,就是告诉RabbitMQ数据已经被接收,处理完成,RabbitMQ可以去安全的删除它了。

    如果Consumer退出了但是没有发送ack,那么RabbitMQ就会把这个Message发送到下一个Consumer(该消费者能接收到到前一个消费者的queue)。这样就保证了在Consumer异常退出的情况下数据也不会丢失。

    这里并没有用到超时机制。RabbitMQ仅仅通过Consumer的连接中断来确认该Message并没有被正确处理。也就是说,RabbitMQ给了Consumer足够长的时间来做数据处理

  步骤、开启消费者1

  

  步骤二、开启消费者2,关闭消费者1

  

  可以看到消费者2也接收到也完全接收到了这些未ack的消息,list查看一下有多少unack的消息

  

  解决:

    可以将所有连接到名为hello的queue的所有消费者都断掉,让这个queue因为没有连接的消费者而删除掉

    可以再创建一个消费者专门处理接收并处理这些消息然后发送ack,然后把其它的消费者都关掉,让这个新建的消费者发送确认机制

    so如果开启了ack机制,请一定要在处理完消息后发送ack确认机制

  • Message durability消息持久化

    当rabbitmq服务意外宕机屌,为了数据不丢失,我们采用了:

    1. 在数据处理结束后发送ack,这样RabbitMQ Server会认为Message Deliver 成功。
    2. 持久化queue,可以防止RabbitMQ Server 重启或者crash引起的数据丢失
    3. 持久化Message,理由同上

comsume.py

复制代码
from amqplib import client_0_8 as amqp
import time
##创建con和chanel
con = amqp.Connection(host="192.168.1.188:32769", userid="guest", password="guest", virtual_host="/")
chan = con.channel()
####创建queue,并持久化queue
chan.queue_declare(queue="hello",durable=True)
######定义回调函数
def recv_callback(msg):
    print('Received: ' + msg.body + ' from channel #' + str(msg.channel.channel_id))
    #######接收并处理unack的message
    time.sleep(10)
    #######发送ack机制
    chan.basic_ack(msg.delivery_tag)
######开始消费、consume
chan.basic_consume(queue="hello",callback=recv_callback,consumer_tag="testtag")

#####开始监听
while True:
    chan.wait()

######取消消费
chan.basic_cancle("testtag")
复制代码

 send.py代码 

复制代码
from amqplib import client_0_8 as amqp
import sys
##创建con和chanel
con = amqp.Connection(host="192.168.1.188:32769", userid="guest", password="guest", virtual_host="/")
chan = con.channel()
####创建queue,并持久化queue
chan.queue_declare(queue="hello",durable=True)
####创建消息
massage = ' '.join(sys.argv[1:])
msg = amqp.Message(massage)
######持久化message
msg.properties["delivery_mode"]=2
###发送
chan.basic_publish(msg,exchange='',routing_key="hello")

####关闭
chan.close()
con.close()
复制代码

  重新启动server,那些未ack的message并不会丢掉,重新启动消费者依然能够拿到、处理、发送ack

 

  • 公平分发

  你可能也注意到了,分发机制不是那么优雅。默认状态下,RabbitMQ将第n个Message分发给第n个Consumer。当然n是取余后的。它不管Consumer是否还有unacked Message,只是按照这个默认机制进行分发

  那么如果有个Consumer工作比较重,那么就会导致有的Consumer基本没事可做,有的Consumer却是毫无休息的机会。那么,RabbitMQ是如何处理这种问题呢

  

  通过 basic.qos 方法设置prefetch_count=1 。这样RabbitMQ就会使得每个Consumer在同一个时间点最多处理一个Message。换句话说,在接收到该Consumer的ack前,他它不会将新的Message分发给它。 设置方法如下

  

复制代码

from amqplib import client_0_8 as amqp
import time
##创建con和chanel
con = amqp.Connection(host="192.168.1.188:32769", userid="guest", password="guest", virtual_host="/")
chan = con.channel()
####创建queue,并持久化queue
chan.queue_declare(queue="hello",durable=True)
######定义回调函数
def recv_callback(msg):
print('Received: ' + msg.body + ' from channel #' + str(msg.channel.channel_id))
#######接收并处理unack的message
time.sleep(10)
#######发送ack机制
chan.basic_ack(msg.delivery_tag)
######公平分发
channel.basic_qos(prefetch_count=1)
######开始消费、consume
chan.basic_consume(queue="hello",callback=recv_callback,consumer_tag="testtag")

#####开始监听
while True:
chan.wait()

######取消消费
chan.basic_cancle("testtag")

复制代码

 

  注意,这种方法可能会导致queue满。当然,这种情况下你可能需要添加更多的Consumer,或者创建更多的virtualHost来细化你的设计

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值