rabbitMQ入门

介绍

RabbitMQ是一个消息代理:它接受并转发消息。你可以把它当成一个邮局:当你想邮寄信件的时候,你会把信件放在投递箱中,并确信邮递员最终会将信件送到收件人的手里。在这个例子中,RabbitMQ就相当与投递箱、邮局和邮递员。

RabbitMQ与邮局的区别在于:RabbitMQ并不处理纸质信件,而是接受、存储并转发二进制数据---消息。

安装

docker安装rabbitmq:

https://www.jianshu.com/p/14ffe0f3db94

docker镜像集群

https://blog.csdn.net/qq_40378034/article/details/89788708

使用场景

他就是服务器之间通信的,相对于其他通信在中间做了一个中间仓库。这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量。

与其他队列的对比

https://www.cnblogs.com/mengchunchen/p/9999774.html

相关名称

1. 生产者: 在现实生活中就好比制造商品的工厂,他们是商品的生产者。生产者只意味着发送。发送消息的程序称之为一个生产者。

2. 队列:rabbitMQ就像一个仓库,一个仓库里面可以 有很多队列,每个队列才是服务器之间消息通信的载体。

3.消费者:消费者就好比是从商店购买或从仓库取走商品的人,消费的意思就是接收。消费者是一个程序,主要是等待接收消息。

4.交换器:在生产者和消息队列之间的交换器,功能类似于网络宽带的交换机,可以根据不同的关键字,将信息发送到不同的队列。

在RabbitMQ中,生产者不是直接将消息发送给消费者,生成者根本不知道这个消息要传递给哪些队列。实际上,生产者只是将消息发送到交换机。交换机收到消息到,根据交换机的类型和配置来处理消息,有如下几种情况:

  • 将消息传送到特定的队列
  • 有可能发送到多个队列中
  • 也有可能丢弃消息

在声明交换机时还可以附带许多其他的属性,其中最重要的几个分别是:

Name:交换机名称
Durability:是否持久化。如果持久性,则RabbitMQ重启后,交换机还存在
Auto-delete:当所有与之绑定的消息队列都完成了对此交换机的使用后,删掉它
Arguments:扩展参数

