RabbitMQ从入门到精通----基本概念

理解消息通信

当我们提到消息通信时,经常会联想到向微信以及QQ这些即时通信的工具,不过这些不是我们所要讨论的RabbitMQ消息通信,二者之间有着很大的区别,虽然RabbitMQ与前者有着类似的地方,但是RabbitMQ更为灵活,向邮箱一类的通信工具,具有着一定的格式,但是AMQP(高级消息队列)更为灵活,消息没有固定的结构,同时可以存储二进制数据。

生产者与消费者

当我们学习多线程的时候,最经典的问题就是生产者以及消费者,二者有着一定的耦合数据,那么我们今天讨论的RabbitMQ可以理解为数据的存储容器,他不负责生产数据,同时我们也可以理解为,RabbitMQ是一个路由的作用负责接收生产者的数据,同时负责发送数据给消费者。

生产者:创建信息,发送给RabbitMQ,消息包含两部分内容:有效载荷(payload)和标签(label),有效载荷就是我们所要传递的数据,标签即为描述有效载荷的信息,RabbitMQ根据标签,来决定谁将获得信息的拷贝。

消费者:它们链接到代理服务器RabbitMQ上,然后订阅到队列( quene)上,把消息队列想象为特定的邮箱,每当消息到达特定的邮箱的时候,RabbitMQ或将其发送给一个订阅的消费者上面。

理解起来非常简单,我们可以把它想象成为一个特别大的通道,同时RabbitMQ为一个转送站, 负责将将生产者的数据发送给特定的消费者,生产者以及消费者之间的连接也就是我们经常提到的主题(topic)。

当我们要使用RabbitMQ的时候生产者以及消费者都需要链接到RabbitMQ上,你的应用程序和RabbitMQ程序之间创建一条TCP连接,一旦TCP连接打开之后,应用程序就可以创建AMQP信道,信道是建立在TCP连接之上,每条信道都会有唯一的ID,AMQP库进行保存,不论发送信息还是接受信息都是建立在信道的基础上,在信道上进行传输。

信道的作用:避免过多的TCP连接,当信息并发量较大的时候,如果都采用TCP链接则会创建过多的连接,很快就会碰到性能瓶颈,但是为了使每个线程具有私密性,各个不相互干扰,那么我们便创建了信道。

这里写图片描述

队列

当我们在学习多线程的时候,用于存放生产者生产出来的信息成为队列。那么我们来看看RabbitMQ的队列。

从概念上来讲,AMQP消息路由必须有三部分:交换器、队列、绑定。生产者将信息发送到交换器上,消息最终到达队列,并被消费者接收,绑定决定了信息如何从路由器到特定的队列上。

这里写图片描述

消费者通过以下两种命令从特定的队列中获取信息:

  1. 通过AMQP的basic.consume命令订阅,这样做将信道置为接收模式,订阅消息后,消费者再消费最近的消息后,会自动的接收下一条信息。
  2. 通过AMQP的basic.get命令,获取单挑信息而不是持续订阅,这样做消费者不会自动的去获取下一条消息,需要获取仍需要进行basic.get,大致上来讲get方法是通过订阅消息然后获取单条信息,然后在取消订阅,因此我们不能将其发在循环中取获取消息,这样会影响RabbitMQ的性能。

如果没有队列上没有消费者订阅,那么消息会存储在队列中,知道有订阅的消费者订阅,然后发送消息给消费者,那么当多个消费者订阅了队列又该如何处置呢。

当有多个消费者订阅到RabbitMQ的队列上,那么队列收到的消息将会以循环的方式发送给消费者,例如第一次发给A,第二次发给B,然后依次循环。当消息被消费者消费后,队列将会删除消息。

RabbitMQ在删除队列中的消息时,要求消费者发送消息接收到的确认,消费者通过basic.ack命令显示的想RabbitMQ发送确认,然后队列在执行删除操作,或者我们将basic.ack参数设置为true,那么RabbitMQ会自动视为消费者确认接收到消息。

如果消费者在接收到消息之后,确认之前与RabbitMQ断开连接,RabbitMQ认为这条消息没有被分发,会继续发送给下一个消费者,同时如果我们的程序忘了发送确认信息,那么RabbitMQ将不会发送信息给该消费者,认为其没有做好接受消息的准备。

拒绝消息

我们有两种方式拒绝消息:

  1. 直接断开与RabbitMQ的连接,RabbitMQ认为其没有确认消息,然后在自动的消息重新入队发送给下一位消费者。
  2. 在RabbitMQ2.0.0之后,我们可以采用basic.reject拒绝消息,如果我们把reject的参数设置为true那么消息会自动入队,然后分发给下一位消费者,设置为false,那么消息会自动删除。
  3. 当然我们也可以不做任何操作,然后发送确认信息。

在RabbitMQ中有一个特殊的队列“死信”,我们可以通过来获取被拒绝然后移除队列的消息,我们可以通过这个方案来查询程序问题。

创建队列

生产者以及消费者都可以通过queue.declare命令来创建队列,但是消费者在一个信道上订阅了另一个队列的话,就无法创建队列,必须取消订阅,才可。

