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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值