rabbit_mq
gitlab地址:http://47.93.254.162:8090/open/mq.git
2019-04-11: 目前更到简单队列使用,预计明晚更新完毕。
一、安装
- erlang环境安装
整理好的安装包,链接:https://pan.baidu.com/s/1BtuwQlAnkbQIjGtz5aVsww 提取码:8vct
安装gcc:yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel unixODBC-devel
解压erlang安装包后执行以下命名
./configure --prefix=/usr/local/erlang --enable-smp-support --enable-threads --enable-sctp --enable-kernel-poll --enable-hipe --with-ssl --without-javac
–prefix 指定安装目录
–enable-smp-support启用对称多处理支持(Symmetric Multi-Processing对称多处理结构的简称)
–enable-threads启用异步线程支持
–enable-sctp启用流控制协议支持(Stream Control Transmission Protocol,流控制传输协议)
–enable-kernel-poll启用Linux内核poll
–enable-hipe启用高性能Erlang –with-ssl 启用ssl包 –without-javac
不用java编译
开始安装编译:
make && make install
export PATH=$PATH:/usr/local/erlang/bin (将erlang写入环境变量中)
export PATH=$PATH:/home/pro_install/rabbitmq/rabbitmq/rabbitmq/sbin (将rabbit_mq写入环境变量中)
source /etc/profile (刷新配置文件)
rabbitmq-server -detached //启动rabbitmq,-detached代表后台守护进程方式启动。
关闭:rabbitmqctl stop
rabbitmqctl status 查看启动状态
systemctl stop firewalld 关闭防火墙
rabbitmq:浏览器访问的插件安装
./rabbitmq-plugins enable rabbitmq_management
./rabbitmq-plugins: 第 27 行:exec: erl: 未找到 (出现这种错误 重新刷新配置文件即可:source /etc/profile)

访问:Ip+15672

如果使用的不是localhost访问使用默认账号和密码guest会报禁止登陆错误。

解决方法:找到/rabbitmq_server-3.6.14/ebin下面的rabbit.app 将以下内容清空


启动成功

以下是其他相关配置
一般情况下,RabbitMQ的默认配置就足够了。如果希望特殊设置的话,有两个途径:
一个是环境变量的配置文件 rabbitmq-env.conf ;
一个是配置信息的配置文件 rabbitmq.config;
注意,这两个文件默认是没有的,如果需要必须自己创建。
rabbitmq-env.conf
这个文件的位置是确定和不能改变的,位于:/etc/rabbitmq目录下(这个目录需要自己创建)。
文件的内容包括了RabbitMQ的一些环境变量,常用的有:
#RABBITMQ_NODE_PORT= //端口号
#HOSTNAME=
RABBITMQ_NODENAME=mq
RABBITMQ_CONFIG_FILE= //配置文件的路径
RABBITMQ_MNESIA_BASE=/rabbitmq/data //需要使用的MNESIA数据库的路径
RABBITMQ_LOG_BASE=/rabbitmq/log //log的路径
RABBITMQ_PLUGINS_DIR=/rabbitmq/plugins //插件的路径
具体的列表见:http://www.rabbitmq.com/configure.html#define-environment-variables
rabbitmq.config
这是一个标准的erlang配置文件。它必须符合erlang配置文件的标准。
它既有默认的目录,也可以在rabbitmq-env.conf文件中配置。
文件的内容详见:http://www.rabbitmq.com/configure.html#config-items
二、rabbit简介
- erlang:Erlang编程语言最初目的是进行大型电信交换设备的软件开发,是一种适用于大规模并行处理环境的高可靠性编程语言。随着多核处理器技术的日渐普及,以及互联网、云计算等技术的发展,该语言的应用范围也有逐渐扩大之势。
- Virtual Hosts:在RabbitMQ中可以虚拟消息服务器Virtual host每个Virtual host相当于独立的RabbitMQ服务器,相互之间是互相隔离,exchange、queue、message不能互通。
- 分类:点对点的简单队列、公平队列模式、发布订阅模式(fanout)、路由模式routing、通配符模式topics。
- 协议(AMQP):高级消息队列协议是异步消息传递所使用的应用层协议规范,为面向消息中间件设计,基于此协议的客户端与消息中间件可无视消息来源,不受客户端,开发语言、消息中间件的限制。
- Server(Broker):实现AMQP协议的消息队列和路由功能的进程,用于接收客户端连接。
- 一个Virtual Host可以拥有多个Exchange和Queue。
- Exchange:交换机,用于接收生产者发送的消息,更具Routing Key将消息路由到相关服务器的Queue队列中。
- Exchange Type:交换机类型,决定着路由消息行为,包含三种分类,fanout、direct、topic。
- Message Queue:消息队列,用于存储未被消费者消费的消息。
- Message:由Header和Body组成,其中Header是由生产者添加的各种属性集合(包括Message是否被持久化,优先级,有具体的Message Queue接收等),Body发送的数据内容。
- Binding Key:绑定关键字,将特定的Exchange和特定的Queue绑定起来。
三、交换机
生产者发送消息先将消息投递到交换中,由交换机转发到具体的队列,队列在将消息以推送或者拉取方式给消费者进行消费,其中交换机的作用是根据具体的路由策略分发到不同的队列中。
- 直连交换机(Direct exchange):根据消息携带的路由键(routing key)将消息投递给对应的队列。
- 扇形交换机(Fanout exchange):将消息路由绑定到该交换机上所有的队列。
- 主题交换机(Topic exchange):队列通过路由键绑定到交换机上,交换机根据消息中的路由值将消息路由给一个或多个绑定到相应队列上。
- 头交换机(Headers exchange):类似主题交换机,不过头交换机使用多个消息属性来代替路由建立路由规则。
Fanout exchange

