【Spring】九,Spring Boot整合ActiveMQ

JMS

JMS是一个Java标准,定义了使用消息代理(message broker)的通用API,最早于2001年提出。长期以来,JMS一直是实现异步消息的首选方案。在JMS出现之前,每个消息代理都有私有的API,这就使得不同代理之间的消息代码很难通用。但是借助JMS,所有遵从规范的实现都使用通用的接口,这就好像JDBC为数据库操作提供了通用的接口一样。

Spring通过基于模板的抽象为JMS功能提供了支持,这个模板就是JmsTemplate。借助JmsTemplate,我们能够非常容易地在消息生产方发送队列和主题消息,在消费消息的那一方,也能够非常容易地接收这些消息。

Spring还提供了消息驱动POJO的理念:这是一个简单的Java对象,它能够以异步的方式响应队列或主题上到达的消息。

搭建JMS环境

安装ActiveMQ

可通过docker快速安装一个ActiveMQ的实验环境,具体过程请查阅资料。

添加依赖到项目中

初始化一个Spring Boot Web项目,并添加ActiveMQ的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

属性配置

spring:
  activemq:
    # 代理的URL
    broker-url: tcp://localhost:61616
    # 用来访问代理的用户
    user: admin
    # 用来访问代理的密码
    password: admin

配置消息转换器

消息转换器用于在消息发送之前,将某个pojo对象转为Message。在底层,这是通过MessageConverter的实现类来完成的,它替我们做了将对象转换成Message的脏活累活。
MessageConverter是Spring定义的接口,只有两个需要实现的方法:

public interface MessageConverter {
  Message toMessage(Object object, Session session)
                   throws JMSException, MessageConversionException;
  Object fromMessage(Message message)
}

这个接口实现起来很简单,但我们通常并没有必要创建自定义的实现。Spring已经提供了多个实现:

  • MappingJackson2MessageConverter:使用Jackson 2 JSON 库实现消息与JSON格式之间的互相转换。
  • MarshallingMessageConverter:使用JAXB库实现消息与XML格式之间的相互转换。
  • MessagingMessageConverter:使用底层的MessageConverter实现消息抽象的Message载荷与javax.jms.Message之间的转换,同时会使用JmsHeaderMapper实现JMS头信息与标准消息头信息之间的转换。
  • SimpleMessageConverter:实现String与TextMessage之间的互相转换、字节数组与ByteMessage之间的相互转换,Map与MapMessage之间的相互转换以及Serializable对象与ObjectMessage之间的相互转换。

默认情况下,将会使用SimpleMessageConverter,但是它需要被发送的对象实现Serializable。这种办法可能也不错,但有时候我们可能想要使用其他的消息转换器来消除这种限制,比如MappingJackson2MessageConverter

@Configuration
public class ActiveMQConfig {

    @Bean
    public MessageConverter messageConverter(){
        MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter();
        messageConverter.setTypeIdPropertyName("_typeId");

        Map<String, Class<?>> typeIdMappings = new HashMap<>();
        typeIdMappings.put("order", Order.class);
        typeIdMappings.put("user", User.class);
        messageConverter.setTypeIdMappings(typeIdMappings);
        return messageConverter;
    }

}

使用 JmsTemplate 发送消息

引入ActiveMQ的依赖后,Spring Boot会自动配置一个JmsTemplate(以及其他内容),我们可以将它注入到其他bean中,并使用它来发送和接收消息。

JmsTemplate是Spring对JMS集成支持功能的核心。与Spring其他面向模板的组件类似,JmsTemplate消除了大量传统使用JMS时所需的样板代码。如果没有JmsTemplate的话,那么我们需要编写代码来创建到消息代理的连接和会话,还要编写更多的代码来处理发送消息过程中可能出现的异常。JmsTemplate能够让我们关注真正要做的事情:发送消息。

JmsTemplate有多个用来发送消息的方法,包括:

// 发送原始的消息
void send(MessageCreator messageCreator) throws JmsException;
void send(Destination destination, MessageCreator messageCreator)
                                                throws JmsException;
void send(String destinationName, MessageCreator messageCreator)
                                                throws JmsException;
// 发送根据对象转换而成的消息
void convertAndSend(Object message) throws JmsException;
void convertAndSend(Destination destination, Object message)
                                                throws JmsException;
