Spring Boot 监听 Activemq 中的特定 topic ,并将数据通过 RabbitMq 发布出去
1、Spring Boot 和 ActiveMQ 、RabbitMQ 简介
最近因为公司的项目需要用到 Spring Boot , 所以自学了一下, 发现它与 Spring 相比,最大的优点就是减少了配置, 看不到 xml 文件的配置, 而是用 appplication.yml 或者 application.propertites 文件来代替 , 再也不用配置 tomcat 环境了, 因为 spring boot 已经将 tomcat 环境整合到里面了。入门可以去 http://spring.io 官网, 上面有一系列介绍 。
本次项目开发中还用到了 ActiveMQ 和 RabbitMQ , 这是两个消息队列,我直到完成模块都不能真正理解消息队列。 关于消息队列的定义和使用场景这篇博客写得十分清楚:
https://blog.csdn.net/KingCat666/article/details/78660535,几个不同的消息队列之间的比较 : https://blog.csdn.net/linsongbin1/article/details/47781187。我负责的任务是 Spring Boot 监听 ActiveMQ 中特定的 topic,并将消息使用 RabbitMq 发布出去。
2、配置环境
2.1 ·使用 maven 构建 Spring Boot 运行环境, 在 pom.xml 文件中加入如下依赖:
<properties>
<project.build.sourceEncoding>UTF8
</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Springboot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<!-- rabbitmq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-stream</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.7.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.0.7.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.0.7.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-maven-plugin -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.2 下载并安装配置 active mq 和 rabbitmq 的运行环境
activemq下载地址如下 : http://activemq.apache.org/download-archives.html
rabbitmq 是使用 erlang 写的, 所以先安装 erlang 环境, 再安装 rabbitmq-server, 现在我将这三个文件整合到了一起, 方便下载 :
链接: https://pan.baidu.com/s/1qdzMpqFwxR78rW7-ABpbCA 提取码: 7aqf 。下载完成以后, 其中比较复杂的是安装 erlang ,安装完以后新建 ERLAGN_HOME 添加到环境变量。 将 %ERLANG_HOME%\bin 添加到 path,然后安装 rabbit-server.exe, 安装完以后在进入 rabbit-server\sbin 目录下, 进入命令行,输入 rabbitmq-plugins enable rabbitmq_management 完成安装,
打开 sbin 目录,双击rabbitmq-server.bat , 启动成功之后访问 http://localhost:15672,默认账号密码都属 guest 。
将下载的 activemq 解压到某个目录下,进入该目录输入 cmd ,敲击 bin\activemq start , 有可能会报错,具体错误查看 data\activemq.log 文件。环境搭建成功以后, 开始干!
3、构建项目
3、1 新建配置文件:
新建 application.yml 文件,输入:
com:
mqtt:
inbound:
url: tcp://127.0.0.1:1883
clientId: familyServerIn
topics: hello,topic
outbound:
urls: tcp://127.0.0.1:1883
clientId: familyServerOut
topic: topic1 spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: root
password: root
virtualHost: /
listener:
concurrency: 2
max-concurrency: 2
main:
web-application-type: none
mqtt:
username: admin
#MQTT-密码
password: admin
#MQTT-服务器连接地址,如果有多个,用逗号隔开,如:tcp://127.0.0.1:61613,tcp://192.168.2.133:61613
url: tcp://127.0.0.1:1883
#MQTT-连接服务器默认客户端ID
client:
id: mqttId
#MQTT-默认的消息推送主题,实际可在调用接口时指定
default:
topic: topic
#连接超时
completionTimeout: 3000
3.2 新建配置类 MQttSenderConfig.java
在这里主要配置了 connectionFactory 和 channelFactory , 值得注意的是在方法 handler() 里面通过监听信道 mqttOutboundChannel 获得了 topic 并将其转发给 RabbitMQ 队列中, topicSender.send(message.getPayload().toString()); 这一行代码将消息发送到 RabbitMQ 队列中 、/*
/**
* 〈一句话功能简述〉<br>
* 〈MQTT发送消息配置〉
*
* @author root
* @create 2018/12/20
* @since 1.0.0
*/
@Configuration
@IntegrationComponentScan
public class MqttSenderConfig { @Value("${spring.mqtt.username}")
private String username; @Value("${spring.mqtt.password}")
private String password; @Value("${spring.mqtt.url}")
private String hostUrl; @Value("${spring.mqtt.client.id}")
private String clientId; @Value("${spring.mqtt.default.topic}")
private String defaultTopic; @Value("${spring.mqtt.completionTimeout}")
private int completionTimeout ; //连接超时 @Autowired
private TopicSender topicSender; @Bean
public MqttConnectOptions getMqttConnectOptions(){
MqttConnectOptions mqttConnectOptions=new MqttConnectOptions();
mqttConnectOptions.setUserName(username);
mqttConnectOptions.setPassword(password.toCharArray());
mqttConnectOptions.setServerURIs(new String[]{hostUrl});
mqttConnectOptions.setKeepAliveInterval(2);
return mqttConnectOptions;
} @Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setConnectionOptions(getMqttConnectOptions());
return factory;
} //mqttOutboundChannel
@Bean
@ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler mqttOutbound() {
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(clientId, mqttClientFactory());
messageHandler.setAsync(true);
messageHandler.setDefaultTopic(defaultTopic);
return messageHandler;
} @Bean
public MessageChannel mqttOutboundChannel() {
return new DirectChannel();
} //接收通道
@Bean
public MessageChannel mqttInputChannel() {
return new DirectChannel();
} //配置client,监听的topic
@Bean
public MessageProducer inbound() {
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter(clientId+"_inbound", mqttClientFactory(),
"topic","hello");
adapter.setCompletionTimeout(completionTimeout);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setQos(1);
adapter.setOutputChannel(mqttOutboundChannel());
return adapter;
} //通过通道获取数据 @Bean
@ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler handler() {
return new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
String topic = message.getHeaders().get("mqtt_receivedTopic").toString();
// String type = topic.substring(topic.lastIndexOf("/")+1, topic.length());
if("hello".equalsIgnoreCase(topic)){
System.out.println("hello,fuckXX," + message.getPayload().toString());
topicSender.send(message.getPayload().toString());
}else if("topic".equalsIgnoreCase(topic)){
System.out.println("topic,fuckXX," + message.getPayload().toString());
topicSender.send(message.getPayload().toString());
}
}
};
} }
3.2 新建配置类 RabbitConfig.java
配置了两个队列 rabbittopic 和 rabbittopic.queue2 , 申明了消息交换器 topicExchange, 通过 key 来绑定, 关于 key 和 路由绑定参考这篇文章 : https://www.jianshu.com/p/04f443dcd8bd 。
@Configuration
public class RabbitConfig implements RabbitListenerConfigurer { //声明队列
@Bean
public Queue queue1() {
return new Queue("rabbitopic", true); // true表示持久化该队列
} @Bean
public Queue queue2() {
return new Queue("rabbitopic.queue2", true);
} //声明交互器
@Bean
TopicExchange topicExchange() {
return new TopicExchange("topicExchange");
} //绑定
@Bean
public Binding binding1() {
return BindingBuilder.bind(queue1()).to(topicExchange()).with("key.1");
} @Bean
public Binding binding2() {
return BindingBuilder.bind(queue2()).to(topicExchange()).with("key.#");
} @Bean
public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(new MappingJackson2MessageConverter());
return factory;
} //queue listener 观察 监听模式 当有消息到达时会通知监听在对应的队列上的监听对象
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
// factory.setPrefetchCount(5);//指定一个请求能处理多少个消息,如果有事务的话,必须大于等于transaction数量.
factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
//MANUAL:将ACK修改为手动确认,避免消息在处理过程中发生异常造成被误认为已经成功消费的假象。
//factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return factory;
} @Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(myHandlerMethodFactory());
}
}
3.3 新建MqttGateway.java
新建 MqttGateWay 接口,设置默认的信道 。
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header; @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface MqttGateway {
void sendToMqtt(String data,@Header(MqttHeaders.TOPIC) String topic);
}
余下代码就不再一一往上贴了 : 具体 demo:https://github.com/blench/mqtt.git
4、遇到的错误及解决办法
1、发送数据后 rabbitmq 一直在接收数据,原因是监听 RabbitMQ 队列消息的方法写错了, 例如:
@RabbitListener(queues = "rabbitopic")
public void processMessage1(String msg) {
// Message message = rabbitTemplate.receive(10000);
System.out.println(" 接收到来自rabbitopic队列的消息:" + msg);
return;
}
接收监听的方法不能有返回值, 只能为 void .
2、配置错误, 中途有一次启动失败,是由于代码的配置问题。
最后启动项目, 在 active mq 中新建 topic 和 hello 主题 , 添加测试内容发送。 控制台下可打印出相应的消息 。
5、总结
虽然这次匆匆忙忙写完了代码,但是对于 RabbitMQ 和 ActiveMQ 只是有了初步的了解, 未来的工作中还会继续学习的 。
参考文档:
https://www.jianshu.com/p/6ca34345b796
https://www.jianshu.com/p/db8391dc1f63
http://blog.sina.com.cn/s/blog_7479f7990100zwkp.html
Spring Boot 监听 Activemq 中的特定 topic ,并将数据通过 RabbitMq 发布出去的更多相关文章
- spring boot 监听容器启动
/** * 在容器启动的时候 加载没问完成的消息重发 * @author zhangyukun * */ @Component @Slf4j public class LoadMessageListe ...
- spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情
<spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...
- 【ActiveMQ】2.spring Boot下使用ActiveMQ
在spring boot下使用ActiveMQ,需要一下几个条件 1.安装并启动了ActiveMQ,参考:http://www.cnblogs.com/sxdcgaq8080/p/7919489.ht ...
- Spring事件监听Demo
Spring事件监听实现了观察者模式.本Demo在junit4测试环境中实现 主要有三个类事件类.监听器类.事件发布类(入口) 事件类必须继承 ApplicationEvent,代码如下: impor ...
- Spring事件监听机制源码解析
Spring事件监听器使用 1.Spring事件监听体系包括三个组件:事件.事件监听器,事件广播器. 事件:定义事件类型和事件源,需要继承ApplicationEvent. package com.y ...
- js 实时监听input中值变化
注意:用到了jquery需要引入jquery.min.js. 需求: 1.每个地方需要分别打分,总分为100; 2.第一个打分总分为40; 3.第二个打分总分为60. 注意:需要判断null.&quo ...
- oracle11g的监听配置文件中的program和env两个配置,必须干掉,客户端才能正常连接
oracle11g的监听配置文件中的program和env两个配置,必须干掉,客户端才能正常连接 oracle11g的监听配置文件中的program和env两个配置,必须干掉,客户端才能正常连接 or ...
- vue 监听对象里的特定数据
vue 监听对象里的特定数据变化 通常是这样写的,只能监听某一个特定数据 watch: { params: function(val) { console.log(val) this.$ajax.g ...
- Fragment-如何监听fragment中的回退事件与怎样保存fragment状态
一.如何监听Fragment中的回退事件 1.问题阐述 在Activity中监听回退事件是件非常容易的事,因为直接重写onBackPressed()函数就好了,但当大家想要监听Fragment中的回退 ...
随机推荐
- 消费者、生产者Java代码示例,wait
箱子中的苹果代表资源,现在有消费者从箱子中拿走苹果,生产者往箱子中放苹果.代码如下: 资源--箱子中的苹果: public class Box { int size; int num; public ...
- Redis数据类型的基本操作
Redis数据类型的基本操作 一.string类型 1.设置value
- Java 从入门到进阶之路(五)
之前的文章我们介绍了 Java 的循环结构,本章我们来看一下 Java 的数组 数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同. 数组 - 相同数据类型的元 ...
- electron-vue-webpack引入bootstrap多实例问题Multiple instances of Vue detected!
在node modules里面找到electron-webpack目录, 修改out->main.js白名单内容,增加 whiteListedModules.add("bootstra ...
- 洛谷P1240-诸侯安置+递推非搜索
诸侯安置 这道题是一题递推题,一开始自己不知道,用了搜索,只过了三个样例: 两两相同的合并, 成 1,1,3,3,5,5........n*2-1; 然后我们会容易发现一种不同与搜索的动态规划做法. ...
- 洛谷P3951 小凯的疑惑 - 数学 /扩展欧几里得
传送门 题意:求出a和b不能通过线性组合(即n*a+m*b)得到的最大值: 思路:摘自洛谷: 不妨设 a<b 假设答案为 x 若 x≡m*a ( mod b )(1≤m≤b−1) (mod3)什 ...
- Codeforces Round #379 (Div. 2) E. Anton and Tree 缩点 树的直径
传送门 题意: 这道题说的是在一颗有两种颜色的树上,每操作一个节点,可以改变这个节点颜色和相邻同色节点的颜色.问最少操作次数,使得树上颜色相同. 思路: 先缩点,把相同的颜色的相邻节点缩在一起.再求出 ...
- CodeForces Round 525
A:Ehab and another construction problem #include<bits/stdc++.h> using namespace std; #define F ...
- Special Judge Ⅱ
Problem Description Q:什么是 Special Judge,Special Judge 的题目有什么不同? A:一个题目可以接受多种正确答案,即有多组解的时候,题目就必须被 Spe ...
- Day005作业
1,有如下变量(tu是个元祖),请实现要求的功能 tu = ("alex", [11, 22, {"k1": 'v1', "k2": [&q ...