• SpringBoot集成RabbitMQ并实现消息确认机制


    RabbitMQ安装请参照RabbitMQ应用

    不啰嗦直接上代码

    目录结构如下:


    pom.xml

    <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.test</groupId>
    	<artifactId>RabbitMQ_MQTT</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<packaging>jar</packaging>
    
    	<name>RabbitMQ_MQTT</name>
    	<url>http://maven.apache.org</url>
    
    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.6.RELEASE</version>
    		<relativePath /> <!-- lookup parent from repository -->
    	</parent>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-amqp</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-devtools</artifactId>
    			<optional>true</optional>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    		<!-- <dependency> <groupId>org.fusesource.mqtt-client</groupId> <artifactId>mqtt-client</artifactId> 
    			<version>1.12</version> </dependency> -->
    	</dependencies>
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    				<configuration>
    					<fork>true</fork>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    
    
    </project>

    application.properties

    servier.port=8080
    
    
    spring.rabbitmq.queues=topic.1,mqtt.test.*,mqtt.test.dd
    spring.rabbitmq.host=127.0.0.1
    spring.rabbitmq.port=5672
    spring.rabbitmq.username=guest
    spring.rabbitmq.password=guest
    spring.rabbitmq.publisher-confirms=true
    spring.rabbitmq.virtual-host=/

    Application.java

    package com.gm;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.gm.rabbit.CallBackSender;
    
    @Configuration
    @RestController  
    @EnableAutoConfiguration  
    @ComponentScan
    @SpringBootApplication
    public class Application {
    
    	@Autowired
    	private CallBackSender sender;
    
    	public static void main(String[] args) {
    		SpringApplication.run(Application.class, args);
    	}
    
    	 @RequestMapping("/callback")
    	public void callbak() {
    		sender.send("topic.baqgl.admin.1", "测试消息");
    	}
    }
    

    RabbitConfig.java

    package com.gm.rabbit;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.amqp.core.AcknowledgeMode;
    import org.springframework.amqp.core.Binding;
    import org.springframework.amqp.core.BindingBuilder;
    import org.springframework.amqp.core.DirectExchange;
    import org.springframework.amqp.core.Message;
    import org.springframework.amqp.core.Queue;
    import org.springframework.amqp.core.TopicExchange;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
    import org.springframework.amqp.rabbit.connection.ConnectionFactory;
    import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    import org.springframework.context.annotation.Scope;
    
    @Configuration
    public class RabbitConfig {
    
    	@Value("${spring.rabbitmq.host}")
    	private String addresses;
    
    	@Value("${spring.rabbitmq.port}")
    	private String port;
    
    	@Value("${spring.rabbitmq.username}")
    	private String username;
    
    	@Value("${spring.rabbitmq.password}")
    	private String password;
    
    	@Value("${spring.rabbitmq.virtual-host}")
    	private String virtualHost;
    
    	@Value("${spring.rabbitmq.publisher-confirms}")
    	private boolean publisherConfirms;
    
    	@Value("${spring.rabbitmq.queues}")
    	private String queues;
    
    	final static String EXCHANGE_NAME = "amq.topic";
    	final static String QUEUE_NAME = "topic.baqgl.*.*";
    	final static String ROUTING_KEY = "topic.baqgl.#";
    
    	@Bean
    	public ConnectionFactory connectionFactory() {
    
    		CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    		connectionFactory.setAddresses(addresses + ":" + port);
    		connectionFactory.setUsername(username);
    		connectionFactory.setPassword(password);
    		connectionFactory.setVirtualHost(virtualHost);
    		/** 如果要进行消息回调,则这里必须要设置为true */
    		connectionFactory.setPublisherConfirms(publisherConfirms);
    		return connectionFactory;
    	}
    
    	@Bean
    	/** 因为要设置回调类,所以应是prototype类型,如果是singleton类型,则回调类为最后一次设置 */
    	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    	public RabbitTemplate rabbitTemplate() {
    		RabbitTemplate template = new RabbitTemplate(connectionFactory());
    		return template;
    	}
    
    	@Bean
    	TopicExchange exchange() {
    		return new TopicExchange(EXCHANGE_NAME);
    	}
    
    	@Bean
    	public Queue queue() {
    		return new Queue(QUEUE_NAME, true);
    	}
    
    	@Bean
    	public Binding binding() {
    		return BindingBuilder.bind(queue()).to(exchange()).with(ROUTING_KEY);
    	}
    	
    
    	@Bean
    	public SimpleMessageListenerContainer messageContainer() {
    		/*Queue[] q = new Queue[queues.split(",").length];
    		for (int i = 0; i < queues.split(",").length; i++) {
    			q[i] = new Queue(queues.split(",")[i]);
    		}*/
    		SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());
    		container.setQueues(queue());
    		container.setExposeListenerChannel(true);
    		container.setMaxConcurrentConsumers(1);
    		container.setConcurrentConsumers(1);
    		container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
    		container.setMessageListener(new ChannelAwareMessageListener() {
    
    			public void onMessage(Message message, com.rabbitmq.client.Channel channel) throws Exception {
    				try {
    					System.out.println(
    							"消费端接收到消息:" + message.getMessageProperties() + ":" + new String(message.getBody()));
    					System.out.println("topic:"+message.getMessageProperties().getReceivedRoutingKey());
    					// deliveryTag是消息传送的次数,我这里是为了让消息队列的第一个消息到达的时候抛出异常,处理异常让消息重新回到队列,然后再次抛出异常,处理异常拒绝让消息重回队列
    					/*if (message.getMessageProperties().getDeliveryTag() == 1
    							|| message.getMessageProperties().getDeliveryTag() == 2) {
    						throw new Exception();
    					}*/
    
    					channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); // false只确认当前一个消息收到,true确认所有consumer获得的消息
    				} catch (Exception e) {
    					e.printStackTrace();
    
    					if (message.getMessageProperties().getRedelivered()) {
    						System.out.println("消息已重复处理失败,拒绝再次接收...");
    						channel.basicReject(message.getMessageProperties().getDeliveryTag(), true); // 拒绝消息
    					} else {
    						System.out.println("消息即将再次返回队列处理...");
    						channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true); // requeue为是否重新回到队列
    					}
    				}
    			}
    		});
    		return container;
    	}
    
    }

    CallBackSender.java

    package com.gm.rabbit;
    
    import java.util.UUID;
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.amqp.rabbit.support.CorrelationData;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CallBackSender implements RabbitTemplate.ConfirmCallback {
    	@Autowired
    	private RabbitTemplate rabbitTemplate;
    
    	public void send(String topic, String message) {
    		rabbitTemplate.setConfirmCallback(this);
    		CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
    		
    		System.out.println("消息id:" + correlationData.getId());
    		//用RabbitMQ发送MQTT需将exchange配置为amq.topic
    		this.rabbitTemplate.convertAndSend("amq.topic", topic, message, correlationData);
    	}
    
    	public void confirm(CorrelationData correlationData, boolean ack, String cause) {
    		System.out.println("消息id:" + correlationData.getId());
    		if (ack) {
    			System.out.println("消息发送确认成功");
    		} else {
    			System.out.println("消息发送确认失败:" + cause);
    		}
    	}
    }

    ApplicationTests.java

    package com.gm;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ApplicationTests {
    
    	@Test
    	public void contextLoads() {
    		System.out.println("hello world");
    	}
    
    }
    

    TopicTest.java

    package com.gm.rabbit;
    
    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;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class TopicTest {
    
    	@Autowired
    	private CallBackSender sender;
    
    	@Test
    	public void topic() throws Exception {
    		sender.send("topic.baqgl.admin.1", "测试消息");
    	}
    }
    本文选择的是RabbitMQ集成MQTT,并实现消息持久化,如不需要集成MQTT只需修改RabbitConfig.java中的EXCHANGE_NAME即可。
    集成MQTT相关配置:
    创建用户:
    创建账号
    rabbitmqctl add_user admin 123456
    设置用户角色
    rabbitmqctl  set_user_tags  admin  administrator
    设置用户权限
    rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"
    设置完成后可以查看当前用户和角色(需要开启服务)
    rabbitmqctl list_users

    安装插件:

    rabbitmq-plugins enable rabbitmq_management  
    rabbitmq-plugins enable rabbitmq_mqtt 

    默认配置。window下,rabbitmq的配置文件在C:UsersAdministratorAppDataRoamingRabbitMQ下。没配置的情况下,采用如下配置:

    [{rabbit,        [{tcp_listeners,    [5672]}]},  
     {rabbitmq_mqtt, [{default_user,     <<"admin">>},  
                      {default_pass,     <<"123456">>},  
                      {allow_anonymous,  true},  
                      {vhost,            <<"/">>},  
                      {exchange,         <<"amq.topic">>},  
                      {subscription_ttl, 1800000},  
                      {prefetch,         10},  
                      {ssl_listeners,    []},  
                      %% Default MQTT with TLS port is 8883  
                      %% {ssl_listeners,    [8883]}  
                      {tcp_listeners,    [1883]},  
                      {tcp_listen_options, [{backlog,   128},  
                                            {nodelay,   true}]}]}  
    ].  

    更多说明请参照官方文档:http://www.rabbitmq.com/mqtt.html


  • 相关阅读:
    defineProperty的使用
    js题库全集
    如何将多个文件夹中的文件合并到一个文件夹中
    CYQ.Data V5 MDataTable 专属篇介绍
    读取和写入配置文件内容的方法
    面对代码中过多的if...else的解决方法
    SQL语句--删除掉重复项只保留一条
    获取当前时间
    Stopwatch 类用于计算程序运行时间
    正则表达式手册
  • 原文地址:https://www.cnblogs.com/gmhappy/p/9472420.html
Copyright © 2020-2023  润新知