void convertAndSend(String destinationName, Object message)
                                                throws JmsException;

// 发送根据对象转换而成的消息并且带有后期处理的功能
void convertAndSend(Object message,
            MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(Destination destination, Object message,
            MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(String destinationName, Object message,
            MessagePostProcessor postProcessor) throws JmsException;

3个send()方法都需要MessageCreator来生成Message对象。

3个convertAndSend()方法会接受Object对象,并且会在幕后自动将Object转换为Message。

最后3个convertAndSend()会自动将Object转换为Message,但同时还能接受一个MessagePostProcessor对象,用来在发送之前对Message进行自定义。

示例代码

@Service
public class JmsOrderMessageService implements OrderMessageService {

    @Autowired
    private JmsTemplate jmsTemplate;

    @Override
    public void sendOrderMessage(Order order) {

        //匿名内部类方式
        /*jmsTemplate.send("boot.integrate.jms.order.queue", new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                Message message = session.createObjectMessage(order);
                return message;
            }
        });*/

        //使用 lambda 替换上面的写法
        /*jmsTemplate.send("boot.integrate.jms.order.queue", session -> {
            Message message = session.createObjectMessage(order);
            return message;
        });*/

        //使用ConvertAndSend
        //jmsTemplate.convertAndSend("boot.integrate.jms.order.queue",order);

        //在消息发送之前使用MessagePostProcessor添加一个自定义的头信息
        /*jmsTemplate.convertAndSend("boot.integrate.jms.order.queue", order, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws JMSException {
                message.setStringProperty("X_ORDER_SOURCE","WEB");
                return message;
            }
        });*/

        //使用方法引用替换匿名内部类的写法
        jmsTemplate.convertAndSend("boot.integrate.jms.order.queue", order, this::addOrderSource);

    }

    @Override
    public void sendUserMessage(User user) {
        jmsTemplate.convertAndSend("boot.integrate.jms.user.queue", user);
    }

    private Message addOrderSource(Message message) throws JMSException {
        message.setStringProperty("X_ORDER_SOURCE","WEB");
        return message;
    }
}

接收JMS消息

在消费消息的时候,我们可以选择拉取模式(pull model)和推送模式(push model),前者会在我们的代码中请求消息并一直等待直到消息到达为止,而后者则会在消息可用的时候自动在你的代码中执行。

JmsTemplate提供了多种方式来接收消息,但它们使用的都是拉取模式。我们可以调用其中的某个方法来请求消息,而线程会一直阻塞到一个消息抵达为止(这可能马上发生,也可能需要等待一会儿)。

另外,我们也可以使用推送模式,在这种情况下,我们会定义一个消息监听器,每当有消息可用时,它就会被调用。

这两种方案能够适用于各种用户场景。人们普遍觉得推送模式是更好的方案,因为它不会阻塞线程;但是,在某些场景下,如果消息抵达的速度太快,那么监听器可能会过载。而拉取模式允许消费者声明它们何时才为接收新消息做好准备。

拉取模式

@Slf4j
@Service
public class JmsOrderMessageService implements OrderMessageService {

    private JmsTemplate jmsTemplate;
    private MessageConverter messageConverter;

    @Autowired
    public JmsOrderMessageService(JmsTemplate jmsTemplate,MessageConverter messageConverter) {
        this.jmsTemplate = jmsTemplate;
        this.messageConverter = messageConverter;
    }

    @Override
    public Order receive() throws JMSException {
        Message message = jmsTemplate.receive("boot.integrate.jms.order.queue");
        Order order = (Order) messageConverter.fromMessage(message);
        return order;
    }

    @Override
    public User receiveUser() throws JMSException {
        Message message = jmsTemplate.receive("boot.integrate.jms.user.queue");
        User user = (User) messageConverter.fromMessage(message);
        return user;
    }


}

推送模式

@Slf4j
@Component
public class OrderMessageListener {

    @JmsListener(destination = "boot.integrate.jms.order.queue")
    public void receiveOrder(Order order){
        log.info("Order info={}", JSON.toJSONString(order));
    }
}
@Slf4j
@Component
public class UserMessageListener {

    @JmsListener(destination = "boot.integrate.jms.user.queue")
    public void receiveOrder(User user){
        log.info("User info={}", JSON.toJSONString(user));
    }
}

完整的工程源码可以访问:boot-integrate-jms

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值