• 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程序的输出日志查看代码自定义日志的发送接收消息。

    消息发送

    依赖

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-artemis</artifactId>
    4. </dependency>

    application.properties关键部分

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

    关键代码JmsOrderMessagingService

    1. package com.wdh.tacocloud_jms.messaging;
    2. import com.wdh.tacocloud_jms.domain.Order;
    3. import lombok.extern.slf4j.Slf4j;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.jms.core.JmsTemplate;
    6. import org.springframework.jms.core.MessageCreator;
    7. import org.springframework.stereotype.Service;
    8. import javax.jms.JMSException;
    9. import javax.jms.Message;
    10. import javax.jms.Session;
    11. /**
    12. * @author WangDH
    13. * @create 2022-09-27 14:32
    14. */
    15. @Slf4j
    16. @Service
    17. public class JmsOrderMessagingService implements OrderMessagingService {
    18. private JmsTemplate jmsTemplate;
    19. @Autowired
    20. public JmsOrderMessagingService(JmsTemplate jmsTemplate) {
    21. this.jmsTemplate = jmsTemplate;
    22. }
    23. @Override
    24. public void sendOrder(Order order) {
    25. log.info("JmsOrderMessagingService start sendOrder start.");
    26. //方式一
    27. // jmsTemplate.send(
    28. // new MessageCreator() {
    29. // @Override
    30. // public Message createMessage(Session session) throws JMSException {
    31. // //return session.createObjectMessage("Hello from wdhSpringBoot");
    32. // return session.createObjectMessage(order);
    33. // }
    34. // }
    35. // );
    36. // //方式2
    37. // jmsTemplate.send("wdhqueue",
    38. // new MessageCreator() {
    39. // @Override
    40. // public Message createMessage(Session session) throws JMSException {
    41. // //return session.createObjectMessage("Hello from wdhSpringBoot");
    42. // return session.createObjectMessage(order);
    43. // }
    44. // }
    45. // );
    46. // //方式3
    47. // jmsTemplate.convertAndSend("wdhqueue",order);
    48. //方式3
    49. jmsTemplate.convertAndSend("wdhqueue",order,this::addOrderSource);
    50. log.info("JmsOrderMessagingService start sendOrder end.");
    51. }
    52. private Message addOrderSource(Message message) throws JMSException{
    53. message.setStringProperty("X_ORDER_SOURCE","WEB");
    54. return message;
    55. }
    56. }

    RestController部分代码

    1. package com.wdh.tacocloud_jms.api;
    2. import com.wdh.tacocloud_jms.data.OrderRepository;
    3. import com.wdh.tacocloud_jms.domain.Order;
    4. import com.wdh.tacocloud_jms.messaging.OrderMessagingService;
    5. import lombok.extern.slf4j.Slf4j;
    6. import org.springframework.dao.EmptyResultDataAccessException;
    7. import org.springframework.http.HttpStatus;
    8. import org.springframework.web.bind.annotation.*;
    9. import java.text.SimpleDateFormat;
    10. import java.util.Date;
    11. import java.util.Optional;
    12. /**
    13. * @author WangDH
    14. * @create 2022-09-23 9:55
    15. */
    16. @Slf4j
    17. @RestController
    18. @RequestMapping(path="/orders",produces = "application/json")
    19. @CrossOrigin(origins = "*")
    20. public class OrderApiController {
    21. private OrderRepository orderRepo;
    22. private OrderMessagingService orderMessagingService;
    23. public OrderApiController(OrderRepository orderRepo
    24. ,OrderMessagingService orderMessagingService
    25. ) {
    26. this.orderMessagingService=orderMessagingService;
    27. this.orderRepo = orderRepo;
    28. }
    29. @GetMapping("/")
    30. public String getDefault(){
    31. log.info("######### OrderApiController enter getDefault");
    32. return "ok,this is OrderApiController getDefault return";
    33. }
    34. @GetMapping("/sendJMS")
    35. public String sendJMS(){
    36. log.info("######### OrderApiController enter sendJMS");
    37. Date date=new Date();
    38. SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    39. String time=formatter.format(date);
    40. Order order=new Order();
    41. order.setId((long) 81);
    42. order.setDeliveryName("jmsTestName"+time);
    43. orderMessagingService.sendOrder(order);
    44. return "ok,this is OrderApiController sendJMS return。time="+time;
    45. }
    46. }

    消息接收

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

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

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

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

    依赖

    1. <dependency>
    2. <groupId>org.springframework.boot</groupId>
    3. <artifactId>spring-boot-starter-artemis</artifactId>
    4. </dependency>

    application.propertites中关键部分

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

    消息接收代码-拉模型

    1. package com.wdh.tacocloud_kitchen.kitchen.messaging.jms;
    2. import com.wdh.tacocloud_kitchen.OrderReceiver;
    3. import com.wdh.tacocloud_kitchen.domain.Order;
    4. import lombok.extern.slf4j.Slf4j;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.jms.core.JmsTemplate;
    7. import org.springframework.jms.support.converter.MessageConverter;
    8. import org.springframework.stereotype.Component;
    9. import javax.jms.JMSException;
    10. import javax.jms.Message;
    11. import java.util.Date;
    12. /**
    13. * @author WangDH
    14. * @create 2022-09-27 17:21
    15. * 注意,这种方式是[拉模型]
    16. */
    17. @Slf4j
    18. @Component
    19. public class JmsOrderReceiver implements OrderReceiver {
    20. // //方式1
    21. private JmsTemplate jmsTemplate;
    22. private MessageConverter messageConverter;
    23. @Autowired
    24. public JmsOrderReceiver(JmsTemplate jmsTemplate, MessageConverter messageConverter) {
    25. this.jmsTemplate = jmsTemplate;
    26. this.messageConverter = messageConverter;
    27. }
    28. @Override
    29. public Order receiveOrder() throws JMSException {
    30. Message message= jmsTemplate.receive("wdhqueue");
    31. Order order=(Order)messageConverter.fromMessage(message);
    32. return order;
    33. }
    34. //方式2
    35. private JmsTemplate jmsTemplate;
    36. @Autowired
    37. public JmsOrderReceiver(JmsTemplate jmsTemplate) {
    38. this.jmsTemplate = jmsTemplate;
    39. }
    40. @Override
    41. public Order receiveOrder() {
    42. jmsTemplate.setReceiveTimeout(10000);//设置10000ms的等待时间,防止receiveAndConvert阻塞
    43. log.info("JmsOrderReceiver receiveOrder,准备接收队列消息,请在队列发送端发送消息。 receiveAndConvert start at "+(new Date()).toString());
    44. Order order=(Order)jmsTemplate.receiveAndConvert("wdhqueue");
    45. log.info("JmsOrderReceiver receiveOrder receiveAndConvert end at "+(new Date()).toString());
    46. return order;
    47. }
    48. }

    消息接收代码-推模型

    1. package com.wdh.tacocloud_kitchen.kitchen.messaging.jms.listener;
    2. import com.wdh.tacocloud_kitchen.KitchenUI;
    3. import com.wdh.tacocloud_kitchen.domain.Order;
    4. import lombok.extern.slf4j.Slf4j;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.jms.annotation.JmsListener;
    7. import org.springframework.stereotype.Component;
    8. /**
    9. * @author WangDH
    10. * @create 2022-09-27 17:33
    11. *
    12. * 建立监听器以配合消息队列实现【推模型】
    13. *
    14. * 注意,这个与JmsOrderReceiver的拉模型互斥,二者代码只能选择一个
    15. * 如果同时使用【推模型】OrderListener和【拉模型】JmsOrderReceiver,
    16. * 则【推模型】OrderListener会优先截取队列的消息,导致【拉模型】JmsOrderReceiver接收到的数据为空
    17. */
    18. @Slf4j
    19. @Component
    20. public class OrderListener {
    21. private KitchenUI kitchenUI;
    22. @Autowired
    23. public OrderListener(KitchenUI kitchenUI) {
    24. this.kitchenUI = kitchenUI;
    25. }
    26. @JmsListener(destination = "wdhqueue")
    27. public void receiveOrder(Order order){
    28. kitchenUI.displayOrder(order);
    29. }
    30. }

    测试消息发送与接收

    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已收到消息。

  • 相关阅读:
    【JVM虚拟机】JVM常见面试题总结
    java存储数据到本地txt文件中-以及-读取txt文件的内容
    Kafka系列之:Kafka Connect错误报告设置
    如何打开pak文件-翻译pak语言包
    机器人制作开源方案 | 晾衣收纳一体机器人
    net-java-php-python-大学生互助旅游网站修改计算机毕业设计程序
    openlayer注册4490坐标系,添加4490超图服务
    mysql查看sql日志操作
    使用DIV、CSS技术设计的个人博客网页(web期末考试)
    Docker:从入门到实战过程全记录
  • 原文地址:https://blog.csdn.net/wangdonghao137/article/details/127122333