当我们创建队列的时候,通常会指定队列的名称,消费者订阅队列的时候需要队列的名称,并在创建绑定的时候也需要指定队列的名称,如果我们不指定队列的名称的话,RabbitMQ会分配一个随机名称并在queue.declare命令中返回。队列设置的参数:

  • exclusive—–如果设置为tre的话,队列将会变位私有,此时只有你的应用程序才能消费队列中的消息,当你逍遥限制一个队列只有一个消费者的时候很有帮助
  • auto-delete——当最后一个消费者取消订阅的时候,队列就会从RabbitMQ中删除,如果我们需要临时队列置为一个消费者服务的话,接上上面两种参数。

如果我们尝试声明一个已经存在的队列,如果声明队列的参数与已有队列相同,RabbitMQ什么也不会做,就如同创建队列成功,但是如果参数不相同,那么会报错。如果我们只是想要知道队列是否创建成功,可以采用queue.declare的passive参数设置为true,如果队列存在返回返回成功,不存在报错。

交换器和绑定

当我们吧消息投递给交换器,交换器根据 路由键(touting key),队列通过路由键绑定到交换器上,如果路由键不存在那么消息会进入黑洞也就是消息丢失。

交换器通过路右键将消息发送个队列,但是如何处理投递到多个队列的情况呢?

协议中定义了四种不同的类型的交换器发挥了作用,direct、 fanout、topic、headers。

  • direct:如果路由键匹配的话,消息就被投递到对应的队列basic_publish($msg,"","queue—name")第一个参数发送的消息内容,第二个参数默认的交换器,第三个路由键,我们之前声明队列的名称。
  • fanout:这种交换器会将接受到的消息广播到绑定的队列上,即当我们发送一条消息到fanout,它会将消息发送到所有的附加在此交换器上的队列
  • topic:假设我们有一个日志系统,队列负责接受日志,首先我们声明一个队列,然后bind到交换器上面queue.bind(msg-inbox-errors,'logs-exchange','error.msg-inbox'),消费者通过basic.publish($msg,'logs-exchange','error.msg-inbox'),发送日志,这是我们想要一个队列监听msg-inbox模块上的所有error级别的日志,我们可以通过将新的队列绑定到同一个交换器上queue.bind('msg-inbox-logs','logs-exchange','*.msg-inbox')那么msg-inbox-logs队列将会接受所有的msg-inbox的日志。

多租户模式:虚拟主机和隔离

每个RabbitMQ都能创建虚拟消息服务器,我们称之为虚拟主机(vhost),每个vhost是一个mini版的RabbitMQ,拥有自己的队列、交换器和绑定等,拥有自己的权限控制。那么我们一个RabbitMQ可以服务多个应用程序,而不用担心程序之间的相互干扰,vhost是RabbitMQ的基本概念,必须在连接时进行指定,由于RabbitMQ采用了开箱即用的默认vhost‘/’,因此使用起来非常方便,默认的用户名密码为guest。

当我们在RabbitMQ创建一个用户的时候,需要为其指定至少一个vhost,并且只能指定 被指派的vhost,vhost之间是绝对隔离的,当我们在RabbitMQ集群上创建vhost时,那么整个集群都会创建该vhost。

创建vhost:

 rabbitmqctl add_vhost [hostname]

删除vhost:

 rabbitmqctl delete_vhost [hostname]

显示vhost:

 rabbitmqctl list_vhosts 

持久化

当服务器宕机时,存储于RabbitMQ中消息,交换器以及队列等都会不翼而飞,我们需要打开每个队列以及交换器的durable属性,该属性默认为false,这样当服务器重启后,队列以及交换器都会重新进行创建。

但是对于消息来说,在消息发布之前,通过它的“投递模式”选项设置为2来吧消息标志位持久化,但是他只是呗标记为持久化,他还需要被发布到持久化的交换器中,并到达持久化的队列中才行。因此持久化消息需要进行:

  1. 把他投递模式的选项设置为2(持久化)
  2. 发送到持久化的交换器
  3. 到达持久化的队列

只有做到了以上3点,消息才会持久化。RabbitMQ的持久化采用写入磁盘的持久化日志中。

事务

RabbitMQ提供事务的支持,确保生产者发送到RabbitMQ中并持久化到磁盘中,但是效率比较低,不做过多的介绍。

更好的解决方案:
发送方确认模式,需要告诉RabbitMQ将信道设置为confirm模式,而且只能通过重新创建信道来关闭该设置,一旦信道进入confirm模式,所有在信道中发送的信息都会被指定唯一的ID,一定被发送到消息队列,那么会发送一个确认信息给生产者程序包含信息的唯一ID,如果消息队列是可持久的话,那么消息只有在被写入到磁盘后才会返回信息。

发送确认模式采用异步的方式,生产者可以在发送完消息后继续发送下一条,当确认消息最终收到的时候,应用程序会调用回调方法来触发处理该信息,如果发生错误,RabbitMQ会发送nack(not acknowledged,未确认)消息。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值