前言

选择Apache ActiveMQ 主要是因为,是一个完全使用Java开发的消息中间件。与Java的Java Message Service 服务可以无缝衔接。并且支持多协议,包括:OpenWire、REST、STOMP、WS-Notification、MQTT、XMPP以及AMQP。
当然性能上和集群使用上,无法和kafka、RabbitMQ等近几年比较火的消息中间件相比。但是,够用就好。


文章目录

  • 前言
  • Docker 部署 ActiveMQ
  • 下载镜像
  • 镜像协议端口
  • 启动容器服务
  • Spring Boot
  • 添加依赖
  • application.yml 配置
  • 消息队列发布
  • 消息广播发布
  • 消费队列消息
  • 消费广播消息
  • 问题
  • 增加ActiveMQConfiguration
  • 消息消费


Docker 部署 ActiveMQ

下载镜像

$ sudo docker pull rmohr/activemq

镜像协议端口

在以下端口开放了针对不同协议的

61616 JMS
8161  UI
5672  AMQP  (since `rmohr/activemq:5.12.1`)
61613 STOMP (since `rmohr/activemq:5.12.1`)
1883  MQTT  (since `rmohr/activemq:5.12.1`)
61614 WS    (since `rmohr/activemq:5.12.1`)

启动容器服务

$ sudo docker run -p 61616:61616 -p 8161:8161 rmohr/activemq

仅映射了 61616 和 8161 端口,需要其他协议方式的,可以自行增加映射

Spring Boot

添加依赖

gradle 为例

dependencies {
...
    compile('org.springframework.boot:spring-boot-starter-activemq')
    compile('org.apache.activemq:activemq-pool')
...
}

application.yml 配置

spring:
  activemq:
    brokerUrl: tcp://localhost:61616  //JMS 服务
    pool:
        enabled: true

消息队列发布

message 类型可以是 String 、 Map<String,String>、List<String>、序列化对象

@Component
class JMSProducer {
    @Autowired
     private lateinit var jmsMessage: JmsMessagingTemplate

    fun sendMessage(destination : String , message:String) {
        jmsTemplate.convertAndSend(ActiveMQQueue(destination),message)
    }
}

消息广播发布

message 类型可以是 String 、 Map<String,String>、List<String>、序列化对象

@Component
class JMSProducer {
    @Autowired
     private lateinit var jmsMessage: JmsMessagingTemplate

     fun sendMessage(destination : String , message:String) {
        jmsTemplate.convertAndSend(ActiveMQTopic(destination),message);
    }
}

消费队列消息

@Component
public class JMSConsumer {
    private val logger = LoggerFactory.getLogger(JMSConsumer.java.class);

    @JmsListener(destination = "springboot.queue.test") // 与发布者destination 一致
    fun receiveQueue(msg:String) {
        logger.info("接收到消息:{}",msg);
    }
}

消费广播消息

默认只能发送和接收queue消息,如果要发送和接收topic消息,需要在application.yml文件中加入:

spring:
    jms:
        pub-sub-domain: true

接收代码仍旧是该方式接受

@Component
class JMSConsumer {
    private val logger = LoggerFactory.getLogger(JMSConsumer.java.class);

    @JmsListener(destination = "springboot.queue.test") // 与发布者destination 一致
    fun receiveQueue(msg:String) {
        logger.info("接收到消息:{}",msg);
    }
}

问题

其实从上面就发现了问题,就是要么只能消费队列消息、要么只能消费广播消息,那么如何能够同时在一个程序中同时处理队列消息和广播消息呢?

增加ActiveMQConfiguration

@Configuration
public class ActiveMQConfiguration {
    
    /** 
     * 在Queue模式中,对消息的监听需要对containerFactory进行配置
     */ 
    @Bean("QueueComsumer")
    fun queueJmsListenerContainerFactory(connectionFactory:ConnectionFactory):JmsListenerContainerFactory<?> {
        val factory = SimpleJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setPubSubDomain(false);
        return factory;
    }
    
    /** 
     * 在Topic模式中,对消息的监听需要对containerFactory进行配置
     */ 
    @Bean("TopicComsumer")
     fun topicJmsListenerContainerFactory(connectionFactory:ConnectionFactory):JmsListenerContainerFactory<?> {
        val factory = SimpleJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setPubSubDomain(true);
        return factory;
    }
}

消息消费

@Component
class JMSConsumer {
    private val logger = LoggerFactory.getLogger(JMSConsumer.java.class);

    @JmsListener(destination = "springboot.queue.test",containerFactory = "QueueComsumer") // 与发布者destination 一致
    fun receiveQueue( msg:String) {
        logger.info("接收到消息:{}",msg);
    }
    
   @JmsListener(destination = "springboot.queue.test",containerFactory = "TopicComsumer") // 与发布者destination 一致
   fun receiveTopic(msg:String) {
        logger.info("接收到消息:{}",msg);
    }
}