activemq是一個非常常用的消息中間件,本節課進行SpringBoot2.0整合activemq隊列模式的講解,關於activemq基本介紹網上很多,這裏就不進行詳細講解了。
SpringBoot關於activemq的官網:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#boot-features-activemq,這裏有對SpringBoot使用activemq詳細講解,下面直接上代碼
一:隊列模式:queue
1.加入activemq相關依賴
一個依賴是activemq的,另外一個是activemq的線程池,類似於jdbc連接池
<!-- 整合消息隊列ActiveMQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!-- 如果配置線程池則加入 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
</dependency>
2.編寫配置文件
spring:
activemq:
broker-url: tcp://localhost:61616
user: admin
password: admin
#下列配置要增加依賴
pool.enabled: true
pool.max-connections: 100
3.啓動類添加支持
4.創建一個隊列交給Spring管理
在啓動類中創建一個隊列交給Spring管理,後續需要使用的時候直接依賴注入就行
package net.xdclass.base_project;
import org.apache.activemq.command.ActiveMQQueue;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import javax.jms.Queue;
@SpringBootApplication //一個註解頂下面3個
@MapperScan("net.xdclass.base_project.mapper")
@EnableJms//開啓支持jms
public class XdclassApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(XdclassApplication.class, args);
}
@Bean//創建一個bean,交給Spring管理
public Queue queue(){
return new ActiveMQQueue("testqueue");//new一個名字叫testqueue的隊列
}
}
5.消息生產者接口,兩種發送方式
下面我們寫消息生產者,這裏有兩種,先定義一個service:
package net.xdclass.base_project.service;
import javax.jms.Destination;
/**
* 消息生產者
*/
public interface ProducerService {
/**
* 指定消息隊列,發送消息
* @param destination
* @param message
*/
public void sendMessage(Destination destination, final String message);
/**
* 使用已經配置好的消息隊列發送消息
* @param message
*/
public void sendMessage(final String message);
}
實現類:
package net.xdclass.base_project.service.impl;
import net.xdclass.base_project.service.ProducerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import javax.jms.Destination;
import javax.jms.Queue;
@Service
public class ProducerServiceImpl implements ProducerService {
@Autowired
private JmsMessagingTemplate jmsTemplate;
@Autowired
private Queue queue;
//發送消息,destination是發送到的隊列,message是待發送的消息
@Override
public void sendMessage(Destination destination, String message) {
jmsTemplate.convertAndSend(destination,message);
}
//發送消息,destination是發送到的隊列,message是待發送的消息
@Override
public void sendMessage(String message) {
jmsTemplate.convertAndSend(this.queue,message);
}
}
這裏有兩個方法,這兩個方法的主要區別就是一個是使用的配置好的destination,另外一個是對外暴露由外界傳入的,在微服務項目或者分佈式項目中,我們只寫一個服務層,需要發消息給誰可能需要控制器來決定,所以消息的目的地可以由外界傳入,我們也可以在本服務中定義好destination,使用的時候直接依賴注入就行,這裏的queue就是剛剛在啓動類中定義好的destination,直接在業務中用。
JmsMessagingTemplate就是一個模板工具,用來發送消息到broker的對象,他有很多api,如果需要使用其他的話可以去官網查看
6.控制器
這裏寫一個控制器模擬微信支付完成的回調函數,調用我們的接口,然後進行消息的發送,這裏order接口我們創建了一個order.queue的destination
package net.xdclass.base_project.controller;
import javax.jms.Destination;
import net.xdclass.base_project.domain.JsonData;
import net.xdclass.base_project.service.ProducerService;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 功能描述:模擬微信支付回調
*
*/
@RestController
@RequestMapping("/api/v1")
public class OrderController {
@Autowired
private ProducerService producerService;
/**
* 功能描述:微信支付回調接口
* @param msg 支付信息
* @return
*/
@GetMapping("order")
public Object order(String msg){
Destination destination = new ActiveMQQueue("order.queue");
producerService.sendMessage(destination, msg);
return JsonData.buildSuccess();
}
@GetMapping("common")
public Object common(String msg){
producerService.sendMessage(msg);
return JsonData.buildSuccess();
}
}
7.測試是否發送成功
啓動項目,訪問http://localhost:8080/api/v1/order?msg=123
查看activemq控制檯,我們發現已經生成一個order.queue,並且裏面有一條未消費的消息
8.消費者
下面我們寫消費者進行消息的消費:
package net.xdclass.base_project.jms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
@Component
public class OrderConsumer {
@JmsListener(destination = "order.queue")
public void receiveQueue(String text){
System.out.println("orderqueue的報文爲:"+text);
}
}
@JmsListener(destination = "order.queue"),這個就是核心代碼,表示監聽對應名稱的消息隊列,隊列(queue)的消息是實時的,持久化的,就算生產者發送的時候消費者不在線,當消費者上線的時候也會進行消費,下面進行測試,直接重啓項目,運行發現有打印了,說明queue的消息是持久化的:
注意:一般queue在消費者啓動的時候,正常情況下是沒有消息堆積的,也就是沒有待消費的消息 ,隊列的消息是消費一條就消失一條,如果有等待消費的隊列消息說明出問題了
二.發佈訂閱者模式topic
1.在剛剛的配置文件中加入配置文件,支持發佈訂閱模型,因爲默認只支持點對點
server:
port: 8081
spring:
activemq:
broker-url: tcp://192.168.1.11:61616
user: admin
password: admin
#下列配置要增加依賴
pool.enabled: true
pool.max-connections: 100
#需要加入配置文件,支持發佈訂閱模型,默認只支持點對點
jms:
pub-sub-domain: true
2.在啓動類中新增一個topic
與queue一樣,在啓動類中新增一個topic的bean,這樣使用的時候直接注入就行了
@Bean
public Topic topic(){
return new ActiveMQTopic("video.topic");
}
3.新增消息生產者
Service:接着上面隊列的消息生產這下面加入一個方法
package net.xdclass.base_project.service;
import javax.jms.Destination;
/**
* 功能描述:消息生產
*
* <p> 創建時間:May 3, 2018 9:48:30 PM </p>
*
*/
public interface ProducerService {
/**
* 功能描述:指定消息隊列,還有消息
* @param destination
* @param message
*/
public void sendMessage(Destination destination, final String message);
/**
* 功能描述:使用默認消息隊列, 發送消息
* @param message
*/
public void sendMessage( final String message);
/**
* 功能描述:消息發佈者
* @param msg
*/
public void publish(String msg);
}
ServiceImpl:注入剛剛創建的topic,然後調用發送的方法:
package net.xdclass.base_project.service.impl;
import javax.jms.Destination;
import javax.jms.Queue;
import javax.jms.Topic;
import net.xdclass.base_project.service.ProducerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Service;
/**
* 功能描述:消息生產者
*
* <p> 創建時間:May 2, 2018 11:29:47 PM </p>
*
*/
@Service
public class ProducerServiceImpl implements ProducerService{
@Autowired
private Queue queue;
@Autowired
private JmsMessagingTemplate jmsTemplate; //用來發送消息到broker的對象
//發送消息,destination是發送到的隊列,message是待發送的消息
@Override
public void sendMessage(Destination destination, String message) {
jmsTemplate.convertAndSend(destination, message);
}
//發送消息,destination是發送到的隊列,message是待發送的消息
@Override
public void sendMessage(final String message) {
jmsTemplate.convertAndSend( message);
}
//=======發佈訂閱相關代碼=========
@Autowired
private Topic topic;
@Override
public void publish(String msg) {
this.jmsTemplate.convertAndSend(this.topic, msg);
}
}
4.Controller調用生產者
下面在剛剛queue的控制器中寫一個調用消息發佈的接口:
/**
* 發佈消息:生產者
* @param msg
* @return
*/
@GetMapping("common")
public Object common(String msg){
producerService.sendMessage(msg);
return JsonData.buildSuccess();
}
5.新建訂閱者
新建一個訂閱者,訂閱剛剛定義的發佈者
package net.xdclass.base_project.jms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
@Component
public class TopicSub {
@JmsListener(destination="video.topic", containerFactory="jmsListenerContainerTopic")
public void receive1(String text){
System.out.println("video.topic 消費者:receive1="+text);
}
@JmsListener(destination="video.topic", containerFactory="jmsListenerContainerTopic")
public void receive2(String text){
System.out.println("video.topic 消費者:receive2="+text);
}
@JmsListener(destination="video.topic", containerFactory="jmsListenerContainerTopic")
public void receive3(String text){
System.out.println("video.topic 消費者:receive3="+text);
}
}
6.測試
發現已經訪問成功,並且成功訂閱,發佈訂閱模式消息不會被消費掉,他會一直在mq上。
7.解決queue與topic兼顧問題
這時候我們再返回前面發送一個隊列的消息:
我們發現這時候隊列的消息成功發送了,但是並沒有被消費,可是之前就是可以的啊,現在爲什麼不行了呢,這是因爲springboot不同時支持queue與topic,只支持點對點也就是queue,剛剛我們在配置文件中開啓了發佈訂閱者模式,則無法發送和接收queue消息,我們可以通過配置讓他同時支持兩種模式,在啓動類中加入如下代碼修改mq的工廠,讓他支持兩種模式:
/**
* 同時支持topic與queue
* @param activeMQConnectionFactory
* @return
*/
@Bean
public JmsListenerContainerFactory<?> jmsListenerContainerTopic(ConnectionFactory activeMQConnectionFactory) {
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
bean.setPubSubDomain(true);
bean.setConnectionFactory(activeMQConnectionFactory);
return bean;
}
在訂閱者中加入如下屬性
containerFactory = "jmsListenerContainerTopic"
@JmsListener(destination="video.topic",containerFactory = "jmsListenerContainerTopic")
public void receive1(String text){
System.out.println("video.topic 消費者:receive1="+text);
}
注意,這時候要把之前配置文件中加的註釋掉
#需要加入配置文件,支持發佈訂閱模型,默認只支持點對點
# jms:
# pub-sub-domain: true
這樣,就可以同時使用queue以及topic了
源碼地址:https://gitee.com/xuxinsunqizheng/springboot_module.git