一起来学SpingBoot(二十一)消息队列RabbitMq

序言

RabbitMQ是一个遵循AMQP协议,由面向高并发的erlanng语言开发而成,用在实时的对可靠性要求比较高的消息传递上,支持多种语言客户端。支持 延迟队列(这是一个非常有用的功能)。

基础概念

Broker:简单来说就是消息队列服务器实体
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列
Queue:消息队列载体,每个消息都会被投入到一个或多个队列
Binding:绑定,它的作用就是把exchangequeue按照路由规则绑定起来
Routing Key:路由关键字,exchange根据这个关键字进行消息投递
vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离
producer:消息生产者,就是投递消息的程序
consumer:消息消费者,就是接受消息的程序
channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务

Exchange

在rabbitmq中可以添加 fanoutdirecttopicheaders 这四种,其中呢headers 这种几乎不用就不说这种了。其余三种就长话短说,做一个入门。

fanout

fanout可以绑定多个队列,只要想fanout中的队列一个发送消息,则这个交换机里的所有队列全部都会收到。

direct

可以实现JMS中点对点的功能,其发送消息的时候,是按照Routing Key 来匹配的,如果绑定的队列中全等,则发送。

topic

topic顾名思义就是发布订阅那种模式了,他和fanout的唯一的不同就是,可以按照 通配符匹配Routing Key 给部分队列发消息,#代表匹配多个单词,*表示匹配一个单词。

使用

加入如下依赖即可

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

然后在yml中配置

spring:
  rabbitmq:
    host: 127.0.0.1
    listener:
      simple:
        acknowledge-mode: manual
    port: 5672
    username: fulin
    password: wangle
    virtual-host: /

先说如何创建队列、交换机、和绑定关系。这里有两种方式

使用AmqpAdmin

@Configuration
public class RabbitAdminConfig {

    @Autowired
    private AmqpAdmin amqpAdmin;

    @Bean
    public void createRabbitInit() {
        //创建交换器
        amqpAdmin.declareExchange(new DirectExchange("amqpadmin.exchange"));
        //创建队列(如果存在同名,则不创建)
        amqpAdmin.declareQueue(new Queue("amqpadmin.queue", true));
        //创建绑定规则   new Binding(目的地,目的地类型,交换器名字,路由件,参数头)
        amqpAdmin.declareBinding(new Binding("amqpadmin.queue", Binding.DestinationType.QUEUE, "amqpadmin.exchange", "amqp.haha", null));
        //删除队列
        //amqpAdmin.deleteQueue("amqpadmin.queue");
    }

}

差不多就是这样然侯还可以使用bean配置

@Slf4j
@Configuration
public class RabbitConfig {

    public static final String FULIN_QUEUE = "dev.fulin.queue";
    public static final String FULIN_EXCHANGE = "dev.fulin.exchange";
    public static final String FULIN_ROUTING_KEY = "all";
	//创建交换器
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(FULIN_EXCHANGE);
    }
	//创建队列
    @Bean
    public Queue queue() {
        return new Queue(FULIN_QUEUE, true);
    }
	 //绑定注解和交换器
    @Bean
    public Binding registerBookBinding() {
        return BindingBuilder.bind(queue()).to(topicExchange()).with(FULIN_ROUTING_KEY);
    }
}

好了东西都会创建了那到底怎么使用呢?然后呢SpringBoot封装了一个针对于amqp协议的一个模板类rabbitTemplate 可以方便进行操作。

@RestController
public class TestController {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/queue")
    public void defaultMessage(@RequestParam("value") String value) {
        this.rabbitTemplate.convertAndSend("dev.fulin.exchange", "dev.fulin.queue", "my name is wangfulin");
    }

}

然后创建一个监听类,同样呢也是加注解@RabbitListener(queues = "dev.fulin.queue") 里面写入队列名就行了。 默认情况下 spring-boot-data-amqp 是自动ACK机制,就意味着 MQ 会在消息消费完毕后自动帮我们去ACK,这样依赖就存在这样一个问题:如果报错了,消息不会丢失,会无限循环消费,虽然可以配置消费的次数。。。推荐手动ACK然后将消费错误的消息转移到其它的消息队列中,做补偿处理。 由于我们需要手动控制ACK,因此下面监听完消息后需要调用basicAck通知rabbitmq消息已被正确消费,可以将远程队列中的消息删除 。

