springboot和rabbitmq整合实现普通任务和延时任务

前言

在之前的文章中,

rabbitmq的延时任务和普通任务

已经介绍过了普通的方式使用rabbitmq来实现普通任务和延时任务的调度,不过现在基本都会用springboot,所以这里还是来写一篇springboot使用rabbitmq的

一 普通任务

安装erlang,安装rabbitmq,这里就省略了

概念:queue,生产者,消费者,exchange,routeKey

生产者是消息发送者,消费者是消息接收者.这两者中间有exchange和queue.消费者需要标注queue来获取queue中的消息,exchange也就是相当于交换机,到底把哪个生产者的消息发给哪个消费者,这需要exchange根据routekey来定,比如routeKey=”key.a”和queue=”queue_a”绑定了,那么生产者必须在参数里面携带”key.a”才能发送到”queue_a”中去.

Routing key是以小数点隔开的字符串, TopicExchange模式下, 它有,#两个通配符可以用,其中代表一个单词,#代表0个或多个, 比如routeKey=”key.*”,那么生产者带的参数为”key.a”,”key.b”都可以发到”queue_a”

二 搭建springboot工程

直接使用idea的骨架生产就好了,然后pom.xml依赖如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zgd.rabbitmq</groupId>
    <artifactId>springboot-rabbitmq-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>Spring Boot Blank Project (from https://github.com/making/spring-boot-blank)</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <start-class>com.zgd.rabbitmq.App</start-class>
        <java.version>1.8</java.version>
        <lombok.version>1.14.8</lombok.version>
        <springboot.version>2.0.4.RELEASE</springboot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${springboot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>${springboot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
            <version>${springboot.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>springloaded</artifactId>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

这里要注意版本的一致,spring-boot-test和springboot的版本要一致

三 代码

3.1 配置类

写一个配置类,配置queue,exchange,绑定binding

在这个类中,定义了三个队列topic.queue_a,topic.queue_b,topic.queue_like,定义了一个exchangeexchange,分别绑定了三个routeKey topic.queue_a,topic.queue_btopic.*

注意这里bindingExchange这三个方法中的入参Queue_B和exchange都是上面声明的方法名,不是变量名,且需要和方法名一致才能确定绑定的是哪个queue和exchange

而且只在Topic Exchange下,通配routeKey才生效!!!

package com.zgd.rabbitmq.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: zgd
 * @Date: 18/08/30 15:18
 * @Description: 队列配置
 */
@Configuration
public class RabbitConfig {

  final static String QUEUE_A = "topic.queue_a";

  final static String QUEUE_B = "topic.queue_b";

  //模糊队列,满足topic.*的都会发送到这个队列
  final static String QUEUE_LIKE = "topic.queue_like";


  //------------队列queue只和消费者直接对接-----------


  @Bean
  public Queue Queue_A() {
    return new Queue(QUEUE_A);
  }

  @Bean
  public Queue Queue_B() {
    return new Queue(QUEUE_B);
  }

  @Bean
  public Queue Queue_Like() {
    return new Queue(QUEUE_LIKE);
  }

  //--------------生产者通过exchange交换器来决定发送到哪个queue-------------

  @Bean
  TopicExchange exchange() {
    return new TopicExchange("exchange");
  }

  //-------------绑定exchange和queue,通过with()中的值来作为绑定key,让exchange知道生产者的消息该分配到哪条queue-----------------

  /**
   * 使用"topic.queue_a"来作为key来匹配生产者和队列,如果生产者使用这个key,就会经过exchange发到QUEUE_A队列
   * @param Queue_A
   * @param exchange
   * @return
   */
  @Bean
  //入参中的Queue_A和exchange是方法名,不是参数名
  Binding bindingExchangeA(Queue Queue_A, TopicExchange exchange) {
    return BindingBuilder.bind(Queue_A).to(exchange).with("topic.queue_a");
  }

  /**
   * 使用"topic.queue_b"来作为key来匹配生产者和队列,如果生产者使用这个key,就会经过exchange发到QUEUE_B队列
   * @param Queue_B
   * @param exchange
   * @return
   */
  @Bean
  Binding bindingExchangeB(Queue Queue_B, TopicExchange exchange) {
    return BindingBuilder.bind(Queue_B).to(exchange).with("topic.queue_b");
  }

  /**
   * 使用"topic.#"来作为key来模糊匹配,生产者使用的key是topic.开头的,都会经过exchange发到QUEUE_LIKE队列
   * @param Queue_Like
   * @param exchange
   * @return
   */
  @Bean
  Binding bindingExchangeLike(Queue Queue_Like, TopicExchange exchange) {
    return BindingBuilder.bind(Queue_Like).to(exchange).with("topic.*");
  }
}
3.2 生产者

this.rabbitTemplate.convertAndSend()三个参数,第一个是exchange名,第二个是routeKey,第三个是消息

其中sendMessageA和sendMessageB都是一对一,routeKey和queueName一样,完全匹配,sendMessageA只会向topic.queue_a队列发送,b则对应

sendMessageLike中,因为刚刚在配置类配置了routeKey topic.*,所以是一对多,所有符合topic.*的都会发到Queue_Like队列中去,包括上面的sendMessageA和sendMessageB

package com.zgd.rabbitmq.producer;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @Author: zgd
 * @Date: 18/08/30 15:21
 * @Description: 生产者
 */
@Component
public class RabbitmqProducer {

  @Autowired
  private AmqpTemplate rabbitTemplate;

  public void sendMessageA(String msg) {
    String context = "[生产者A发送消息]\t"+new Date().toLocaleString() + "\t------------> "+msg  ;
    System.out.println(context);
    this.rabbitTemplate.convertAndSend("exchange","topic.queue_a", msg);
  }

  public void sendMessageB(String msg) {
    String context = "[生产者B发送消息]\t"+new Date().toLocaleString() + "\t------------> "+msg  ;
    System.out.println(context);
    this.rabbitTemplate.convertAndSend("exchange","topic.queue_b", msg);
  }

  public void sendMessageLike(String msg) {
    String context = "[生产者like发送消息]\t"+new Date().toLocaleString() + "\t------------> "+msg  ;
    System.out.println(context);
    this.rabbitTemplate.convertAndSend("exchange","topic.aa",msg);
  }

}
3.3 消费者

使用注解@RabbitHandler和@RabbitListener来指定一个queue,监听这个queue,可以监听多个queue消费

package com.zgd.rabbitmq.consumer;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @Author: zgd
 * @Date: 18/08/30 15:22
 * @Description: 消费者
 */

@Component
public class RabbitmqConsumer {


  @RabbitHandler
  @RabbitListener(queues = "topic.queue_a")
  public void processA(String msg) {
    System.out.println("[消费者queue_a收到消息]:\t" + new Date().toLocaleString()+"\t------------> "+ msg);
  }

  @RabbitHandler
  @RabbitListener(queues = "topic.queue_b")
  public void processB(String msg) {
    System.out.println("[消费者queue_b收到消息]:\t" + new Date().toLocaleString()+"\t------------> "+ msg);
  }

  @RabbitHandler
  @RabbitListener(queues = "topic.queue_like")
  public void processLike(String msg) {
    System.out.println("[模糊匹配的消费者queue_like收到消息]:\t" + new Date().toLocaleString()+"\t------------> "+ msg);
  }
}
3.4 测试
package com.zgd.rabbitmq.test;

import com.zgd.rabbitmq.producer.DelayRabbitmqProducer;
import com.zgd.rabbitmq.producer.RabbitmqProducer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.TimeUnit;

/**
 * @Author: zgd
 * @Date: 18/08/30 15:28
 * @Description:
 */

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqTest {

  @Autowired
  private RabbitmqProducer producer;


  @Test
  public void sendMsg() throws Exception {
    producer.sendMessageA("AAAAAAAAA");
    producer.sendMessageB("BBBBBBBBB");
    producer.sendMessageLike("********");
  }
}

这里写图片描述
可以看到模糊匹配,也就是topic模式下,队列topic.queue_like在用topic.*的routeKey与exchange绑定后,生产者发送topic.queue_a, topic.queue_b, topic.queue_like的消息都经过topic.queue_like,被processLike消费者消费了, 而processA和processB都是消费各自的消息

四 延时任务

4.1 配置类修改

增加一个样式的队列,exchange

//-----------------------延时任务--------------------------
  /**
   * 延时任务的exchange
   * @return
   */
  @Bean
  public DirectExchange  delayExchange() {
    DirectExchange directExchange = new DirectExchange("delay_exchange", true, false);
    /**只需简单一步开启延时消息,md找了好久,原来这么简单*/
    directExchange.setDelayed(true);
    return directExchange;
  }

  /**
   * 延时任务的队列
   * @return
   */
  @Bean
  public Queue delayQueue() {
    Queue queue = new Queue("queue.delay");
    return queue;
  }


  @Bean
  Binding binding(Queue delayQueue,DirectExchange delayExchange) {
    return BindingBuilder.bind(delayQueue).to(delayExchange).with("delay.abc");

  }

注意这里不是用的TopicExchange了,而是DirectExchange

4.2 生产者
package com.zgd.rabbitmq.producer;

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Timer;
import java.util.concurrent.TimeUnit;

/**
 * @Author: zgd
 * @Date: 18/08/30 17:46
 * @Description: 延时任务生产者
 */
@Component
public class DelayRabbitmqProducer {

  @Autowired
  private AmqpTemplate rabbitTemplate;

  /**
   * 发送延时消息
   * @param msg
   * @param millisecond
   */
  public void sendDelay(String msg, final Integer millisecond){
    String context = "[延时任务发送消息]\t"+new Date().toLocaleString() + "\t------------> "+msg  ;
    System.out.println(context);
    rabbitTemplate.convertAndSend("delay_exchange", "delay.abc", msg, new MessagePostProcessor() {
      @Override
      public Message postProcessMessage(Message message) throws AmqpException {
        message.getMessageProperties().setHeader("x-delay",millisecond);
        return message;
      }
    });
  }
}
4.3 消费者
@RabbitHandler
  @RabbitListener(queues = "queue.delay")
  public void processDelay(String msg) {
    System.out.println("[延时任务收到消息]:\t" + new Date().toLocaleString()+"\t------------> "+ msg);
  }
4.4 测试
 @Autowired
  private DelayRabbitmqProducer delayProducer;

 @Test
  public void sendDelayMsg() throws Exception {
    delayProducer.sendDelay("延时消息",10000);
    while (true){

    }
  }

这里写图片描述
测试成功

代码:
https://github.com/zzzgd/demo-rabbitmq/tree/master/springboot-rabbitmq-demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值