- 拥有一个生产者,多个消费者。
- 每一个消费者都有一个自己的队列。
- 生产者没有直接发送消息到队列中,是发送到交换机。
- 每个消费者的队列都要绑定到交换机上。
- 消息通过交换机到达消费者队列。
- 交换机没有存储消息功能,如果消息发送到没有绑定的消费队列的交换机,则消息将丢失。
路由模式(Routing Key):生产者发送消息到交换机上并指定一个路由key,消费者队列绑定到交换机时要制定路由key(key只能匹配接受相同的key)。

通配符模式(Topics):建立在路由key模式基础上,使用通配符来管理消费者接受消息,type=topic,交换机根据绑定队列的routing key的值进行通配符匹配。

四、代码实现
- 简单队列(不通过交换机)
pom文件
1 <dependencies> 2 <dependency> 3 <groupId>com.rabbitmq</groupId> 4 <artifactId>amqp-client</artifactId> 5 <version>3.6.5</version> 6 </dependency> 7 </dependencies>
连接的工具类
1 public class ConnectionUtils { 2 3 private static final String host = "xx.xx.xx.xx"; 4 private static final Integer port = 5672; 5 private static final String virtualHost = "/test_001"; 6 private static final String name = "test001"; 7 private static final String pw = "123456"; 8 9 public static Connection getConnection() throws IOException, TimeoutException { 10 11 ConnectionFactory factory = new ConnectionFactory(); 12 13 factory.setHost(host); 14 factory.setPort(port); 15 factory.setVirtualHost(virtualHost); 16 factory.setUsername(name); 17 factory.setPassword(pw); 18 19 Connection connection = factory.newConnection(); 20 21 return connection; 22 } 23 24 }
生成者
1 public class Produce { 2 3 private static final String queueName = "simple_name_queue"; 4 5 public static void main(String[] args) throws IOException, TimeoutException { 6 // 创建连接 7 Connection connection = ConnectionUtils.getConnection(); 8 // 建立通道 9 Channel channel = connection.createChannel(); 10 // 创建队列 11 channel.queueDeclare(queueName, false, false, false, null); 12 String msg = "this is first simple message..."; 13 14 // 发送 15 channel.basicPublish("", queueName, null, msg.getBytes()); 16 channel.close(); 17 connection.close(); 18 } 19 20 }
消费者
1 public class Consumer { 2 3 private static final String queueName = "simple_name_queue"; 4 5 public static void main(String[] args) throws Exception { 6 7 // 创建连接 8 Connection connection = ConnectionUtils.getConnection(); 9 // 建立通道 10 Channel channel = connection.createChannel(); 11 // 创建队列 12 channel.queueDeclare(queueName, false, false, false, null); 13 14 DefaultConsumer consumer = new DefaultConsumer(channel) { 15 16 @Override 17 public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { 18 String msgString = new String(body, "UTF-8"); 19 System.out.println("消费者获取消息:" + msgString); 20 21 } 22 23 }; 24 25 // 监听队列 26 channel.basicConsume(queueName, true, consumer); 27 } 28 29 }
测试(先启动消费者)

2.工作队列模式(使用一个生产者,多个消费者同时消费,在这种模式下保证消费者不能重复消费)
生成者
1 public class Produce { 2 3 private static final String QUEUE_NAME = "worker_queue"; 4 5 public static void main(String[] args) throws IOException, TimeoutException { 6 7 Connection connection = ConnectionUtils.getConnection(); 8 9 Channel channel = connection.createChannel(); 10 // 如果有多个消费者的情况下保证一条消息只发送给同一个消费者 11 channel.basicQos(1); 12 for(int i = 1; i <= 50; i++) { 13 String msg = "worker produce send message " + i; 14 channel.basicPublish("", QUEUE_NAME, null, msg.getBytes()); 15 } 16 17 channel.close(); 18 connection.close(); 19 } 20 21 }
消费者
1 public class Consumer { 2 3 private static final String QUEUE_NAME = "worker_queue"; 4 5 public static void main(String[] args) throws IOException, TimeoutException { 6 7 Connection connection = ConnectionUtils.getConnection(); 8 9 Channel channel = connection.createChannel(); 10 // 如果有多个消费者的情况下保证一条消息只发送给同一个消费者 11 channel.basicQos(1); 12 // 创建队列 13 channel.queueDeclare(QUEUE_NAME, false, false, false, null); 14 DefaultConsumer consumer = new DefaultConsumer(channel) { 15 16 @Override 17 public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { 18 String msgString = new String(body, "UTF-8"); 19 System.out.println("消费者获取消息:" + msgString); 20 try { 21 Thread.sleep(1000); 22 } catch (Exception e) { 23 } finally { 24 channel.basicAck(envelope.getDeliveryTag(), false); 25 } 26 } 27 }; 28 29 channel.basicConsume(QUEUE_NAME, false, consumer); 30 } 31 }
测试

第一个消费者

第二个消费者

3. 扇形交换机使用 注意:需要先在管理端创建交换机(页面上操作,比较简单,暂不演示)。
生成者
1 public class FanoutProduce { 2 3 private static final String EXCHANGE_NAME = "fanout_queue_test"; 4 5 public static void main(String[] args) throws IOException, TimeoutException { 6 7 Connection connection = ConnectionUtils.getConnection(); 8 9 Channel channel = connection.createChannel(); 10 // 绑定交换机 11 channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); 12 String msg = "fanout exchange produce seng message ..."; 13 // 注意如果消费者没有绑定到交换机和队列,则消息将会丢失 14 channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes()); 15 16 channel.close(); 17 connection.close(); 18 } 19 20 }
消费者
1 public class FanoutConsumer { 2 3 private static final String QUEUE_NAME = "fanout_queue"; 4 5 private static final String EXCHANGE_NAME = "fanout_queue_test"; 6 7 public static void main(String[] args) throws IOException, TimeoutException { 8 9 Connection connection = ConnectionUtils.getConnection(); 10 11 Channel channel = connection.createChannel(); 12 channel.queueDeclare(QUEUE_NAME, false, false, false, null); 13 // 关联到交换机,最后一个参数为路由键使用 14 channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ""); 15 DefaultConsumer consumer = new DefaultConsumer(channel) { 16 @Override 17 public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { 18 String msgString = new String(body, "UTF-8"); 19 System.out.println("消费者获取消息:" + msgString); 20 } 21 }; 22 23 // 监听队列 24 channel.basicConsume(QUEUE_NAME, true, consumer); 25 } 26 }
测试

4. 直连交换机使用(fanout, 使用路由键的情况) [这里用info和error两个路由键来接受]
生产者
1 public class DirectRoutingKeyProduce { 2 3 private static final String EXCHANGE_NAME = "direct_routing_key_test"; 4 private static final String ROUTING_KEY = "info"; 5 6 public static void main(String[] args) throws IOException, TimeoutException { 7 8 Connection connection = ConnectionUtils.getConnection(); 9 10 Channel channel = connection.createChannel(); 11 // 绑定交换机 12 channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 13 String msg = "direct routing key is info ..."; 14 channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, null, msg.getBytes()); 15 16 channel.close(); 17 connection.close(); 18 } 19 20 }
info路由键消费者
1 public class DirectRoutingKeyInfoConsumer { 2 3 private static final String EXCHANGE_NAME = "direct_routing_key_test"; 4 private static final String ROUTING_KEY = "info"; 5 private static final String QUEUE_NAME = "routing_key_info_queue"; 6 7 public static void main(String[] args) throws IOException, TimeoutException { 8 9 Connection connection = ConnectionUtils.getConnection(); 10 11 Channel channel = connection.createChannel(); 12 channel.queueDeclare(QUEUE_NAME, false, false, false, null); 13 // 关联到交换机,最后一个参数为路由键使用 14 channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY); 15 // 接受多个使用情况 16 // channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY1); 17 18 DefaultConsumer consumer = new DefaultConsumer(channel) { 19 @Override 20 public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { 21 String msgString = new String(body, "UTF-8"); 22 System.out.println("消费者获取消息:" + msgString); 23 } 24 }; 25 26 // 监听队列 27 channel.basicConsume(QUEUE_NAME, true, consumer); 28 } 29 }
error路由键消费者
1 public class DirectRoutingKeyErrorConsumer { 2 3 private static final String EXCHANGE_NAME = "direct_routing_key_test"; 4 private static final String ROUTING_KEY = "error"; 5 private static final String QUEUE_NAME = "routing_key_info_queue"; 6 7 public static void main(String[] args) throws IOException, TimeoutException { 8 9 Connection connection = ConnectionUtils.getConnection(); 10 11 Channel channel = connection.createChannel(); 12 channel.queueDeclare(QUEUE_NAME, false, false, false, null); 13 // 关联到交换机,最后一个参数为路由键使用 14 channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY); 15 // 接受多个使用情况 16 // channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY1); 17 18 DefaultConsumer consumer = new DefaultConsumer(channel) { 19 @Override 20 public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { 21 String msgString = new String(body, "UTF-8"); 22 System.out.println("消费者获取消息:" + msgString); 23 } 24 }; 25 26 // 监听队列 27 channel.basicConsume(QUEUE_NAME, true, consumer); 28 } 29 30 }
测试(启动info和error的消费者,由于生产者只投递了info,此时只有info的消费者才能接受)



5. topic(路由键需要用 . 来区分,消费者可以用 * 和 # 来模糊匹配,* 模糊匹配后面一位[类似MySQL了的like查询中 '_'] ,# 模糊匹配后面所有 )
生产者
1 public class TopicProduce { 2 3 private static final String EXCHANGE_NAME = "topic_routing_key_test"; 4 private static final String ROUTING_KEY = "log.info.error"; 5 6 public static void main(String[] args) throws IOException, TimeoutException { 7 8 Connection connection = ConnectionUtils.getConnection(); 9 10 Channel channel = connection.createChannel(); 11 // 绑定交换机 12 channel.exchangeDeclare(EXCHANGE_NAME, "topic"); 13 String msg = "topic routing key is log.info.error ..."; 14 channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, null, msg.getBytes()); 15 16 channel.close(); 17 connection.close(); 18 } 19 20 }
#: 消费者
1 public class TopicRoutingKeyAllConsumer { 2 3 private static final String EXCHANGE_NAME = "topic_routing_key_test"; 4 private static final String ROUTING_KEY = "log.#"; 5 private static final String QUEUE_NAME = "topic_routing_key_all_queue"; 6 7 public static void main(String[] args) throws IOException, TimeoutException { 8 9 Connection connection = ConnectionUtils.getConnection(); 10 11 Channel channel = connection.createChannel(); 12 // 绑定交换机 13 channel.exchangeDeclare(EXCHANGE_NAME, "topic"); 14 channel.queueDeclare(QUEUE_NAME, false, false, false, null); 15 // 关联到交换机,最后一个参数为路由键使用 16 channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY); 17 18 DefaultConsumer consumer = new DefaultConsumer(channel) { 19 @Override 20 public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { 21 String msgString = new String(body, "UTF-8"); 22 System.out.println("消费者获取消息:" + msgString); 23 } 24 }; 25 26 // 监听队列 27 channel.basicConsume(QUEUE_NAME, true, consumer); 28 } 29 }
* 消费者
1 public class TopicRoutingKeyInfoConsumer { 2 3 private static final String EXCHANGE_NAME = "topic_routing_key_test"; 4 private static final String ROUTING_KEY = "log.*"; 5 private static final String QUEUE_NAME = "topic_routing_key_info_queue"; 6 7 public static void main(String[] args) throws IOException, TimeoutException { 8 9 Connection connection = ConnectionUtils.getConnection(); 10 11 Channel channel = connection.createChannel(); 12 // 绑定交换机 13 channel.exchangeDeclare(EXCHANGE_NAME, "topic"); 14 channel.queueDeclare(QUEUE_NAME, false, false, false, null); 15 // 关联到交换机,最后一个参数为路由键使用 16 channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY); 17 18 DefaultConsumer consumer = new DefaultConsumer(channel) { 19 @Override 20 public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { 21 String msgString = new String(body, "UTF-8"); 22 System.out.println("消费者获取消息:" + msgString); 23 } 24 }; 25 26 // 监听队列 27 channel.basicConsume(QUEUE_NAME, true, consumer); 28 } 29 }
此时将 * 的消费者的路由键改成 log.*.* 则会出来
测试



五、事物控制
采用AMQP事物机制和Confirm模式
- 其中txSelect 将当前的channel设置成transaction事物模式。
- 其中txCommit 提交事务。
- 其中txRollback 回滚事务。
- 生产者实现
1 public class TransactionProduce { 2 3 private static final String queueName = "simple_name_queue"; 4 5 public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { 6 // 创建连接 7 Connection connection = ConnectionUtils.getConnection(); 8 // 建立通道 9 Channel channel = connection.createChannel(); 10 // 创建队列 11 channel.queueDeclare(queueName, false, false, false, null); 12 String msg = "this is transaction simple message..."; 13 // 开启事物 14 channel.txSelect(); 15 // 发送 16 try { 17 channel.basicPublish("", queueName, null, msg.getBytes()); 18 //提交事务 19 //int i = 1 / 0; 20 channel.txCommit(); 21 22 /*if(!channel.waitForConfirms()) { 23 System.out.println("生产者消息发送失败..."); 24 } else { 25 System.out.println("生产者消息发送成功..."); 26 }*/ 27 } catch (Exception e) { 28 e.printStackTrace(); 29 // 回滚事务 30 channel.txRollback(); 31 } finally { 32 channel.close(); 33 connection.close(); 34 } 35 } 36 }
2. 消费者实现
1 public class TransactionComsumer { 2 3 private static final String queueName = "simple_name_queue"; 4 5 public static void main(String[] args) throws Exception { 6 7 // 创建连接 8 Connection connection = ConnectionUtils.getConnection(); 9 // 建立通道 10 Channel channel = connection.createChannel(); 11 // 创建队列 12 channel.queueDeclare(queueName, false, false, false, null); 13 14 DefaultConsumer consumer = new DefaultConsumer(channel) { 15 16 @Override 17 public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { 18 String msgString = new String(body, "UTF-8"); 19 System.out.println("消费者获取消息:" + msgString); 20 21 } 22 23 }; 24 25 // 监听队列 26 channel.basicConsume(queueName, true, consumer); 27 } 28 29 }
六、与springboot整合
这里给出核心方法,具体请到上面gitLab地址下载,下载时请加上8090端口,否则下载不了。
生产者(配置类)
1 @Component 2 public class FanoutConfig { 3 4 // info 5 private String FANOUT_INFO_QUEUE = "fanout_info_queue"; 6 7 // error 8 private String FANOUT_ERROR_QUEUE = "fanout_error_queue"; 9 // fanout交换机 10 private String EXCHANGE_NAME = "fanout_queue_springboot_test"; 11 12 @Bean 13 public Queue fanOutInfoQueue() { 14 return new Queue(FANOUT_INFO_QUEUE); 15 } 16 17 @Bean 18 public Queue fanOutErrorQueue() { 19 return new Queue(FANOUT_ERROR_QUEUE); 20 } 21 22 // 3.fanout类型定义交换机 23 @Bean 24 FanoutExchange fanoutExchange() { 25 return new FanoutExchange(EXCHANGE_NAME); 26 } 27 28 @Bean 29 Binding bindingExchangeInfo(Queue fanOutInfoQueue, FanoutExchange fanoutExchange) { 30 return BindingBuilder.bind(fanOutInfoQueue).to(fanoutExchange); 31 } 32 33 @Bean 34 Binding bindingExchangeError(Queue fanOutErrorQueue, FanoutExchange fanoutExchange) { 35 return BindingBuilder.bind(fanOutErrorQueue).to(fanoutExchange); 36 } 37 }
生产者(消费发送类)
1 @Component 2 public class FanoutProducer { 3 @Autowired 4 private AmqpTemplate amqpTemplate; 5 6 public void send(String queueName) { 7 8 SimpleDateFormat formatter = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss"); 9 String formatStr =formatter.format(new Date()); 10 11 String msg = "my_fanout_msg:" + formatStr; 12 13 amqpTemplate.convertAndSend(queueName, msg); 14 15 } 16 }
resource
1 @RestController 2 public class ProduceController { 3 4 @Autowired 5 private FanoutProducer fanoutProducer; 6 7 @RequestMapping("/fanout") 8 public String sendFanout(String queueName) { 9 10 fanoutProducer.send(queueName); 11 12 return "success..."; 13 } 14 15 16 }
配置文件(消费者和生成者只是端口号不同)
1 spring: 2 rabbitmq: 3 host: 47.93.254.162 4 port: 5672 5 username: test001 6 password: 123456 7 virtual-host: /test_001 8 server: 9 port: 8080
消费者
1 @Component 2 @RabbitListener(queues = "fanout_error_queue") 3 public class FanoutErrorConsumer { 4 5 @RabbitHandler 6 public void process(String msg) { 7 8 System.out.println("error consumer accept : " + msg); 9 } 10 11 }
1 @Component 2 @RabbitListener(queues = "fanout_info_queue") 3 public class FanoutInfoConsumer { 4 5 @RabbitHandler 6 public void process(String msg) { 7 8 System.out.println("info consumer accept : " + msg); 9 } 10 11 12 }
测试



注意这里只给处理扇形交换机,topic的话在下面截图中加上路由键即可(这个比较简单)。

基于手动应答模式
application.yml
1 spring: 2 rabbitmq: 3 #url 4 host: 47.93.254.162 5 ####端口号 6 port: 5672 7 #账号 8 username: test001 9 #密码 10 password: 123456 11 #地址 12 virtual-host: /test_001 13 listener: 14 simple: 15 retry: 16 #重试 17 enabled: true 18 #最大重试次数 19 max-attempts: 3 20 #重试间隔次数 21 initial-interval: 3000 22 #开启手动确认 23 acknowledge-mode: manual 24 server: 25 port: 8083
消费者
1 @Component 2 public class FanoutInfoConsumer { 3 4 // RabbitListener:底层采用AOP进行拦截,一旦程序抛出异常,将会自动实现补偿机制,如果手动try catch则不会。 5 @RabbitListener(queues = "fanout_info_queue") 6 public void process(Message message, @Headers Map<String, Object> headers, Channel channel) throws Exception { 7 8 String receiveMessage = new String(message.getBody(), "UTF-8"); 9 String messageId = message.getMessageProperties().getMessageId(); 10 11 System.out.println("================"); 12 System.out.println(receiveMessage); 13 System.out.println(messageId); 14 15 //int i = 1 / 0; // 这里模拟异常情况下,实现重试机制 16 17 // 以下代码让mq帮助业务代码,来实现重试机制的调用 18 String retryUrl = "http://127.0.0.1:8084/retry"; 19 JSONObject jsonObject = HttpClientUtils.httpGet(retryUrl); 20 if(jsonObject == null) { 21 throw new Exception("重试接口调用失败...,请求重试"); 22 } 23 System.out.println("请求完成..."); 24 System.out.println(jsonObject.get("code")); 25 System.out.println(jsonObject.get("msg")); 26 27 // 手动应答模式 28 Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG); 29 channel.basicAck(deliveryTag, false); 30 } 31 32 }

若开启了手动应答模式,消费者没有回复,则不会删除该消息。
rabbit_mq分布式事物实现这里放在单独的章节里面(保函死信队列)。

浙公网安备 33010602011771号