RabbitMQ springboot集成RabbitMQ并实现延时任务的发送和接收

起因:在实际项目开发过程中,需要使用RabbitMQ来实现消息队列的功能,比如说我发布一个动态之后,需要在30分钟使用默认用户给他点几个赞,之前考虑使用redis的zset对他进行操作,之后决定使用RabbitMQ,专业的事情使用专业的工具来操作。


第一种单体模式,即发送亦接收

1.引入RabbitMQ

        
        
            org.springframework.boot
            spring-boot-starter-amqp
        

2.yml添加相关配置

virtual-host 如何配置后文讲解,请看我的另一篇博文

  rabbitmq:
    host: 101.132.111.*(真实ip)
    port: 5672
    virtual-host: /mall
    username: mall
    password: mall

3.添加消息队列枚举类配置

@Getter
public enum QueueEnum {

    /**
     * 点赞 消息通知队列 EachPraise
     */
    QUEUE_DYNAMIC_PRAISE("mall.dynamic.praise.direct", "mall.dynamic.praise.cancel", "mall.dynamic.praise.cancel"),
    /**
     *  点赞 ttl队列
     */
    QUEUE_TTL_DYNAMIC_PRAISE("mall.dynamic.praise.direct.ttl", "mall.dynamic.praise.cancel.ttl", "mall.dynamic.praise.cancel.ttl");

    /**
     * 交换名称
     */
    private String exchange;
    /**
     * 队列名称
     */
    private String name;
    /**
     * 路由键
     */
    private String routeKey;

    QueueEnum(String exchange, String name, String routeKey) {
        this.exchange = exchange;
        this.name = name;
        this.routeKey = routeKey;
    }
}

4.添加消息队列相关配置

package com.ptdot.portal.config;

import com.ptdot.portal.domain.QueueEnum;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 消息队列相关配置
 *
 * @author macro
 * @date 2018/9/14
 */
@Configuration
public class RabbitMqConfig {


//    ============================ 点赞
    /**
     * 订单消息实际消费队列所绑定的交换机
     */
    @Bean
    DirectExchange dynamicPraiseDirect() {
        return (DirectExchange) ExchangeBuilder
                .directExchange(QueueEnum.QUEUE_DYNAMIC_PRAISE.getExchange())
                .durable(true)
                .build();
    }

    /**
     * 订单延迟队列队列所绑定的交换机
     */
    @Bean
    DirectExchange dynamicPraiseTtlDirect() {
        return (DirectExchange) ExchangeBuilder
                .directExchange(QueueEnum.QUEUE_TTL_DYNAMIC_PRAISE.getExchange())
                .durable(true)
                .build();
    }

    /**
     * 订单实际消费队列
     */
    @Bean
    public Queue dynamicPraiseQueue() {
        return new Queue(QueueEnum.QUEUE_DYNAMIC_PRAISE.getName());
    }

    /**
     * 订单延迟队列(死信队列)
     */
    @Bean
    public Queue dynamicPraiseTtlQueue() {
        return QueueBuilder
                .durable(QueueEnum.QUEUE_TTL_DYNAMIC_PRAISE.getName())
                .withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_DYNAMIC_PRAISE.getExchange())//到期后转发的交换机
                .withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_DYNAMIC_PRAISE.getRouteKey())//到期后转发的路由键
                .build();
    }

    /**
     * 将订单队列绑定到交换机
     */
    @Bean
    Binding dynamicPraiseBinding(DirectExchange dynamicPraiseDirect,Queue dynamicPraiseQueue){
        return BindingBuilder
                .bind(dynamicPraiseQueue)
                .to(dynamicPraiseDirect)
                .with(QueueEnum.QUEUE_DYNAMIC_PRAISE.getRouteKey());
    }

    /**
     * 将订单延迟队列绑定到交换机
     */
    @Bean
    Binding dynamicPraiseTtlBinding(DirectExchange dynamicPraiseTtlDirect,Queue dynamicPraiseTtlQueue){
        return BindingBuilder
                .bind(dynamicPraiseTtlQueue)
                .to(dynamicPraiseTtlDirect)
                .with(QueueEnum.QUEUE_TTL_DYNAMIC_PRAISE.getRouteKey());
    }
}

5.服务提供者

/**
 * @ClassName DynamicTimingSender
 * @Description TODO
 * @Author liulinfang
 * @Date 2020/9/21 15:40
 * @Version 1.0
 */
@Component
@Slf4j
public class DynamicPraiseSender {
    @Resource
    private AmqpTemplate amqpTemplate;

    public void sendMessage(Long dynamicId,final long delayTimes){
        //给延迟队列发送消息
        amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_DYNAMIC_PRAISE.getExchange(), QueueEnum.QUEUE_TTL_DYNAMIC_PRAISE.getRouteKey(), dynamicId, message -> {
            //给消息设置延迟毫秒值
            message.getMessageProperties().setExpiration(String.valueOf(delayTimes));
            return message;
        });
        log.info("send dynamicId:{}",dynamicId);
    }
}

6.服务消费者

/**
 * @ClassName DynamicTimingReceiver
 * @Description TODO
 * @Author liulinfang
 * @Date 2020/9/21 16:59
 * @Version 1.0
 */
@Slf4j
@Component
@RabbitListener(queues = "mall.dynamic.praise.cancel")
public class DynamicPraiseReceiver {

