SpringBoot2 集成 activeMQ 消息队列

本文只是简单的介绍集成和使用,偏重于应用,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。可以访问就说明安装成功了。
SpringBoot2 集成 activeMQ 消息队列_第1张图片

输入账号密码
SpringBoot2 集成 activeMQ 消息队列_第2张图片
目前没有任何的消息。
SpringBoot2 集成 activeMQ 消息队列_第3张图片

正常操作肯定是要修改密码的了:
找到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

项目结构:
SpringBoot2 集成 activeMQ 消息队列_第4张图片
总工程activemq,子模块是customer和producter 步骤:

  1. 创建一个maven项目,不勾选类别,名称为activemq。
  2. 创建完毕,删除项目src文件夹。
  3. 右击项目,创建modules,然后选择正常的springboot项目构建,创建2个。
  4. 创建完毕,删除.mvn文件夹,mvnw,mvnw.cmd文件。
  5. 配置pom。
  6. 然后在总项目进行一次编译,如果通过,基本上完成搭建。

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
            
        
    

完成之后,编译:
SpringBoot2 集成 activeMQ 消息队列_第5张图片
成功,没毛病。

然后子模块加入配置【我修改了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,没有消费者,所以这个消息会一直保存着。
SpringBoot2 集成 activeMQ 消息队列_第6张图片
好了,生产者就这样构建好了,超级简单了。
下面是消费者。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】,服务刚启动,就收到了消息
SpringBoot2 集成 activeMQ 消息队列_第7张图片
查看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了】:
SpringBoot2 集成 activeMQ 消息队列_第8张图片
我们再调一次接口【去掉了thread的sleep】,如果你发现没有延迟发送,就看看active的配置是否开启了延迟,重启一下activemq
生产者产生消息
消费者消费消息
可以看到中间相差了10s时间。

再多加一个消费者试试,生产者也多生产一些消息。

for (int i = 0; i < 30; i++) {
    String message = "我是延迟消息内容, " + i;
    handler.delaySend("delaySend01", message, 10);
}

生产者跑起来,消费者8081跑起来,然后修改一下端口改为8082,再跑起来。
IDEA如何一个项目跑多个实例:
SpringBoot2 集成 activeMQ 消息队列_第9张图片
SpringBoot2 集成 activeMQ 消息队列_第10张图片
右上角勾掉,保存,改端口,右键main就可以跑多个了。
SpringBoot2 集成 activeMQ 消息队列_第11张图片
然后调用生产者的接口

http://127.0.0.1:8080/delaySend

看到日志打印,8081和8082,都是平均的接收到了消息。
SpringBoot2 集成 activeMQ 消息队列_第12张图片
SpringBoot2 集成 activeMQ 消息队列_第13张图片

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的更高级的用法,大家可以网上搜索再去实现了。

以上。

你可能感兴趣的:(springboot,activemq)