@Component
@Slf4j
public class QueueHandler {

    @RabbitListener(queues = "dev.fulin.queue")
    public void devFulinQueue(String value, Message message, Channel channel) {
        // TODO 如果手动ACK,消息会被监听消费,
        //  但是消息在队列中依旧存在,如果
        //  未配置 acknowledge-mode 默认是会在消费完毕后自动ACK掉
        final long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            log.info(" dev.fulin.queue 消息监听是{}", value);
            // TODO 通知 MQ 消息已被成功消费,可以ACK了
            channel.basicAck(deliveryTag, false);
        } catch (IOException e) {
            try {
                channel.basicRecover();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}

至于fanoutdirecttopic 大家多实现巩固,这些都是不难的。

延时队列

所谓延时消息就是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费。 RabbitMQ队列本身是没有直接实现支持延迟队列的功能,但可以通过它的 Time-To-Live ExtensionsDead Letter Exchange 的特性模拟出延迟队列的功能。 简称DLXDLK

Time-To-Live Extensions

RabbitMQ支持为队列或者消息设置TTL(time to live 存活时间)。TTL表明了一条消息可在队列中存活的最大时间。当某条消息被设置了TTL或者当某条消息进入了设置了TTL的队列时,这条消息会在TTL时间后**死亡成为Dead Letter**。如果既配置了消息的TTL,又配置了队列的TTL,那么较小的那个值会被取用。

Dead Letter Exchange

死信交换机,上文中提到设置了 TTL 的消息或队列最终会成为Dead Letter。如果为队列设置了Dead Letter Exchange(DLX),那么这些Dead Letter就会被重新发送到Dead Letter Exchange中,然后通过Dead Letter Exchange路由到其他队列,即可实现延迟队列的功能。

来一个队列创建

@Slf4j
@Configuration
public class RabbitConfig {

    //-- 延迟队列
    //延迟队列 TTL 名称
    private static final String FULIN_DELAY_QUEUE = "dev.fulin.delay.queue";
    // DLX,dead letter发送到的 exchange
    public static final String FULIN_DELAY_EXCHANGE = "dev.fulin.delay.exchange";
    /**
     * routing key 名称
     */
    public static final String FULIN_DELAY_ROUTING_KEY = "";


    //-- 普通队列

    public static final String FULIN_QUEUE = "dev.fulin.queue";
    public static final String FULIN_EXCHANGE = "dev.fulin.exchange";
    public static final String FULIN_ROUTING_KEY = "all";


    //-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx关联

    @Bean
    public Queue delayProcessQueue() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", FULIN_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", FULIN_ROUTING_KEY);
        return new Queue(FULIN_DELAY_QUEUE, true, false, false, params);
    }

    @Bean
    public DirectExchange delayExchange() {
        return new DirectExchange(FULIN_DELAY_EXCHANGE);
    }

    @Bean
    public Binding dlxBinding() {
        return BindingBuilder.bind(delayProcessQueue()).to(delayExchange()).with(FULIN_DELAY_ROUTING_KEY); }

    //-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx关联
    @Bean
    public TopicExchange registerBookTopicExchange() {
        return new TopicExchange(FULIN_EXCHANGE);
    }

    @Bean
    public Queue registerBookQueue() {
        return new Queue(FULIN_QUEUE, true);
    }

    @Bean
    public Binding registerBookBinding() {
        return BindingBuilder.bind(registerBookQueue()).to(registerBookTopicExchange()).with(FULIN_ROUTING_KEY);
    }
}

然后在controller中正常调用即可

@RestController
public class TestController {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/time")
    public void timeMessage(@RequestParam("value") String value) {
        this.rabbitTemplate.convertAndSend(RabbitConfig.FULIN_DELAY_EXCHANGE, RabbitConfig.FULIN_DELAY_ROUTING_KEY, "my name is wangfulin", message -> {
            message.getMessageProperties().setExpiration(10 * 1000 + "");
            return message;
        });
    }
}

然后在写个监听类看看是不是几秒后发过去了

  @RabbitListener(queues = "dev.fulin.queue")
    public void devFulinQueue(String value, Message message, Channel channel) {
        final long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            log.info(" test.fulin 消息监听是{}", value);
            channel.basicAck(deliveryTag, false);
        } catch (IOException e) {
            try {
                channel.basicRecover();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

本博文是基于springboot2.x 如果有什么不对的请在下方留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值