    //消息处理器
    @RabbitHandler
    public void handle(Long dynamicId){
        System.out.println("Receiver:"+dynamicId);
        log.error("process dynamicId:{}",dynamicId);
    }
}

7.单元测试

package com.ptdot.portal.service.impl;


import com.ptdot.portal.component.DynamicTimingSender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import javax.annotation.Resource;
import java.util.Date;

@RunWith(SpringRunner.class)
@SpringBootTest
//由于是Web项目,Junit需要模拟ServletContext,因此我们需要给我们的测试类加上@WebAppConfiguration。
@WebAppConfiguration
public class RabbitmqdemoApplicationTests {

    @Resource
    DynamicTimingSender dynamicTimingSender;

    @Test
    public void contextLoads() {
        System.out.println(System.currentTimeMillis());
        System.out.println(new Date());
        dynamicTimingSender.sendMessage(1L,10 * 1000);
    }
}


第二种任务发送接收分离模式

1.1. 创建工程

  • 提供消息对象的项目:放消息对象的
import lombok.Data;
​
import java.io.Serializable;
​
@Data
public class OrderInfo implements Serializable {
 //消息对象需要序列化
 private static final long serialVersionUID = 4084996990296644842L;
 private String id;
 private String order_name;
 //消息id是用来生成一个消息的唯一id,通过消息id能找到这个消息的业务信息
 private String message_id;
}
  • provider项目:消息发送方

  • receive项目:消息接收方

1.2. 发送方的设置

POM依赖

消息体的工程依赖


 com.icoding.basic
 rmq-basic
 1.0-SNAPSHOT
 

AMQP的依赖


 org.springframework.boot
 spring-boot-starter-amqp
 

Yaml的配置

spring:
 rabbitmq:
 host: 39.99.219.219
 username: guest
 password: guest
 virtual-host: /
 connection-timeout: 15000

编写发送类

import com.icoding.basic.po.OrderInfo;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
​
@Component
public class OrderSender {
​
 @Autowired
 RabbitTemplate rabbitTemplate;
​
 public void sendOrder(OrderInfo orderInfo) throws Exception{
 /**
 * exchange: 交换机名字,是个你自己定义的字符串
 * routingkey: 队列关联的key,是个你自己定义的字符串
 * object: 要传输的消息对象
 * correlationData: 消息的唯一id
 */
 CorrelationData correlationData = new CorrelationData();
 correlationData.setId(orderInfo.getMessage_id());
 rabbitTemplate.convertAndSend("order-exchange","order.update",orderInfo,correlationData);
 }
}

这个时候还不能发送消息,因为还没有创建exchange,可以在控制台创建exchange

image-20200226222921963.png
  • type是exchage的routingkey的绑定类型

  • Durability:消息是否持久化

  • Auto delete:如果设置为yes则当exchange最后一个绑定的队列被删除后,就会自动删除

  • Internal:如果设置为yes,是RabbitMQ的内部使用,不提供给外部,自己编写erlang语言做扩展时使用

  • Arguments:扩展AMQP的自定义参数

创建消息队列

image-20200226223502985.png

在exchange里创建Binding并输入routingkey

  • 这里的routingkey就是我们的一个接收规则
image-20200226223802830.png

这个时候再启动消息的发送MQ就能接收到消息了

import com.icoding.basic.po.OrderInfo;
import com.icoding.provider.provider.OrderSender;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
​
@SpringBootTest
class RmqProviderApplicationTests {
​
 @Autowired
 OrderSender orderSender;
​
 @Test
 void contextLoads() {
 OrderInfo orderInfo = new OrderInfo();
 orderInfo.setId("10001");
 orderInfo.setOrder_name("消息队列RabbitMQ从入门到精通");
 orderInfo.setMessage_id("MS99999");
 try {
 System.out.println("***********开始发送************");
 orderSender.sendOrder(orderInfo);
 System.out.println("-----------发送完成------------");
 }catch (Exception ex){
 ex.printStackTrace();
 }
 }
}
image-20200226224105217.png

1.3. 接收方的设置

POM依赖

       
            org.springframework.boot
            spring-boot-starter-amqp
        
        
            com.icoding.basic
            rmq-basic
            1.0-SNAPSHOT
        

yaml设置

spring:
  rabbitmq:
    host: 39.99.219.219
    username: guest
    password: guest
    virtual-host: /
    connection-timeout: 15000
    listener: #消费端配置
      simple:
        concurrency: 5 #初始化并发数
        max-concurrency: 10 #最大并发数据
        auto-startup: true #自动开启监听
        prefetch: 1 #每个并发连接同一时间最多处理几个消息,限流设置
        acknowledge-mode: manual #签收模式,设置为手动

编写接收的实现类

import com.icoding.basic.po.OrderInfo;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class OrderReceiver {

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "order-queue",durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = "order-exchange",durable = "true",type = "topic"),
            key = "order.update"
        )
    )
    @RabbitHandler
    public void onOrderMessage(@Payload OrderInfo orderInfo, @Headers Map headers, Channel channel) throws Exception{
        System.out.println("************消息接收开始***********");
        System.out.println("Order Name: "+orderInfo.getOrder_name());
    }
}

完结,之后对细节进行补充


不要以为每天把功能完成了就行了,这种思想是要不得的,互勉~!

你可能感兴趣的:(RabbitMQ springboot集成RabbitMQ并实现延时任务的发送和接收)