上图的E就是交换器,通过关键字绑定,如果生产者给的消息中指定类型是ERROR,就给队列1,如果是INFO或者WARN就给队列2。当然也可以一个关键字绑定两个队列。(INFO等字段自己可以定义,也可以用*,#来匹配。*(星号)表示一个单词#(井号)表示零个或者多个单词。 比如ok.yes可以被ok.*匹配到)

5.临时队列:根据需求临时创建的一条队列,在断开连接后自动删除。

 

流程介绍

  生产者发送一条消息给交换机——交换机根据关键字匹配到对应的队列——将消息存入队列——消费者从队列中取出消息使用
 

交换机的类型:

交换机主要包括如下4种类型:

  • Direct exchange(直连交换机)
  • Fanout exchange(扇型交换机)
  • Topic exchange(主题交换机)
  • Headers exchange(头交换机)

另外RabbitMQ默认定义一些交换机:

  • 默认交换机
  • amq.* exchanges

还有一类特殊的交换机:Dead Letter Exchange(死信交换机)

Direct exchange直连交换机(使用例子:https://blog.csdn.net/vbirdbest/article/details/78596426
直连型交换机(direct exchange)是根据消息携带的路由键(routing key)将消息投递给对应队列的,步骤如下:

将一个队列绑定到某个交换机上,同时赋予该绑定一个路由键(routing key)
当一个携带着路由值为R的消息被发送给直连交换机时,交换机会把它路由给绑定值同样为R的队列。
Fanout exchange扇型交换机(使用例子:https://blog.csdn.net/vbirdbest/article/details/78628659
扇型交换机(funout exchange)将消息路由给绑定到它身上的所有队列。不同于直连交换机,路由键在此类型上不启任务作用。如果N个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的发送给这所有的N个队列

Topic exchange主题交换机(使用例子:https://blog.csdn.net/vbirdbest/article/details/78631035
主题交换机(topic exchanges)中,队列通过路由键绑定到交换机上,然后,交换机根据消息里的路由值,将消息路由给一个或多个绑定队列。

扇型交换机和主题交换机异同:

对于扇型交换机路由键是没有意义的,只要有消息,它都发送到它绑定的所有队列上
对于主题交换机,路由规则由路由键决定,只有满足路由键的规则,消息才可以路由到对应的队列上
Headers exchange头交换机(使用例子:https://blog.csdn.net/vbirdbest/article/details/78638988
类似主题交换机,但是头交换机使用多个消息属性来代替路由键建立路由规则。通过判断消息头的值能否与指定的绑定相匹配来确立路由规则。
此交换机有个重要参数:”x-match”

当”x-match”为“any”时,消息头的任意一个值被匹配就可以满足条件
当”x-match”设置为“all”的时候,就需要消息头的所有值都匹配成功

RabbitMQ默认定义一些交换机

在RabbitMQ默认定义一些交换机,主要如下:

默认交换机
默认交换机(default exchange)实际上是一个由RabbitMQ预先声明好的名字为空字符串的直连交换机(direct exchange)。它有一个特殊的属性使得它对于简单应用特别有用处:那就是每个新建队列(queue)都会自动绑定到默认交换机上,绑定的路由键(routing key)名称与队列名称相同。

如:当你声明了一个名为”hello”的队列,RabbitMQ会自动将其绑定到默认交换机上,绑定(binding)的路由键名称也是为”hello”。因此,当携带着名为”hello”的路由键的消息被发送到默认交换机的时候,此消息会被默认交换机路由至名为”hello”的队列中。即默认交换机看起来貌似能够直接将消息投递给队列,如同我们之前文章里看到一例子。

类似amq.*的名称的交换机

这些是RabbitMQ默认创建的交换机。这些队列名称被预留做RabbitMQ内部使用,不能被应用使用,否则抛出403 (ACCESS_REFUSED)错误

 

 

@RabbitListener 与 @RabbitHandler

http://www.imooc.com/article/274470

RabbitMQ之消息确认机制(事务+Confirm)

https://blog.csdn.net/u013256816/article/details/55515234

https://www.cnblogs.com/wangiqngpei557/p/9381478.html

RabbitMQ消息确认机制之消息的正确消费

消费者的ack方式默认是自动的,也就是说消息一旦被消费(无论是否处理成功),消息都会被确认,然后会从队列中删除。这就意味着当消息处理失败的时候,也会被从队列中删除,这绝对不是我们所期望的。我们希望当消息正确消费时,消息从队列中删除,否则,消息不能删除,该消息应该继续被消费,直到成功消费。

所以,首先我们将ack的方式设置为手动:

spring:
  rabbitmq:
    host: xxx.xxx.xxx.xx
    port: 5672
    username: xxxx
    password: xxxx
    listener:
      direct:
        acknowledge-mode: manual   # 配置该消费者的ack方式为手动

消费成功后手动确认

处理成功时直接确认,处理失败时,将消息重新放回队列中。

package com.space.rbq.store.consumer;
 
import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import com.space.rbq.store.bean.Order;
import com.space.rbq.store.service.StoreService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import java.io.IOException;
 
/**
 * 负责接收处理订单服务发送的消息
 * @author zhuzhe
 * @date 2018/6/7 10:09
 * @email 1529949535@qq.com
 */
@Slf4j
@Component
public class OrderConsumer {
 
    @Autowired
    private StoreService storeService;
 
    /*对列名称*/
    public final String QUEUE_NAME1 = "first-queue";
 
    /**
     * queues  指定从哪个队列(queue)订阅消息
     * @param message
     * @param channel
     */
    @RabbitListener(queues = {QUEUE_NAME1})
    public void handleMessage(Message message,Channel channel) throws IOException {
        try {
            // 处理消息
            System.out.println("OrderConsumer {} handleMessage :"+message);
            // 执行减库存操作
            storeService.update(new Gson().fromJson(new String(message.getBody()),Order.class));
 
           /**
            * 第一个参数 deliveryTag:就是接受的消息的deliveryTag,可以通过msg.getMessageProperties().getDeliveryTag()获得
            * 第二个参数 multiple:如果为true,确认之前接受到的消息;如果为false,只确认当前消息。
            * 如果为true就表示连续取得多条消息才发会确认,和计算机网络的中tcp协议接受分组的累积确认十分相似,
            * 能够提高效率。
            *
            * 同样的,如果要nack或者拒绝消息(reject)的时候,
            * 也是调用channel里面的basicXXX方法就可以了(要指定tagId)。
            *
            * 注意:如果抛异常或nack(并且requeue为true),消息会重新入队列,
            * 并且会造成消费者不断从队列中读取同一条消息的假象。
            */
            // 确认消息
            // 如果 channel.basicAck   channel.basicNack  channel.basicReject 这三个方法都不执行,消息也会被确认
            // 所以,正常情况下一般不需要执行 channel.basicAck
            // channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
 
        }catch (Exception e){
            log.error("OrderConsumer  handleMessage {} , error:",message,e);
            // 处理消息失败,将消息重新放回队列
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,true);
        }
    }
}
/*
* 消息的标识,false只确认当前一个消息收到,true确认consumer获得的所有消息
* channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
*
* ack返回false,并重新回到队列
* channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
*
* 拒绝消息
* channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
*

这样,如果处理失败,handleMessage方法就会一直收到这个消息,直到成功消费。 

关于ack可以再看看这篇文章https://my.oschina.net/gaoguofan/blog/776057

 

Message详解

消息。服务器和应用程序之间传送的数据,本质上就是一段数据,由Properties和Payload(body)组成。

Properties(属性)

配置项类型说明
content_type短文本MIME类型表示消息是一种什么类型的格式,参考MIME类型
content_encoding短文本正文传输编码,比如内容是gzip压缩的.值就是gzip,参考
application_headers数组请求的headers信息
delivery_mode数字表示是否持久化,1为否,2为是 参考
priority数字发送权重,也就是优先级
correlation_id短文本相关性ID 参考
reply_to短文本消息被发送者处理完后,返回回复时执行的回调
expiration短文本存活时间,毫秒数
message_id短文本扩展属性
timestamp数字时间戳
type短文本扩展属性
user_id短文本扩展属性
app_id短文本扩展属性
cluster_id短文本扩展属性

 

channel参数

channel.exchangeDeclare()

channel.ExchangeDeclare(string exchange: "cjlTest",string  type: "direct/topic/header/fanout",bool  durable: true);

参数解析:

         exchange:交换机名称

         type:交换机类型,有fanout、direct、topic、header;选择合适自己的

        durable:是否开启持久化exchange。true:服务器重启会保留下来Exchange。警告:仅设置此选项,不代表消息持久化。即不保证重启后消息还在。

        autoDelete: 当已经没有消费者时,服务器是否可以删除该exchange

实现真正持久化两步操作

①、将queue的持久化标识durable设置为true,则代表是一个持久的队列

②、发送消息的时候将deliveryMode=2

chanel.basicQos()

channel.basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;

参数解析:

  prefetchSize:消息的大小

  prefetchCount:会告诉RabbitMQ不要同时给一个消费者推送多于N个消息,即一旦有N个消息还没有ack,则该consumer将block掉,直到有消息ack

  global:是否将上面设置应用于channel,简单点说,就是上面限制是channel级别的还是consumer级别

channel.basicPublish()

void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body)
            throws IOException;
参数解析:
  exchange:交换机名称
  routingKey:路由键,#匹配0个或多个单词,*匹配一个单词,在topic exchange做消息转发用
  mandatory:为true时如果exchange根据自身类型和消息routeKey无法找到一个符合条件的queue,那么会调用basic.return方法将消息返还给生产者。为false时出现上述情形broker会直接将消息扔掉
  immediate:为true时如果exchange在将消息route到queue(s)时发现对应的queue上没有消费者,那么这条消息不会放入队列中。当与消息routeKey关联的所有queue(一个或多个)都没有消费者时,该消息会通过basic.return方法返还给生产者。
    props:需要注意的是BasicProperties.deliveryMode,1:不持久化 2:持久化 这里指的是消息的持久化,配合channel(durable=true),queue(durable)可以实现,即使服务器宕机,消息仍然保留
    body:要发送的信息

channel.basicAck()

void basicAck(long deliveryTag, boolean multiple) throws IOException;
参数解析
  deliveryTag:该消息的index
    multiple:是否批量处理.true:将一次性ack所有小于deliveryTag的消息。

channel.basicNack()

void basicNack(long deliveryTag, boolean multiple, boolean requeue)
            throws IOException;
参数解析
deliveryTag:该消息的index
multiple:是否批量.true:将一次性拒绝所有小于deliveryTag的消息。
requeue:被拒绝的是否重新入队列

channel.basicReject()

void basicReject(long deliveryTag, boolean requeue) throws IOException;

参数解析
deliveryTag:该消息的index
requeue:被拒绝的是否重新入队列

channel.basicNack 与 channel.basicReject 的区别在于basicNack可以拒绝多条消息,而basicReject一次只能拒绝一条消息

channel.basicConsume() 

String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;

参数解析
queue:队列名称
autoAck:是否自动ack,如果不自动ack,需要使用channel.ack、channel.nack、channel.basicReject 进行消息应答callback:回调函数,一个事件

chanel.exchangeBind()

用于通过绑定bindingkey讲queue到exchange,之后便可以进行消息接收

Exchange.BindOk exchangeBind(String destination, String source, String routingKey) throws IOException;

channel.queueDeclare()

Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
                                 Map<String, Object> arguments) throws IOException;
durable:true、false true:在服务器重启时,能够存活
exclusive :是否为当前连接的专用队列,在连接断开后,会自动删除该队列,生产环境中应该很少用到吧。
autodelete:当没有任何消费者使用时,自动删除该队列

关于消息顺序与重复消费的问题:https://blog.csdn.net/varyall/article/details/79111745

 

 

RabbitAdmin 与 RabbitTemplate 使用

https://www.jianshu.com/p/e647758a7c50

springboot整合rabbitmq

https://www.cnblogs.com/hlhdidi/p/6535677.html

https://blog.51cto.com/13877966/2297056

RabbitMq各个参数含义(new Queue)

https://blog.csdn.net/fsgsggd/article/details/81349553

@RabbitListener源码解析

https://blog.csdn.net/u013905744/article/details/86736536

附:推和拉消费模型对比

RabbitMQ高可用系列之消费失败处理-死信

https://blog.csdn.net/zjcjava/article/details/79410137

 

延时队列

https://www.cnblogs.com/qjm201000/p/10346471.html

 

RabbitMQ消息可靠性投递解决方案 - 基于SpringBoot实现

https://www.imooc.com/article/49814

 

消息确认机制(Confirm模式)

https://blog.csdn.net/anumbrella/article/details/81321701

 

整理的有点乱

其他入门博客文章:https://blog.csdn.net/lyhkmm/article/details/78775369

https://blog.csdn.net/hellozpc/article/details/81436980

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值