├── README.md ├── .gitignore ├── Dockerfile ├── src └── main │ ├── resources │ └── logback.xml │ └── java │ └── io │ └── biteenew │ └── rabbitmq │ ├── properties │ └── RabbitmqClientOptions.java │ ├── Launcher.java │ └── handler │ ├── Consumer1.java │ ├── Consumer2.java │ ├── Producer2.java │ ├── Producer1.java │ ├── Consumer3.java │ └── Producer3.java └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | ## RabbitMQ实现延迟队列 2 | 使用RabbitMQ的TTL(Time-To-Live)和DLX(Dead Letter Exchanges)特性实现的消息延迟队列的代码样例:实现对消息的延迟消费和延迟重试。 3 | 详细说明看这里:[RabbitMQ实现延迟队列](https://biteeniu.github.io/rabbitmq/rabbitmq_delay_queue/) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre 2 | MAINTAINER biteeniu 3 | WORKDIR /usr/src 4 | RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone 5 | COPY target/rabbitmq-delay-queue-v1.0.0-jar-with-dependencies.jar /usr/src/rabbitmq-delay-queue.jar 6 | CMD ["java", "-jar", "/usr/src/rabbitmq-delay-queue.jar"] -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/io/biteenew/rabbitmq/properties/RabbitmqClientOptions.java: -------------------------------------------------------------------------------- 1 | package io.biteenew.rabbitmq.properties; 2 | 3 | /** 4 | * RabbitMQ客户端参数定义类 5 | * @author luzhanghong 6 | * @date 2018-07-02 13:25 7 | */ 8 | public class RabbitmqClientOptions { 9 | 10 | private String host; // RabbitMQ服务器地址(域名或IP) 11 | private Integer port; // RabbitMQ服务器端口 12 | private String username; // RabbitMQ服务器登录用户名 13 | private String password; // RabbitMQ服务器登录密码 14 | 15 | public String getHost() { 16 | return host; 17 | } 18 | 19 | public RabbitmqClientOptions setHost(String host) { 20 | this.host = host; 21 | return this; 22 | } 23 | 24 | public Integer getPort() { 25 | return port; 26 | } 27 | 28 | public RabbitmqClientOptions setPort(Integer port) { 29 | this.port = port; 30 | return this; 31 | } 32 | 33 | public String getUsername() { 34 | return username; 35 | } 36 | 37 | public RabbitmqClientOptions setUsername(String username) { 38 | this.username = username; 39 | return this; 40 | } 41 | 42 | public String getPassword() { 43 | return password; 44 | } 45 | 46 | public RabbitmqClientOptions setPassword(String password) { 47 | this.password = password; 48 | return this; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/biteenew/rabbitmq/Launcher.java: -------------------------------------------------------------------------------- 1 | package io.biteenew.rabbitmq; 2 | 3 | import io.biteenew.rabbitmq.handler.*; 4 | import io.biteenew.rabbitmq.properties.RabbitmqClientOptions; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * 程序启动类 10 | * @author luzhanghong 11 | * @date 2018-07-02 10:07 12 | */ 13 | public class Launcher { 14 | 15 | private final static Logger LOGGER = LoggerFactory.getLogger(Launcher.class); 16 | 17 | /** 18 | * 程序启动方法 19 | * @param args args 20 | */ 21 | public static void main(String[] args) { 22 | RabbitmqClientOptions options = new RabbitmqClientOptions() 23 | .setHost("10.200.0.206") 24 | .setPort(5672) 25 | .setUsername("rabbitmq") 26 | .setPassword("mZzMeaxhUZxqWm7F"); 27 | 28 | // 消息延迟消费模式:方式1测试:为发布的每一条消息设置TTL 29 | // test1(options); 30 | // 消息延迟消费模式:方式2测试:为队列设置TTL 31 | // test2(options); 32 | // 消息延迟重试模式测试 33 | test3(options); 34 | } 35 | 36 | private static void test1(RabbitmqClientOptions options) { 37 | Producer1 producer1 = new Producer1(options); 38 | Consumer1 consumer1 = new Consumer1(options); 39 | // 测试1:生产5条消息,分别设置消息的TTL为:1秒,2秒,3秒,4秒,5秒 40 | for (int i = 1; i <= 5; i++) { 41 | String message = "" + i; 42 | LOGGER.info("Producer publish message: {}", message); 43 | producer1.publish(message.getBytes(), i*1000L); 44 | } 45 | // 测试2:生产5条消息,分别设置消息的TTL为:5秒,4秒,3秒,2秒,1秒 46 | for (int i = 5; i > 0; i--) { 47 | String message = "" + i; 48 | LOGGER.info("Producer publish message: {}", message); 49 | producer1.publish(message.getBytes(), i*1000L); 50 | } 51 | } 52 | 53 | private static void test2(RabbitmqClientOptions options) { 54 | Producer2 producer2 = new Producer2(options); 55 | Consumer2 consumer2 = new Consumer2(options); 56 | // 测试1:生产5条消息,分别设置消息的TTL为:1秒,2秒,3秒,4秒,5秒 57 | for (int i = 1; i <= 5; i++) { 58 | String message = "" + i; 59 | LOGGER.info("Producer publish message: {}", message); 60 | producer2.publish(message.getBytes()); 61 | } 62 | } 63 | 64 | private static void test3(RabbitmqClientOptions options) { 65 | Producer3 producer3 = new Producer3(options); 66 | Consumer3 consumer3 = new Consumer3(options); 67 | for (int i = 1; i <= 5; i++) { 68 | String message = "" + i; 69 | LOGGER.info("Producer publish message: {}", message); 70 | producer3.publish(message.getBytes()); 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/io/biteenew/rabbitmq/handler/Consumer1.java: -------------------------------------------------------------------------------- 1 | package io.biteenew.rabbitmq.handler; 2 | 3 | import com.rabbitmq.client.*; 4 | import io.biteenew.rabbitmq.properties.RabbitmqClientOptions; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 消息消费者
13 | * 使用方式1:为发布的每条消息单独设置TTL 14 | * @author luzhanghong 15 | * @date 2018-07-03 15:21 16 | */ 17 | public class Consumer1 { 18 | 19 | private final static Logger LOGGER = LoggerFactory.getLogger(Consumer1.class); 20 | private ConnectionFactory connectionFactory; 21 | private Connection connection; 22 | private final static String DXL_EXCHANGE = "dlx.exchange"; 23 | private final static String DLX_ROUTING_KEY = "dlx.routing.key"; 24 | private final static String QUEUE = "worker-queue"; 25 | 26 | public Consumer1(RabbitmqClientOptions options) { 27 | init(options); 28 | connectAndDeclare(options); 29 | } 30 | 31 | /** 32 | * 初始化连接参数 33 | * @param options RabbitMQ客户端参数 34 | */ 35 | private void init(RabbitmqClientOptions options) { 36 | connectionFactory = new ConnectionFactory(); 37 | connectionFactory.setHost(options.getHost()); 38 | connectionFactory.setPort(options.getPort()); 39 | if (StringUtils.isNoneBlank(options.getUsername(), options.getPassword())) { 40 | connectionFactory.setUsername(options.getUsername()); 41 | connectionFactory.setPassword(options.getPassword()); 42 | } 43 | // 设置连接超时时间:20秒 44 | connectionFactory.setConnectionTimeout(20000); 45 | } 46 | 47 | /** 48 | * 连接RabbitMQ服务器并创建相关的交换机和队列 49 | * @param options RabbitMQ客户端参数 50 | */ 51 | private void connectAndDeclare(RabbitmqClientOptions options) { 52 | try { 53 | connection = connectionFactory.newConnection(); 54 | Channel channel = connection.createChannel(); 55 | channel.exchangeDeclare(DXL_EXCHANGE, BuiltinExchangeType.DIRECT, true); 56 | channel.queueDeclare(QUEUE, true, false, false, null); 57 | channel.queueBind(QUEUE, DXL_EXCHANGE, DLX_ROUTING_KEY); 58 | channel.basicConsume(QUEUE, true, new DefaultConsumer(channel) { 59 | @Override 60 | public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { 61 | LOGGER.info("Consumer received message: {}", new String(body)); 62 | } 63 | }); 64 | LOGGER.info("Consumer connected to broker[{}:{}].", options.getHost(), options.getPort()); 65 | } catch (Exception e) { 66 | LOGGER.error("Consumer connect or declare exchange(queue) with error[{}:{}]: {}", options.getHost(), options.getPort(), e.getLocalizedMessage()); 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/io/biteenew/rabbitmq/handler/Consumer2.java: -------------------------------------------------------------------------------- 1 | package io.biteenew.rabbitmq.handler; 2 | 3 | import com.rabbitmq.client.*; 4 | import io.biteenew.rabbitmq.properties.RabbitmqClientOptions; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 消息消费者
13 | * 使用方式1:为发布的每条消息单独设置TTL 14 | * @author luzhanghong 15 | * @date 2018-07-03 15:21 16 | */ 17 | public class Consumer2 { 18 | 19 | private final static Logger LOGGER = LoggerFactory.getLogger(Consumer2.class); 20 | private ConnectionFactory connectionFactory; 21 | private Connection connection; 22 | private final static String DXL_EXCHANGE = "dlx.exchange2"; 23 | private final static String DLX_ROUTING_KEY = "dlx.routing.key2"; 24 | private final static String QUEUE = "worker-queue2"; 25 | 26 | public Consumer2(RabbitmqClientOptions options) { 27 | init(options); 28 | connectAndDeclare(options); 29 | } 30 | 31 | /** 32 | * 初始化连接参数 33 | * @param options RabbitMQ客户端参数 34 | */ 35 | private void init(RabbitmqClientOptions options) { 36 | connectionFactory = new ConnectionFactory(); 37 | connectionFactory.setHost(options.getHost()); 38 | connectionFactory.setPort(options.getPort()); 39 | if (StringUtils.isNoneBlank(options.getUsername(), options.getPassword())) { 40 | connectionFactory.setUsername(options.getUsername()); 41 | connectionFactory.setPassword(options.getPassword()); 42 | } 43 | // 设置连接超时时间:20秒 44 | connectionFactory.setConnectionTimeout(20000); 45 | } 46 | 47 | /** 48 | * 连接RabbitMQ服务器并创建相关的交换机和队列 49 | * @param options RabbitMQ客户端参数 50 | */ 51 | private void connectAndDeclare(RabbitmqClientOptions options) { 52 | try { 53 | connection = connectionFactory.newConnection(); 54 | Channel channel = connection.createChannel(); 55 | channel.exchangeDeclare(DXL_EXCHANGE, BuiltinExchangeType.DIRECT, true); 56 | channel.queueDeclare(QUEUE, true, false, false, null); 57 | channel.queueBind(QUEUE, DXL_EXCHANGE, DLX_ROUTING_KEY); 58 | channel.basicConsume(QUEUE, true, new DefaultConsumer(channel) { 59 | @Override 60 | public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { 61 | LOGGER.info("Consumer received message: {}", new String(body)); 62 | } 63 | }); 64 | LOGGER.info("Consumer connected to broker[{}:{}].", options.getHost(), options.getPort()); 65 | } catch (Exception e) { 66 | LOGGER.error("Consumer connect or declare exchange(queue) with error[{}:{}]: {}", options.getHost(), options.getPort(), e.getLocalizedMessage()); 67 | } 68 | } 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | io.biteenew 6 | rabbitmq-delay-queue 7 | v1.0.0 8 | jar 9 | 10 | rabbitmq-delay-queue 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 5.3.0 17 | 18 | 1.7.13 19 | 1.1.7 20 | 3.7 21 | 22 | 23 | 24 | 25 | com.rabbitmq 26 | amqp-client 27 | ${rabbitmq.version} 28 | 29 | 30 | org.slf4j 31 | slf4j-api 32 | ${slf4j-api.version} 33 | 34 | 35 | ch.qos.logback 36 | logback-core 37 | ${ch.qos.logback.version} 38 | 39 | 40 | ch.qos.logback 41 | logback-classic 42 | ${ch.qos.logback.version} 43 | 44 | 45 | ch.qos.logback 46 | logback-access 47 | ${ch.qos.logback.version} 48 | 49 | 50 | org.apache.commons 51 | commons-lang3 52 | ${commons-lang3.version} 53 | 54 | 55 | 56 | 57 | 58 | 59 | maven-assembly-plugin 60 | 2.6 61 | 62 | 63 | 64 | io.biteenew.rabbitmq.Launcher 65 | 66 | 67 | 68 | 69 | jar-with-dependencies 70 | 71 | 72 | 73 | 74 | 75 | make-assembly 76 | package 77 | 78 | single 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-compiler-plugin 88 | 3.5.1 89 | 90 | ${java.version} 91 | ${java.version} 92 | UTF-8 93 | true 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/main/java/io/biteenew/rabbitmq/handler/Producer2.java: -------------------------------------------------------------------------------- 1 | package io.biteenew.rabbitmq.handler; 2 | 3 | import com.rabbitmq.client.*; 4 | import io.biteenew.rabbitmq.properties.RabbitmqClientOptions; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * 消息生产者
15 | * 使用方式2:为队列设置TTL 16 | * @author luzhanghong 17 | * @date 2018-07-03 14:10 18 | */ 19 | public class Producer2 { 20 | 21 | private final static Logger LOGGER = LoggerFactory.getLogger(Producer2.class); 22 | private ConnectionFactory connectionFactory; 23 | private Connection connection; 24 | private Channel channel; 25 | private final static String EXCHANGE = "out.exchange2"; 26 | private final static String ROUTING_KEY = "out.routing.key2"; 27 | private final static String DXL_EXCHANGE = "dlx.exchange2"; 28 | private final static String DLX_ROUTING_KEY = "dlx.routing.key2"; 29 | private final static String QUEUE = "buffer-queue2"; 30 | 31 | public Producer2(RabbitmqClientOptions options) { 32 | init(options); 33 | connectAndDeclare(options); 34 | } 35 | 36 | /** 37 | * 初始化连接参数 38 | * @param options RabbitMQ客户端参数 39 | */ 40 | private void init(RabbitmqClientOptions options) { 41 | connectionFactory = new ConnectionFactory(); 42 | connectionFactory.setHost(options.getHost()); 43 | connectionFactory.setPort(options.getPort()); 44 | if (StringUtils.isNoneBlank(options.getUsername(), options.getPassword())) { 45 | connectionFactory.setUsername(options.getUsername()); 46 | connectionFactory.setPassword(options.getPassword()); 47 | } 48 | // 设置连接超时时间:20秒 49 | connectionFactory.setConnectionTimeout(20000); 50 | } 51 | 52 | /** 53 | * 连接RabbitMQ服务器并创建相关的交换机和队列 54 | * @param options RabbitMQ客户端参数 55 | */ 56 | private void connectAndDeclare(RabbitmqClientOptions options) { 57 | try { 58 | connection = connectionFactory.newConnection(); 59 | channel = connection.createChannel(); 60 | // 创建交换机"out.exchange":生产者将消息通过"out.exchange"发送到"buffer-queue"。这里设置交换机类型为"direct",当然也可以使用其他类型 61 | channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true); 62 | // 创建死信交换机"dlx.exchange":"buffer-queue"中产生死信后,会通过此交换机发送出去。这里设置交换机类型为"direct",当然也可以使用其他类型 63 | channel.exchangeDeclare(DXL_EXCHANGE, BuiltinExchangeType.DIRECT, true); 64 | // 创建缓冲队列"buffer-queue",并为"buffer-queue"设置死信交换机参数:生产者发布的消息会先到达此"buffer-queue",消息在"buffer-queue"中变成死信后,会通过死信交换机和死信路由键发送出去 65 | Map arguments = new HashMap<>(); 66 | arguments.put("x-message-ttl", 5000); // 指定队列中消息最大存活时间("x-message-ttl) 67 | arguments.put("x-dead-letter-exchange", DXL_EXCHANGE); // 指定死信交换机参数(x-dead-letter-exchange) 68 | arguments.put("x-dead-letter-routing-key", DLX_ROUTING_KEY); // 指定死信路由键参数(x-dead-letter-routing-key) 69 | channel.queueDeclare(QUEUE, true, false, false, arguments); 70 | // 将缓冲队列"buffer-queue"绑定到交换机"out.exchange",路由键为"out.routing.key" 71 | channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY); 72 | LOGGER.info("Producer connected to broker[{}:{}].", options.getHost(), options.getPort()); 73 | } catch (Exception e) { 74 | LOGGER.error("Producer connect or declare exchange(queue) with error[{}:{}]: {}", options.getHost(), options.getPort(), e.getLocalizedMessage()); 75 | } 76 | } 77 | 78 | /** 79 | * 发布消息 80 | * @param bytes 消息实体 81 | */ 82 | public void publish(byte[] bytes) { 83 | try { 84 | channel.basicPublish(EXCHANGE, ROUTING_KEY, null, bytes); 85 | } catch (IOException e) { 86 | LOGGER.error("Producer publish message with error: {}", e.getLocalizedMessage()); 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/io/biteenew/rabbitmq/handler/Producer1.java: -------------------------------------------------------------------------------- 1 | package io.biteenew.rabbitmq.handler; 2 | 3 | import com.rabbitmq.client.*; 4 | import io.biteenew.rabbitmq.properties.RabbitmqClientOptions; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * 消息生产者
15 | * 使用方式1:为发布的每条消息单独设置TTL 16 | * @author luzhanghong 17 | * @date 2018-07-03 14:10 18 | */ 19 | public class Producer1 { 20 | 21 | private final static Logger LOGGER = LoggerFactory.getLogger(Producer1.class); 22 | private ConnectionFactory connectionFactory; 23 | private Connection connection; 24 | private Channel channel; 25 | private final static String EXCHANGE = "out.exchange"; 26 | private final static String ROUTING_KEY = "out.routing.key"; 27 | private final static String DXL_EXCHANGE = "dlx.exchange"; 28 | private final static String DLX_ROUTING_KEY = "dlx.routing.key"; 29 | private final static String QUEUE = "buffer-queue"; 30 | 31 | public Producer1(RabbitmqClientOptions options) { 32 | init(options); 33 | connectAndDeclare(options); 34 | } 35 | 36 | /** 37 | * 初始化连接参数 38 | * @param options RabbitMQ客户端参数 39 | */ 40 | private void init(RabbitmqClientOptions options) { 41 | connectionFactory = new ConnectionFactory(); 42 | connectionFactory.setHost(options.getHost()); 43 | connectionFactory.setPort(options.getPort()); 44 | if (StringUtils.isNoneBlank(options.getUsername(), options.getPassword())) { 45 | connectionFactory.setUsername(options.getUsername()); 46 | connectionFactory.setPassword(options.getPassword()); 47 | } 48 | // 设置连接超时时间:20秒 49 | connectionFactory.setConnectionTimeout(20000); 50 | } 51 | 52 | /** 53 | * 连接RabbitMQ服务器并创建相关的交换机和队列 54 | * @param options RabbitMQ客户端参数 55 | */ 56 | private void connectAndDeclare(RabbitmqClientOptions options) { 57 | try { 58 | connection = connectionFactory.newConnection(); 59 | channel = connection.createChannel(); 60 | // 创建交换机"out.exchange":生产者将消息通过"out.exchange"发送到"buffer-queue"。这里设置交换机类型为"direct",当然也可以使用其他类型 61 | channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true); 62 | // 创建死信交换机"dlx.exchange":"buffer-queue"中产生死信后,会通过此交换机发送出去。这里设置交换机类型为"direct",当然也可以使用其他类型 63 | channel.exchangeDeclare(DXL_EXCHANGE, BuiltinExchangeType.DIRECT, true); 64 | // 创建缓冲队列"buffer-queue",并为"buffer-queue"设置死信交换机参数:生产者发布的消息会先到达此"buffer-queue",消息在"buffer-queue"中变成死信后,会通过死信交换机和死信路由键发送出去 65 | Map arguments = new HashMap<>(); 66 | arguments.put("x-dead-letter-exchange", DXL_EXCHANGE); // 指定死信交换机参数(x-dead-letter-exchange) 67 | arguments.put("x-dead-letter-routing-key", DLX_ROUTING_KEY); // 指定死信路由键参数(x-dead-letter-routing-key) 68 | channel.queueDeclare(QUEUE, true, false, false, arguments); 69 | // 将缓冲队列"buffer-queue"绑定到交换机"out.exchange",路由键为"out.routing.key" 70 | channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY); 71 | LOGGER.info("Producer connected to broker[{}:{}].", options.getHost(), options.getPort()); 72 | } catch (Exception e) { 73 | LOGGER.error("Producer connect or declare exchange(queue) with error[{}:{}]: {}", options.getHost(), options.getPort(), e.getLocalizedMessage()); 74 | } 75 | } 76 | 77 | /** 78 | * 发布消息 79 | * @param bytes 消息实体 80 | * @param ttl 消息的TTL,单位毫秒 81 | */ 82 | public void publish(byte[] bytes, long ttl) { 83 | try { 84 | AMQP.BasicProperties properties = new AMQP.BasicProperties().builder() 85 | .expiration(String.valueOf(ttl)) 86 | .build(); 87 | channel.basicPublish(EXCHANGE, ROUTING_KEY, properties, bytes); 88 | } catch (IOException e) { 89 | LOGGER.error("Producer publish message with error: {}", e.getLocalizedMessage()); 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/io/biteenew/rabbitmq/handler/Consumer3.java: -------------------------------------------------------------------------------- 1 | package io.biteenew.rabbitmq.handler; 2 | 3 | import com.rabbitmq.client.*; 4 | import io.biteenew.rabbitmq.properties.RabbitmqClientOptions; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | import java.util.Random; 11 | 12 | /** 13 | * 消息消费者
14 | * 消息延迟重试模型的消费者:使用方式2:为队列设置TTL 15 | * @author luzhanghong 16 | * @date 2018-07-03 15:21 17 | */ 18 | public class Consumer3 { 19 | 20 | private final static Logger LOGGER = LoggerFactory.getLogger(Consumer3.class); 21 | private ConnectionFactory connectionFactory; 22 | private Connection connection; 23 | private Channel channel; 24 | private final static String EXCHANGE = "out.exchange3"; 25 | private final static String ROUTING_KEY = "out.routing.key3"; 26 | private final static String DXL_EXCHANGE = "dlx.exchange3"; 27 | private final static String DLX_ROUTING_KEY = "dlx.routing.key3"; 28 | private final static String QUEUE = "worker-queue3"; 29 | private final static String FAILED_EXCHANGE = "failed.exchange"; 30 | private final static String FAILED_ROUTING_KEY = "failed.routing.key"; 31 | private final static Random random = new Random(); 32 | 33 | public Consumer3(RabbitmqClientOptions options) { 34 | init(options); 35 | connectAndDeclare(options); 36 | } 37 | 38 | /** 39 | * 初始化连接参数 40 | * @param options RabbitMQ客户端参数 41 | */ 42 | private void init(RabbitmqClientOptions options) { 43 | connectionFactory = new ConnectionFactory(); 44 | connectionFactory.setHost(options.getHost()); 45 | connectionFactory.setPort(options.getPort()); 46 | if (StringUtils.isNoneBlank(options.getUsername(), options.getPassword())) { 47 | connectionFactory.setUsername(options.getUsername()); 48 | connectionFactory.setPassword(options.getPassword()); 49 | } 50 | // 设置连接超时时间:20秒 51 | connectionFactory.setConnectionTimeout(20000); 52 | } 53 | 54 | /** 55 | * 连接RabbitMQ服务器并创建相关的交换机和队列 56 | * @param options RabbitMQ客户端参数 57 | */ 58 | private void connectAndDeclare(RabbitmqClientOptions options) { 59 | try { 60 | connection = connectionFactory.newConnection(); 61 | channel = connection.createChannel(); 62 | channel.exchangeDeclare(DXL_EXCHANGE, BuiltinExchangeType.DIRECT, true); 63 | channel.queueDeclare(QUEUE, true, false, false, null); 64 | // 将"worker-queue"绑定到交换机"out.exchange",路由键为"out.routing.key" 65 | channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY); 66 | // 同时将"worker-queue"绑定到失败交换机"dlx.exchange",路由键为"dlx.routing.key" 67 | channel.queueBind(QUEUE, DXL_EXCHANGE, DLX_ROUTING_KEY); 68 | channel.basicConsume(QUEUE, true, new DefaultConsumer(channel) { 69 | @Override 70 | public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { 71 | if (random.nextBoolean()) { 72 | LOGGER.info("Consumer received message: {} -- handle ok.", new String(body)); 73 | } else { 74 | LOGGER.info("Consumer received message: {} -- handle failed, will retry in 10 seconds.", new String(body)); 75 | // 将处理失败的消息丢到"buffer-queue" 76 | failedPublish(body); 77 | } 78 | } 79 | }); 80 | LOGGER.info("Consumer connected to broker[{}:{}].", options.getHost(), options.getPort()); 81 | } catch (Exception e) { 82 | LOGGER.error("Consumer connect or declare exchange(queue) with error[{}:{}]: {}", options.getHost(), options.getPort(), e.getLocalizedMessage()); 83 | } 84 | } 85 | 86 | /** 87 | * 将处理失败的消息发布到buffer-queue中 88 | * @param bytes 消息实体 89 | */ 90 | public void failedPublish(byte[] bytes) { 91 | try { 92 | channel.basicPublish(FAILED_EXCHANGE, FAILED_ROUTING_KEY, null, bytes); 93 | } catch (IOException e) { 94 | LOGGER.error("Consumer publish failed-handle message with error: {}", e.getLocalizedMessage()); 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/io/biteenew/rabbitmq/handler/Producer3.java: -------------------------------------------------------------------------------- 1 | package io.biteenew.rabbitmq.handler; 2 | 3 | import com.rabbitmq.client.BuiltinExchangeType; 4 | import com.rabbitmq.client.Channel; 5 | import com.rabbitmq.client.Connection; 6 | import com.rabbitmq.client.ConnectionFactory; 7 | import io.biteenew.rabbitmq.properties.RabbitmqClientOptions; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.io.IOException; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * 消息生产者
18 | * 消息延迟重试模型的生产者:使用方式2:为队列设置TTL 19 | * @author luzhanghong 20 | * @date 2018-07-03 14:10 21 | */ 22 | public class Producer3 { 23 | 24 | private final static Logger LOGGER = LoggerFactory.getLogger(Producer3.class); 25 | private ConnectionFactory connectionFactory; 26 | private Connection connection; 27 | private Channel channel; 28 | private final static String EXCHANGE = "out.exchange3"; 29 | private final static String FAILED_EXCHANGE = "failed.exchange"; 30 | private final static String ROUTING_KEY = "out.routing.key3"; 31 | private final static String FAILED_ROUTING_KEY = "failed.routing.key"; 32 | private final static String DXL_EXCHANGE = "dlx.exchange3"; 33 | private final static String DLX_ROUTING_KEY = "dlx.routing.key3"; 34 | private final static String QUEUE = "buffer-queue3"; 35 | 36 | public Producer3(RabbitmqClientOptions options) { 37 | init(options); 38 | connectAndDeclare(options); 39 | } 40 | 41 | /** 42 | * 初始化连接参数 43 | * @param options RabbitMQ客户端参数 44 | */ 45 | private void init(RabbitmqClientOptions options) { 46 | connectionFactory = new ConnectionFactory(); 47 | connectionFactory.setHost(options.getHost()); 48 | connectionFactory.setPort(options.getPort()); 49 | if (StringUtils.isNoneBlank(options.getUsername(), options.getPassword())) { 50 | connectionFactory.setUsername(options.getUsername()); 51 | connectionFactory.setPassword(options.getPassword()); 52 | } 53 | // 设置连接超时时间:20秒 54 | connectionFactory.setConnectionTimeout(20000); 55 | } 56 | 57 | /** 58 | * 连接RabbitMQ服务器并创建相关的交换机和队列 59 | * @param options RabbitMQ客户端参数 60 | */ 61 | private void connectAndDeclare(RabbitmqClientOptions options) { 62 | try { 63 | connection = connectionFactory.newConnection(); 64 | channel = connection.createChannel(); 65 | // 创建交换机"out.exchange":生产者将消息通过"out.exchange"发送到"buffer-queue"。这里设置交换机类型为"direct",当然也可以使用其他类型 66 | channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true); 67 | // 创建死信交换机"dlx.exchange":"buffer-queue"中产生死信后,会通过此交换机发送出去。这里设置交换机类型为"direct",当然也可以使用其他类型 68 | channel.exchangeDeclare(DXL_EXCHANGE, BuiltinExchangeType.DIRECT, true); 69 | // 创建"failed.exchange" 70 | channel.exchangeDeclare(FAILED_EXCHANGE, BuiltinExchangeType.DIRECT, true); 71 | // 创建缓冲队列"buffer-queue",并为"buffer-queue"设置死信交换机参数:生产者发布的消息会先到达此"buffer-queue",消息在"buffer-queue"中变成死信后,会通过死信交换机和死信路由键发送出去 72 | Map arguments = new HashMap<>(); 73 | arguments.put("x-message-ttl", 10000); // 指定队列中消息最大存活时间("x-message-ttl) 74 | arguments.put("x-dead-letter-exchange", DXL_EXCHANGE); // 指定死信交换机参数(x-dead-letter-exchange) 75 | arguments.put("x-dead-letter-routing-key", DLX_ROUTING_KEY); // 指定死信路由键参数(x-dead-letter-routing-key) 76 | channel.queueDeclare(QUEUE, true, false, false, arguments); 77 | // 将缓冲队列"buffer-queue"绑定到处理失败的交换机"failed.exchange"上,路由键为"failed.routing.key" 78 | channel.queueBind(QUEUE, FAILED_EXCHANGE, FAILED_ROUTING_KEY); 79 | LOGGER.info("Producer connected to broker[{}:{}].", options.getHost(), options.getPort()); 80 | } catch (Exception e) { 81 | LOGGER.error("Producer connect or declare exchange(queue) with error[{}:{}]: {}", options.getHost(), options.getPort(), e.getLocalizedMessage()); 82 | } 83 | } 84 | 85 | /** 86 | * 发布消息 87 | * @param bytes 消息实体 88 | */ 89 | public void publish(byte[] bytes) { 90 | try { 91 | channel.basicPublish(EXCHANGE, ROUTING_KEY, null, bytes); 92 | } catch (IOException e) { 93 | LOGGER.error("Producer publish message with error: {}", e.getLocalizedMessage()); 94 | } 95 | } 96 | 97 | } 98 | --------------------------------------------------------------------------------