RabbitMQ消息中间件

RabbitMq简介:是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用之间共享数据RabbitMQ使用ErLang语         言编写的并且是基于AMQP协议的。

 RabbitMq安装使用:略

 核心概念:

  • server:又称为brocker,接受客户端的连接实现AMOP实体服务工作
  • connection:应用程序与brocker之间的网络连接
  • channel:网络信道,几乎所有的操作都在这里进行,是进行消息读写的通道。客户端可以建立多个通道,每个通道代表一   个会话任务。
  • message:消息,服务器和应用程序之间传送的数据,由properties和body组成。properties可以对消息修饰,比如消息的优先级,延迟性等。body就是消息内容。
  • virtual host:虚拟主机地址。用于进行逻辑隔离。最上层的消息路由。一个virtual host 里面可以有若干个Exchange 和Queue,同一个virtual host不能有相同名称的Exchange或Queue.
  • Exchange:交换机,接受消息,根据路由key转发消息到绑定的队列
  • bingding:Exchange和Queue之间的虚拟连接,bingding中可以包含 routing key
  • Routing key:一个路由规则,虚拟机用它来确定如何路由一个特定消息
  • Queue: 消息队列,保存消息并转发给消费者

死信队列

什么情况下消息会变成死信?

1)消息被消费者拒绝并且未设置重回队列:(NACK || Reject ) && requeue == false

2)消息过期

3)队列达到最大长度,超过了 Max length(消息数)或者 Max length bytes (字节数),最先入队的消息会被发送到 DLX

死信队列如何使用?

1、声明原交换机(ORI_USE_EXCHANGE)、原队列(ORI_USE_QUEUE)相互绑定
队列中的消息 10 秒钟过期,因为没有消费者,会变成死信。指定原队列的死信交换机(DEAD_LETTER_EXCHANGE)

@Bean("oriUseExchange")
public DirectExchange exchange() {
    return new DirectExchange("ORI_USE_EXCHANGE", true, false, new HashMap<>());
}
@Bean("oriUseQueue")
public Queue queue() {
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("x-message-ttl", 10000); // 10 秒钟后成为死信
    map.put("x-dead-letter-exchange", "DEAD_LETTER_EXCHANGE"); // 队列中的消息变成    死信后,进入死信交换机
    return new Queue("ORI_USE_QUEUE", true, false, false, map);
}
@Bean
public Binding binding(@Qualifier("oriUseQueue") Queue queue,@Qualifier("oriUseExchange") DirectExchange exchange) {
    return BindingBuilder.bind(queue).to(exchange).with("ori.use");
}

2 、 声 明 死 信 交 换 机 (DEAD_LETTER_EXCHANGE ) 、 死信队列 (DEAD_LETTER_QUEUE),相互绑定

@Bean("deatLetterExchange")
public TopicExchange deadLetterExchange() {
    return new TopicExchange("DEAD_LETTER_EXCHANGE", true, false, new HashMap<>());
}

@Bean("deatLetterQueue")
public Queue deadLetterQueue() {
    return new Queue("DEAD_LETTER_QUEUE", true, false, false, new HashMap<>());
}

@Bean
public Binding bindingDead(@Qualifier("deatLetterQueue") Queue queue,@Qualifier("deatLetterExchange") TopicExchange exchange) {
    return BindingBuilder.bind(queue).to(exchange).with("#"); // 无条件路由
}

3、最终消费者监听死信队列。
4、生产者发送消息。

延迟队列

RabbitMQ 本身不支持延迟队列,总的来说有三种实现方案:

1、 先存储到数据库,用定时任务扫描

2、 利用 RabbitMQ 的死信队列(Dead Letter Queue)实现

3、 利用 rabbitmq-delayed-message-exchange 插件

使用死信队列实现延时消息的缺点:

1) 如果统一用队列来设置消息的 TTL,当梯度非常多的情况下,比如 1 分钟,2 分钟,5 分钟,10 分钟,20 分钟,30 分钟……需要创建很多交换机和队列来路由消息。

2) 如果单独设置消息的 TTL,则可能会造成队列中的消息阻塞——前一条消息没有出队(没有被消费),后面的消息无法投递(比如第一条消息过期 TTL 是 30min,第二条消息 TTL 是 10min。10 分钟后,即使第二条消息应该投递了,但是由于第一条消息 还未出队,所以无法投递。

3) 可能存在一定的时间误差

服务端流控

https://www.rabbitmq.com/configure.html 

队列有两个控制长度的属性:(意义不大)

x-max-length:队列中最大存储最大消息数,超过这个数量,队头的消息会被丢 弃。

x-max-length-bytes:队列中存储的最大消息容量(单位 bytes),超过这个容 量,队头的消息会被丢弃

内存控制

RabbitMQ 会在启动时检测机器的物理内存数值。默认当 MQ 占用 40% 以上内 存时,MQ 会主动抛出一个内存警告并阻塞所有连接(Connections)。可以通过修改 rabbitmq.config 文件来调整内存阈值,默认值是 0.4      [{rabbit, [{vm_memory_high_watermark, 0.4}]}]

磁盘控制

  当磁盘空间低于指定的值时(默认 50MB),触发流控措施

