RabbitMQ消息中间件

MQ

优点:

  1. 可以实现异步通信:
    (1) 同步通信:请求后,阻塞在那,一直等待响应
    (2) 异步通信:请求发送后,就立即返回了。不会阻塞
  2. 实现系统解耦
    (1)系统耦合:系统或者模块之间项目依赖
  3. 流量削峰

缺点:

  1. 系统可用性降低
  2. 数据一致性问题
  3. 系统复杂度变高

RabbitMQ

1.实现了AMQP协议
2.运行模型:
在这里插入图片描述
3.组成
(1) Broker运行实例: 一个实例可以有多个VHost虚拟主机
(2) virtual host (VHost)虚拟主机:一个服务可以有多个VHost,不同的Vhost之间是相互隔离的.
(3) exchange 交换机:是VHost的组成部分,生产这将消息发送到Exchange中,交换机通过绑定规则将消息分发到响应的队列
(4) queue 队列:是VHost的组成部分、
(5) Channel信道

Exchange交换机

1.direct 交换机(直连交换机)

1.创建交换机,指定绑定键
2.生产者推送消息时,需要指定对应的交换机、路由键、消息内容
3.当路由键符合绑定键规则时,消息才会推送到对应的队列中

2.Topic交换机(主题交换机)

1.创建交换机,指定绑定键
2.通配符:* 匹配一个单词; # 匹配0个或多个单词 以 . 分割单词

3.Fanout交换机(广播交换机)

1.创建交换机,不需要指定绑定键

死信队列(Dead letter)

消息过期时间
  1. TTL:统一设置队列里面的消息的过期时间
  2. 也可以设置一条消息单独的过期时间。
  3. 如果消息有统一的过期时间和单独的过期时间,那么以最小的过期时间为准
死信队列(dead letter)
  1. 死信:
    (1) 过期的消息
    (2) 消费者拒绝了这条消息,并且不让他重回队列
    (3) 如果消息超过了定义的max length 或 max length bytes 消息也会放入到死信队列中
  2. 死信队列:保存过期的消息
    如何创建一个死信队列?在创建队列时可以指定死信交换机,死信交换机绑定对应的死信队列

延迟队列

基于rabbitMq的插件进行实现。

  1. 下载插件
    2.将插件放入plugins中。
    3.启用插件。需要输入命令
    4.插件会提供一个特殊的类型的交换机:x-delaymessage-exchange

流量控制

服务端流控:

  1. 内存控制

40% Conn vm_memeory_high_watermark 内存使用大小达到40%

  1. 磁盘控制:

disk_free_limit.relative=0.3 磁盘用量达到30%
disk_free_limit.absolute=2G 磁盘存储的达到2G则不再接受消息

消费端控制:

/**
* 默认:自动应答。消费者获取到消息后,会立即发送一个ack指令给生产者。
* 下面代码:设置为手动应答模式,如果超过两条消息没有应答,则暂停就收消息
*/
channel.basicQos(2);
channel.basicCounsume(QUEUQ_NAME,false,consumer)

安装

  1. RabbitMQ是基于erLang语言开发的。要安装mq首先需要有erlang的环境
  2. 安装好rabbitmq,通过:http://127.0.0.1:15672可以访问管理员界面,如果页面无法访问,可能是插件没开启,执行以下命令:
windows: 
	1. rabbitmqctl.bat start_app
	2. rabbitmq-plugins enable rabbitmq_management
	3. rabbitmqctl stop

Springboot集成rabbitmq

<!-- 导入依赖 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

交换机和消息队列遵循的原则是:那边使用那边维护
应为交换机和队列是在消费者方维护的,那么启动的时候应该先启动消费者。
若先启动生产者,如果mq服务没有对应的交换机和队列,那么投递消息会失败。

生产者

1.生产消息(使用AMQPTemplate推送消息)
2.将消息丢给交换机,并携带上路由键

PropertySource("classpath:rabbitmqProvider.properties")
@Component
public class ProviderSender {

    @Value(value = "${com.rabbitmq.provider.directExchange}")
    private String directExchange;

    @Value(value = "${com.rabbitmq.provider.directBindKey}")
    private String directBindKey;

    @Value(value = "${com.rabbitmq.provider.topicExchange}")
    private String topicExchange;

    @Value(value = "${com.rabbitmq.provider.topicBindKey}")
    private String topicKey;

