SpringBoot2.X學習第二十一課(SpringBoot2.X整合ActiveMQ實戰同時支持queue以及topic)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章