消费端限流

https://www.rabbitmq.com/consumer-prefetch.html

可以基于 Consumer 或者 channel 设置 prefetch count 的值,含义为 Consumer端的最大的 unacked messages 数目。当超过这个数值的消息未被确认,RabbitMQ 会停止投递新的消息给该消费者

channel.basicQos(2); // 如果超过 2 条消息没有发送 ACK,当前消费者不再接受队列消息

channel.basicConsume(QUEUE_NAME, false, consumer);

springboot整合Rabbitmq:

  • 加入依赖
  • 配置文件

来个例子:

public class Order implements Serializable{

     private String name;

     private String id;
     
     private String messageId;
//set get 省略

}

@component
public class OrderSender{
    
    @Autowired
    private RabbitTemplate rabbitTemplate;

        //不要忘了创建交换机和队列
    public sendOrder(Order order){

    CorrelationData correlationData = new CorrelationData();
        correlationData .set(order.getMessageId);
        //有很多重载方法
        rabbitTemplate.converAndSend(
            
            "order-exchange",    //交换机
            "order.abcd",        //routingkey  order.* order.#
            order,               // 消息体内容
            correlationData      // 消息唯一标识
        )

    }




}

消费端的配置

 

@Component
public class OrderReceiver{

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "order-queue", durable = "true"),
            exchange = @Exchange(name="order-exchange", durable = "true", type="topic")
            key = "order.*"

        )

    )
    @RabbitHandler
    public void onOrderMessage(

        @Payload Order order,    //消息体内容
        @Heads Map<String,Object>,    //消息的properties
         Channer channel
        )
    {

        Long deliverTag=(Long) headers.get(AmapHeaders.DELIVERY_TAG);
        channel.basicAck(deliverTag,false);//手动回收



    }



}

消息100%可靠的投递解决方案

https://www.rabbitmq.com/reliability.html

 

消息发送到 RabbitMQ 服务器

在 RabbitMQ 里面提供了两种机制服务端确认机制,也就是在生产者发送消息给 RabbitMQ 的服务端的时候,服务端会通过某种方式返回一个应答,只要生产者收到了 这个应答,就知道消息发送成功了.

第一种是 Transaction(事务)模式,第二种 Confirm(确认)模式。

Transaction(事务)模式

事务模式有一个缺点,它是阻塞的
    try {
            channel.txSelect();
            // 发送消息
            // String exchange, String routingKey, BasicProperties props, byte[] body
            channel.basicPublish("", QUEUE_NAME, null, (msg).getBytes());
            // int i =1/0;
            channel.txCommit();
            System.out.println("消息发送成功");
        } catch (Exception e) {
            channel.txRollback();
            System.out.println("消息已经回滚");
        }

Spring Boot 中的设置:rabbitTemplate.setChannelTransacted(true);

Confirm(确认)模式

异步确认模式需要添加一个 ConfirmListener,并且用一个 SortedSet 来维护没有被确认的消息。
Confirm 模式是在 Channel 上开启的,因为 RabbitTemplate 对 Channel 进行了封装,叫做ConfimrCallback


        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                if (!ack) {
                    System.out.println("发送消息失败:" + cause);
                    throw new RuntimeException("发送异常:" + cause);
                }
            }
        });

消息从交换机路由到队列

两种方式处理无法路由的消息,一种就是让服务端重发给生产者,一种是让交换机路由到另一个备份的交换机

  • 消息回发的方式:使用 mandatory 参数和 ReturnListener(在 Spring AMQP 中是 ReturnCallback)
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback(){
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey){
                System.out.println("回发的消息:");
                System.out.println("replyCode: "+replyCode);
                System.out.println("replyText: "+replyText);
                System.out.println("exchange: "+exchange);
                System.out.println("routingKey: "+routingKey);
            }
        });
  • 消息路由到备份交换机的方式:在创建交换机的时候,从属性中指定备份交换机。
Map<String,Object> arguments = new HashMap<String,Object>();
arguments.put("alternate-exchange","ALTERNATE_EXCHANGE"); // 指定交换机的备份交换机
channel.exchangeDeclare("TEST_EXCHANGE","topic", false, false, false, arguments);
(注意区别,队列可以指定死信交换机;交换机可以指定备份交换机)

消息在队列存储

把消息本身和元数据(队列、交换机、绑定)都 保存到磁盘。开启一系列的持久化属性。

消息投递到消费者

RabbitMQ 提供了消费者的消息确认机制(message acknowledgement),消费者可以自动或者手动地发送 ACK 给服务端

NONE:自动 ACK

MANUAL: 手动 ACK

AUTO:如果方法未抛出异常,则发送 ack。

当抛出 AmqpRejectAndDontRequeueException 异常的时候,则消息会被拒绝, 且不重新入队。当抛出 ImmediateAcknowledgeAmqpException 异常,则消费者会 发送 ACK。其他的异常,则消息会被拒绝,且 requeue = true 会重新入队

消费者回调

消费者的是否成功最终生产者是无法知道的。

1) 调用生产者 API

2) 发送响应消息给生产者

 

 

 

 

 

首先要开启异步确认模式 

主要代码 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值