Spring In Action 5 学习笔记 chapter8 JMS(ActiveMQ Artemis)要点

 本文记录Sping In Action5 第8章 发送异步消息JMS(ActiveMQ Artemis)中的踩坑情况。

第8章的源码请自行github或gitee搜索,或参考一下。

GitHub - habuma/spring-in-action-5-samples: Home for example code from Spring in Action 5.

https://gitee.com/drop1et/spring-in-action-5-samples/

操作系统及IDE环境

win7 x64

jdk 1.8

idea 2018.3

JMS(ActiveMQ Artemis)

环境

ActiveMQ Artemis版本号:apache-artemis-2.19.1

下载、安装、启动步骤请网搜。可参考https://www.jianshu.com/p/b2734268aaaf

官网https://activemq.apache.org/components/artemis/

注意使用cmd窗口将artemis启动后,不要关闭cmd窗口,以便保留artemis。

注意,因jms使用的并非MQTT协议,因此使用可视化图形界面时将看不到springboot JMS发送和接收的消息,可通过IDEA控制台中springboot程序的输出日志查看代码自定义日志的发送接收消息。

消息发送

依赖

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

application.properties关键部分

#ActiveMQ Artemis 配置
#ActiveMQ Artemis broker主机
spring.artemis.host=localhost
#ActiveMQ Artemis broker端口
spring.artemis.port=61616
#ActiveMQ Artemis broker 访问用户名
spring.artemis.user=admin
#ActiveMQ Artemis broker 访问密码
spring.artemis.password=admin
#指定jms发送消息的目的地(主体)
spring.jms.template.default-destination=wdhqueue

关键代码JmsOrderMessagingService

package com.wdh.tacocloud_jms.messaging;

import com.wdh.tacocloud_jms.domain.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;

/**
 * @author WangDH
 * @create 2022-09-27 14:32
 */
@Slf4j
@Service
public class JmsOrderMessagingService implements OrderMessagingService {

    private JmsTemplate jmsTemplate;

    @Autowired
    public JmsOrderMessagingService(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }


    @Override
    public void sendOrder(Order order) {
        log.info("JmsOrderMessagingService start sendOrder start.");

        //方式一
//        jmsTemplate.send(
//                new MessageCreator() {
//                    @Override
//                    public Message createMessage(Session session) throws JMSException {
//                        //return session.createObjectMessage("Hello from wdhSpringBoot");
//                        return session.createObjectMessage(order);
//                    }
//                }
//        );

//        //方式2
//        jmsTemplate.send("wdhqueue",
//                new MessageCreator() {
//                    @Override
//                    public Message createMessage(Session session) throws JMSException {
//                        //return session.createObjectMessage("Hello from wdhSpringBoot");
//                        return session.createObjectMessage(order);
//                    }
//                }
//        );



//        //方式3
//        jmsTemplate.convertAndSend("wdhqueue",order);


        //方式3
        jmsTemplate.convertAndSend("wdhqueue",order,this::addOrderSource);

        log.info("JmsOrderMessagingService start sendOrder end.");
    }

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

RestController部分代码

package com.wdh.tacocloud_jms.api;

import com.wdh.tacocloud_jms.data.OrderRepository;
import com.wdh.tacocloud_jms.domain.Order;
import com.wdh.tacocloud_jms.messaging.OrderMessagingService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Optional;

/**
 * @author WangDH
 * @create 2022-09-23 9:55
 */
@Slf4j
@RestController
@RequestMapping(path="/orders",produces = "application/json")
@CrossOrigin(origins = "*")
public class OrderApiController {

    private OrderRepository orderRepo;
    private OrderMessagingService orderMessagingService;


    public OrderApiController(OrderRepository orderRepo
                              ,OrderMessagingService orderMessagingService
    ) {
        this.orderMessagingService=orderMessagingService;
        this.orderRepo = orderRepo;
    }

    @GetMapping("/")
    public String getDefault(){

        log.info("######### OrderApiController enter getDefault");


        return "ok,this is OrderApiController getDefault return";
    }

