本文只是简单的介绍集成和使用,偏重于应用,springboot版本为2.1.0
参看:
https://blog.csdn.net/lemonTree_DXP/article/details/81334812
https://www.jianshu.com/p/d8d73c872665
https://blog.csdn.net/liuchuanhong1/article/details/54603546
- 安装activemq
- 引入pom和配置
- 即时消息,延时消息
安装
activemq的下载地址:http://activemq.apache.org
当前时间(2018-11-30)的mq版本是5.15.8。各位依据自己的系统下载的对应的版本,其实使用都一样。但是注意,最新版本的mq,需要java8支持。
我的是mac,所以可以直接使用brew安装。
下载完毕,直接跑起来(找到bin目录,./activemq start),然后访问:http://127.0.0.1:8161 默认管理员账号密码都是admin。可以访问就说明安装成功了。
正常操作肯定是要修改密码的了:
找到mq安装目录,找到conf文件夹,找到文件:jetty-realm.properties
# Defines users that can access the web (console, demo, etc.)
# username: password [,rolename ...]
admin: admin, admin
user: user, user
详细的配置说明不解释,直接修改下admin和user的账号密码,这里为了测试就不去修改了。
很重要的,要使用延迟消息【比如2小时后才送达消息,默认是即时】,必须修改的配置,找到activemq.xml文件,添加schedulerSupport=true这个属性
activemq的配置就完成了。
项目pom和配置
详细的pom和配置查看下面的代码部分介绍。
怎么使用,消息的区分
首先明确一个问题,为什么要使用mq,才能有怎么使用,不能为了使用而使用。
一些介绍可以看看这篇文章:https://www.cnblogs.com/linjiqin/p/5720865.html
消息分为2种,queue
队列模式和topic
订阅模式(一般订阅,持久订阅)【主要区别就是是否能重复消费
】
关于2个的介绍这篇文章讲的很好:https://www.cnblogs.com/lemon-flm/p/7676047.html
我这里做一个简单的总结
queue
队列:一个消息只能被一个消费者消耗,如果有100个消息,有5个消费者,那么他们均分这100个消息,而不是每个人都获取100个消息,如果当前没有任何消费者,那么消息就会保存到磁盘,直到出现一个消费者。
topic
订阅模式:打个比方,报社印制报纸,有很多人订阅了这个报纸,那么报社在每一期报纸发行时,将同样内容
的报纸发放到订阅者手里。所以每个订阅者收到的消息是一样的
,没有订阅这个报纸的人肯定是收不到报纸的。如果我是今年下半年才订阅的报纸,报社也不可能将上半年的报纸发放给我,所以已经发送过的消息,不会再次发送给新的订阅者的
。
特殊的,如果这个报社没有任何人订阅
,他还坚持生产报纸怎么办?这就像是将报纸丢到水里了,没有任何人收的到,除非出现一个订阅者。报社社长很固执,已经发放的报纸也不会重新给订阅者,即便是丢弃掉。
好了,总结完,大概大家就知道这2个的应用场景了,保证消息必达的就用queue,类似聊天室的就用topic。
下面就用代码来实现以下这个2个消息模式,为了方便创建一个多模块的项目,包含customer和producter,多模块项目的创建我这里不详细的介绍。直接给出关键的pom。
queue,一对一,即时消息
queue,延时消息
queue,一对多,多个消费者
topic
项目结构:
总工程activemq,子模块是customer和producter 步骤:
activemq的pom
:
4.0.0
com.ly
activemq
1.0-SNAPSHOT
pom
producter
customer
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-parent
2.1.1.RELEASE
org.springframework.boot
spring-boot-starter-activemq
org.apache.activemq
activemq-pool
org.messaginghub
pooled-jms
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
producter模块的pom,customer的一样,就不重复了:
4.0.0
com.ly
producter
0.0.1-SNAPSHOT
jar
producter
Demo project for Spring Boot
com.ly
activemq
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
然后子模块加入配置【我修改了application.properties为yaml格式的,个人喜好】:
spring:
activemq:
#账号密码
user: user
password: user
#URL of the ActiveMQ broker.
broker-url: tcp://127.0.0.1:61616
in-memory: false
#必须使用连接池
pool:
#启用连接池
enabled: true
#连接池最大连接数
max-connections: 5
#空闲的连接过期时间,默认为30秒
idle-timeout: 30s
先来完善生产者producter,作为一个消息产生者,提供一个产生消息的接口,调用一次产生一个消息【一个字符串作为消息。】
创建一个测试的controller
@RestController
public class Producter {
private final JmsMessagingTemplate template;
@Autowired
public Producter(JmsMessagingTemplate template) {
this.template = template;
}
@RequestMapping("/queue")
public String queue(){
// 构建一个消息, 名称是 queue01
Destination destination = new ActiveMQQueue("queue01");
String message = "我是消息内容, " + System.currentTimeMillis();
template.convertAndSend(destination, message);
return "success";
}
}
别忘了在application中加一个注解@EnableJms
@EnableJms
@SpringBootApplication
public class ProducterApplication {
public static void main(String[] args) {
SpringApplication.run(ProducterApplication.class, args);
}
}
然后右键main跑起来,调用接口【使用的是postman】
http://127.0.0.1:8080/queue
查看activemq的管理界面,Queues界面多了一条记录,名称是queue01,因为没有启动customer,没有消费者,所以这个消息会一直保存着。
好了,生产者就这样构建好了,超级简单了。
下面是消费者。customer中不需要那么麻烦,创建一个class,然后加上注解,@JmsListener
,然后指定监听的任务名称destination
@EnableJms
@Component
public class Customer {
@JmsListener(destination = "queue01")
public void customer(String msg) {
System.out.println("接收到的消息:");
System.out.println(msg);
}
}
然后跑起来,【为了避免端口冲突,customer改为8081】,服务刚启动,就收到了消息
查看activemq状态,已经被接收了。
下面试试延时消息,服务全部停止。
改造一下producter,为了发送延迟消息,构建一个handler。
@Slf4j
@Component
public class ActiveMQHandler {
private final JmsMessagingTemplate template;
@Autowired
public ActiveMQHandler(JmsMessagingTemplate template) {
this.template = template;
}
/**
* 发送即时消息
* @param destination
* @param data
*/
public void send(String queueName, String data) {
log.info(">>>>>>>立即发送:" + data);
template.convertAndSend(new ActiveMQQueue(queueName), data);
}
/**
* 延时发送的信息
* @param name 监听的名称
* @param data 发送的数据
* @param time 延时多少时间处理消息.
*/
public void delaySend(String name, String data, long time) {
log.info("====>>> 延时任务:" + name + ",data:" + data);
//获取连接工厂
ConnectionFactory connectionFactory = template.getConnectionFactory();
try {
//获取连接
assert connectionFactory != null;
Connection connection = connectionFactory.createConnection();
connection.start();
//获取session, true开启事务,false关闭事务
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// 创建一个消息队列
Destination destination = session.createQueue(name);
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
TextMessage message = session.createTextMessage(data);
//设置延迟时间 //AMQ_SCHEDULED_DELAY
message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, time * 1000L);
//发送
producer.send(message);
session.commit();
producer.close();
session.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
改造producter:
@RestController
public class Producter {
private final ActiveMQHandler handler;
@Autowired
public Producter(ActiveMQHandler handler) {
this.handler = handler;
}
/**
* 即时消息
* @return
*/
@RequestMapping("/queue")
public String queue(){
// 构建一个消息, 名称是 queue01
String message = "我是消息内容, " + System.currentTimeMillis();
handler.send("queue01", message);
return "success";
}
/**
* 延迟消息
* @return
*/
@RequestMapping("/delaySend")
public String delaySend(){
// 构建一个消息, 名称是 queue01
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = "我是延迟消息内容, " + i;
handler.delaySend("delaySend01",message,10);
}
return "success";
}
}
跑起来,然后调用延迟方法:
http://127.0.0.1:8080/delaySend
@Slf4j
@EnableJms
@Component
public class Customer {
@JmsListener(destination = "queue01")
public void customer(String msg) {
System.out.println("接收到的消息:");
System.out.println(msg);
}
@JmsListener(destination = "delaySend01")
public void customer2(String msg) {
log.info("接收延时消息:" + msg);
}
}
跑起来看看效果【因为我们是在后面启动的消费者,而时间已经超过10s了】:
我们再调一次接口【去掉了thread的sleep】,如果你发现没有延迟发送,就看看active的配置是否开启了延迟,重启一下activemq
。
可以看到中间相差了10s时间。
再多加一个消费者试试,生产者也多生产一些消息。
for (int i = 0; i < 30; i++) {
String message = "我是延迟消息内容, " + i;
handler.delaySend("delaySend01", message, 10);
}
生产者跑起来,消费者8081跑起来,然后修改一下端口改为8082,再跑起来。
IDEA如何一个项目跑多个实例:
右上角勾掉,保存,改端口,右键main就可以跑多个了。
然后调用生产者的接口
http://127.0.0.1:8080/delaySend
topic模式
,上面看到,2个消费者并不是接收到全部的消息,这个只能适应部分场景,但是如果你要做一个聊天系统,肯定不能这样子啊。
消费者先来改造,基本不需要动,加一个监听:
@JmsListener(destination = "topic01")
public void customer3(String msg) {
log.info("收到订阅消息:" + msg);
}
然后在配置文件加一个配置:
spring:
jms:
# 如果要使用topic,开启配置
pub-sub-domain: true
生产者的handler增加topic,其实就是讲原来的ActiveMQQueue
换成了ActiveMQTopic
/**
* 发送订阅消息
* @param topicName
* @param data
*/
public void topic(String topicName,String data){
log.info("---->发送订阅消息" + topicName + ",data:" + data);
template.convertAndSend(new ActiveMQTopic("topic01"), data);
}
这样就可以了。写个调用接口
@RequestMapping("/topic")
public String topic(){
handler.topic("topic01","hello world");
return "success";
}
但是这样的配置,queue和topic不能同时存在,再去调用queue的接口,就没有收到消息了。
具体的解决方案,查看下一篇文章:springboot中activemq消息队列同时支持queue和topic消息。
总结
springboot2整合activemq的就到这里了,关于topic的更高级的用法,大家可以网上搜索再去实现了。
以上。