SpringBoot使用RabbitMQ延时队列
RabbitMQ简介
AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
延时队列
延时队列的使用场景:
1.订单业务:在电商中,用户下单后30分钟后未付款则取消订单。
2.短信通知:用户下单并付款后,1分钟后发短信给用户。
SpringBoot整合RabbitMQ
添加依赖
1、在 pom.xml
中添加 spring-boot-starter-amqp
的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2、在 application.properties
文件中配置rabbitmq
相关内容
#对于rabbitMQ的支持
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
使用Topic模式
1、初始化queue exchange和queue及exchange之间的binding关系
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.amqp.core.Queue;
import java.util.HashMap;
import java.util.Map;
/**
* @auther: ***
* @time: 2019-12-19 11:11
*/
@Configuration
public class Config {
public static final String IMMEDIATE_QUEUE = "queue.demo.immediate";//立即消费的队列名称
public static final String IMMEDIATE_EXCHANGE = "exchange.demo.immediate";//立即消费的exchange
public static final String IMMEDIATE_ROUTING_KEY = "routingkey.demo.immediate";//立即消费的routing-key 名称
public static final String DELAY_QUEUE= "queue.demo.delay";//延时消费的队列名称
public static final String DEAD_LETTER_EXCHANGE = "exchange.demo.delay";//延时消费的exchange
public static final String DELAY_ROUTING_KEY = "routingkey.demo.delay";//延时消费的routing-key名称
// 创建一个立即消费队列
@Bean
public Queue immediateQueue() {
// 第一个参数是创建的queue的名字,第二个参数是是否支持持久化
return new Queue(IMMEDIATE_QUEUE, true);
}
// 创建一个延时队列
@Bean
public Queue delayQueue() {
Map<String, Object> params = new HashMap<>();
// x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
params.put("x-dead-letter-exchange", IMMEDIATE_EXCHANGE);
// x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
params.put("x-dead-letter-routing-key", IMMEDIATE_ROUTING_KEY);
return new Queue(DELAY_QUEUE, true, false, false, params);
}
public DirectExchange immediateExchange() {
// 一共有三种构造方法,可以只传exchange的名字, 第二种,可以传exchange名字,是否支持持久化,是否可以自动删除,
//第三种在第二种参数上可以增加Map,Map中可以存放自定义exchange中的参数
return new DirectExchange(IMMEDIATE_EXCHANGE, true, false);
}
@Bean public DirectExchange deadLetterExchange() {
// 一共有三种构造方法,可以只传exchange的名字, 第二种,可以传exchange名字,是否支持持久化,是否可以自动删除,
// 第三种在第二种参数上可以增加Map,Map中可以存放自定义exchange中的参数
return new DirectExchange(DEAD_LETTER_EXCHANGE, true, false);
}
//把立即消费的队列和立即消费的exchange绑定在一起
@Bean
public Binding immediateBinding() {
return BindingBuilder.bind(immediateQueue()).to(immediateExchange()).with(IMMEDIATE_ROUTING_KEY);
}
//把延时消费的队列和延时消费的exchange绑定在一起
@Bean
public Binding delayBinding() {
return BindingBuilder.bind(delayQueue()).to(deadLetterExchange()).with(DELAY_ROUTING_KEY);
}
}
2、生产者生产消息
import com.demo01.demo.config.Config;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 消费者消费消息
* @auther: ***
* @time: 2019-12-19 11:21
*/
@Component
@EnableRabbit
@Configuration
public class ImmediateReceiver {
@RabbitListener(queues = Config.IMMEDIATE_QUEUE)
@RabbitHandler
public void get(String msg) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("收到延时消息时间:"+sdf.format(new Date()) + " Delay sent.");
System.out.println("收到延时消息了:" + msg);
}
}
消费者消费消息:
import com.demo01.demo.config.Config;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @auther: **
* @time: 2019-12-19 11:20
*/
@Component
public class ImmediateSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(String msg, int delayTime) {
System.out.println("msg="+",delayTime" + delayTime);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
this.rabbitTemplate.convertAndSend(Config.DEAD_LETTER_EXCHANGE, Config.DELAY_ROUTING_KEY, msg, message -> {
message.getMessageProperties().setExpiration(delayTime + ""); System.out.println(sdf.format(new Date()) + " Delay sent."); return message;
});
}
}
测试类
import com.demo01.demo.component.ImmediateSender;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.TimeUnit;
@SpringBootTest
class DemoApplicationTests {
@Autowired
ImmediateSender immediateSender;
@Test
public void test() {
immediateSender.send("我是一个延时消息",30000);//3秒
//让服务一直挂起,不然,接收消息时,服务已经停了
while(true){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
第一次运行,需要进入rabbitmq管理界面,加上exchange,不然会报错(no exchange ‘exchange.demo.immediate’ in vhost ‘/’)
添加方式:1.浏览器打开:http://127.0.0.1:15672 2.选择Exchanges 3.Add a new exchange ,填写name:exchange.demo.immediate,type选择:direct,点击Add exchange ,完成。
mq管理界面,加上exchange,不然会报错(no exchange ‘exchange.demo.immediate’ in vhost ‘/’)
添加方式:1.浏览器打开:http://127.0.0.1:15672 2.选择Exchanges 3.Add a new exchange ,填写name:exchange.demo.immediate,type选择:direct,点击Add exchange ,完成。