    @GetMapping("/sendJMS")
    public String sendJMS(){

        log.info("######### OrderApiController enter sendJMS");

        Date date=new Date();
        SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time=formatter.format(date);


        Order order=new Order();
        order.setId((long) 81);
        order.setDeliveryName("jmsTestName"+time);
        orderMessagingService.sendOrder(order);

        return "ok,this is OrderApiController sendJMS return。time="+time;
    }


}

消息接收

要点1:【推模型】OrderListener和【拉模型】JmsOrderReceiver互斥,二者代码只能选择一个

要点2:如果同时使用【推模型】OrderListener和【拉模型】JmsOrderReceiver,则【推模型】OrderListener会优先截取队列的消息,导致【拉模型】JmsOrderReceiver接收到的数据为空。

要点3:【拉模型】类JmsOrderReceiver的receiveOrder()方法中的jmsTemplate.receiveAndConvert()会阻塞直到发送端有数据发送。

消息接收端程序采用SpringMVC+Thymeleaf

依赖

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

application.propertites中关键部分

#ActiveMQ Artemis 配置
#ActiveMQ Artemis broker主机
spring.artemis.host=localhost
#ActiveMQ Artemis broker端口
spring.artemis.port=61616
#ActiveMQ Artemis broker 访问用户名
spring.artemis.user=admin
#ActiveMQ Artemis broker 访问密码
spring.artemis.password=admin
#指定jms发送消息的目的地(主体)
spring.jms.template.default-destination=wdhqueue

消息接收代码-拉模型

package com.wdh.tacocloud_kitchen.kitchen.messaging.jms;

import com.wdh.tacocloud_kitchen.OrderReceiver;
import com.wdh.tacocloud_kitchen.domain.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import java.util.Date;

/**
 * @author WangDH
 * @create 2022-09-27 17:21
 * 注意,这种方式是[拉模型]
 */


@Slf4j
@Component
public class JmsOrderReceiver implements OrderReceiver {

//    //方式1
////    private JmsTemplate jmsTemplate;
////    private MessageConverter messageConverter;
////
////    @Autowired
////    public JmsOrderReceiver(JmsTemplate jmsTemplate, MessageConverter messageConverter) {
////        this.jmsTemplate = jmsTemplate;
////        this.messageConverter = messageConverter;
////    }
////
////    @Override
////    public Order receiveOrder() throws JMSException {
////
////        Message message= jmsTemplate.receive("wdhqueue");
////        Order order=(Order)messageConverter.fromMessage(message);
////
////        return order;
////    }


    //方式2
    private JmsTemplate jmsTemplate;

    @Autowired
    public JmsOrderReceiver(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    @Override
    public Order receiveOrder() {

        jmsTemplate.setReceiveTimeout(10000);//设置10000ms的等待时间,防止receiveAndConvert阻塞

        log.info("JmsOrderReceiver receiveOrder,准备接收队列消息,请在队列发送端发送消息。 receiveAndConvert start at "+(new Date()).toString());

        Order order=(Order)jmsTemplate.receiveAndConvert("wdhqueue");

        log.info("JmsOrderReceiver receiveOrder receiveAndConvert end at "+(new Date()).toString());
        return order;
    }
}

消息接收代码-推模型

package com.wdh.tacocloud_kitchen.kitchen.messaging.jms.listener;

import com.wdh.tacocloud_kitchen.KitchenUI;
import com.wdh.tacocloud_kitchen.domain.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

/**
 * @author WangDH
 * @create 2022-09-27 17:33
 *
 * 建立监听器以配合消息队列实现【推模型】
 *
 * 注意,这个与JmsOrderReceiver的拉模型互斥,二者代码只能选择一个
 * 如果同时使用【推模型】OrderListener和【拉模型】JmsOrderReceiver,
 * 则【推模型】OrderListener会优先截取队列的消息,导致【拉模型】JmsOrderReceiver接收到的数据为空
 */
@Slf4j
@Component
public class OrderListener {

    private KitchenUI kitchenUI;

    @Autowired
    public OrderListener(KitchenUI kitchenUI) {
        this.kitchenUI = kitchenUI;
    }

    @JmsListener(destination = "wdhqueue")
    public void receiveOrder(Order order){

        kitchenUI.displayOrder(order);

    }

}

测试消息发送与接收

1.保证ActiveMQ artemis正在运行中

2.启动发送端springboot程序TacocloudJmsApplication(端口8080)

3.启动接收端springboot程序TacocloudKitchenApplication(端口8081)

4.打开浏览器输入【http://localhost:8090/orders/sendJMS】以调用发送端程序的rest服务,回车后因spring security要求先输入用户名user,再根据idea中提示的密码复制过来再回车,登录后,网页提示消息已发送。

5.接收端【拉模式】,在发送端已发送消息后, 打开浏览器输入【http://localhost:8081/orders/receive】回车会跳转到receiveOrder.html显示相应信息。(如果再次发送消息,则需要手动刷新浏览器以再次拉取消息)

6.接收端【推模式】,在发送端已发送消息后,在IDEA的控制台中即可看到TacocloudKitchenApplication程序输出的日志显示OrderListener已收到消息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值