    @Value(value = "${com.rabbitmq.provider.fanoutExchange}")
    private String fanoutExchange;

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void send() {
        System.out.println("================生产者开始生产消息================");
        /**
         * direct exchange
         */
        amqpTemplate.convertAndSend(directExchange, directBindKey, "this is a direct message");
        System.out.println("<========推送消息至:"+directExchange+" ====>");
        /**
         * topic exchange
         */
        amqpTemplate.convertAndSend(topicExchange, topicKey, "this is a topic message");
        System.out.println("<========推送消息至:"+topicExchange+" ====>");

        /**
         * fanout exchange
         */
        amqpTemplate.convertAndSend(fanoutExchange, "", "this is a fanout message");
        System.out.println("<========推送消息至:"+fanoutExchange+" ====>");

        System.out.println("<================生产者结束生产消息================>");

    }
}

消费者(消费消息)

1.维护交换机
2.维护队列
3.建立交换机和队列之间的绑定关系

/**
 *  配置交换机和队列 及 之间的关系
 */
@PropertySource("classpath:rabbitMqConsumer.properties")
@Configuration
public class RabbitMQConfig {

    @Value(value = "${com.rabbitmq.consumer.directExchange}")
    private String directExchange;

    @Value(value = "${com.rabbitmq.consumer.directQueue}")
    private String directQueue;

    @Value(value = "${com.rabbitmq.consumer.directQueueKey}")
    private String directQueueKey;

    @Value(value = "${com.rabbitmq.consumer.topicExchange}")
    private String topicExchange;

    @Value(value = "${com.rabbitmq.consumer.topicQueue}")
    private String topicQueue;

    @Value(value = "${com.rabbitmq.consumer.topicQueueKey}")
    private String topicQueueKey;

    @Value(value = "${com.rabbitmq.consumer.fanoutExchange}")
    private String fanoutExchange;

    @Value(value = "${com.rabbitmq.consumer.fanoutQueue}")
    private String fanoutQueue;

    @Bean("directQueue")
    public Queue directQueue(){
        return new Queue(directQueue);
    }

    @Bean
    public DirectExchange directExchange() {
        return  new DirectExchange(directExchange);
    }

    @Bean("topicQueue")
    public Queue topicQueue(){
        return new Queue(topicQueue);
    }

    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(topicExchange);
    }

    @Bean("fanoutQueue")
    public Queue fanoutQueue(){
        return new Queue(fanoutQueue);
    }

    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(fanoutExchange);
    }

    @Bean
    public Binding bindDirectExchange(@Qualifier("directQueue") Queue directQueue,DirectExchange directExchange) {
        return BindingBuilder.bind(directQueue).to(directExchange).with(directQueueKey);
    }

    @Bean
    public Binding bindTopicExchange(@Qualifier("topicQueue") Queue topicQueue,TopicExchange topicExchange) {
        return BindingBuilder.bind(topicQueue).to(topicExchange).with(topicQueueKey);
    }

    @Bean
    public Binding bindingFanoutExchange(@Qualifier("fanoutQueue") Queue fanoutQueue,FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue).to(fanoutExchange);
    }
}
@Component
@PropertySource("classpath:rabbitMqConsumer.properties")
@RabbitListener(queues = "${com.rabbitmq.consumer.directQueue}") //配置该类是rabbitMq的监听类 并配置监听的队列
public class DirectQueueConsumer {

    @RabbitHandler //监听到有消息投递 则调用的处理方法  方法名称自定义
    public void receiveMessage(String message){
        System.out.println("<===========directQueue=========>");
        System.out.println(message);
    }

}

可靠性投递

  1. 消息丢失
    (1) 生产者发送消息到Broker
    (2)交换机通过绑定键将消息路由到队列失败(绑定键不匹配)
    (3)消息在队列中,一致没有被消费,队列或服务出现故障。
  2. 消息重复投递
    (1)消费者:收到消息后,告诉broker消息可以删除

解决方案

服务端确认
Transcation模式
  1. 将channel设置成事务模式 channel.txSelect()
  2. channel.txCommit();//发送成功
  3. channel.txRollback();//发送失败
    缺点:当消息推送过程中是一个阻塞的方式。没发送一次消息,都需要txSelect() txCommit() txRollback() 命令复杂过多。
    工作流程:
    在这里插入图片描述
Confirm模式
  1. 将channel设置为confirm模式
  2. broker发送成功后,会回应一个ack指令,通过:channel.waitConfirms()获取指定。缺点:每条消息都判断
  3. 批量确认:channel.waitForCOnfirmsOrDie 全部发送失败,该方法会抛出异常。缺点:有一条失败,则全部失败。
  4. 异步确认

工作流程:
在这里插入图片描述

路由保证
  1. mandatory = true+mandatory
  2. 使用备份交换机,交换机的属性中,可以配置备份交换机。
消费端确认模式

1.AutoAck自动应答(服务自带应答)
2.手动应答:设置channel的手动应答

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值