SpringBoot集成消息队列

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
日志服务 SLS,月写入数据量 50GB 1个月
简介: 最近在对公司开发框架进行优化,框架内涉及到多处入库的日志记录,例如登录日志/操作日志/访问日志/业务执行日志,集成在业务代码中耦合度较高且占用业务操作执行时间,所以准备集成相关消息队列进行代码解耦

背景

最近在对公司开发框架进行优化,框架内涉及到多处入库的日志记录,例如登录日志/操作日志/访问日志/业务执行日志,集成在业务代码中耦合度较高且占用业务操作执行时间,所以准备集成相关消息队列进行代码解耦

方案规划

现有的成熟消息队列组件非常多,例如RabbitMQ,ActiveMQ,Kafka等,考虑到业务并发量不高且框架已经应用于多个项目平稳运行,准备提供基于Redis的消息队列和集成ActiveMQ两种方案,Redis消息队列的好处是无需额外安装部署存量项目可平稳过度但消息无法持久化可能丢失,ActiveMQ解决方案成熟可以保证消息持久化但是需要实施人员额外掌握操作部署

统一设计

增加自定义配置指定消息队列方式

system:
  #消息队列方式 redis/activemq
  messageChannel: redis

定义消息传输统一模型

public class MessageModel {
   
    private Class<? extends IMessageReceiver> handleClazz;
    private String bodyContent;
    private Class bodyClass;
    private HashMap extraParam;

    public MessageModel(){
   
        extraParam = new HashMap();
    }

    public Class<? extends IMessageReceiver> getHandleClazz() {
   
        return handleClazz;
    }

    public void setHandleClazz(Class<? extends IMessageReceiver> handleClazz) {
   
        this.handleClazz = handleClazz;
    }

    public HashMap getExtraParam() {
   
        return extraParam;
    }

    public void setExtraParam(HashMap extraParam) {
   
        this.extraParam = extraParam;
    }

    public String getBodyContent() {
   
        return bodyContent;
    }

    public void setBodyContent(String bodyContent) {
   
        this.bodyContent = bodyContent;
    }

    public Class getBodyClass() {
   
        return bodyClass;
    }

    public void setBodyClass(Class bodyClass) {
   
        this.bodyClass = bodyClass;
    }
}

定义标准消息处理接口

public interface IMessageReceiver {
   
    void handleMessage(Object bodyObject, HashMap extraParam);
}

定义统一对外发送消息工具类

@Component
public class MessageUtil {
   
    @Autowired
    private SystemConfig systemConfig;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;

    public void sendMessage(Object messageBody, Class<? extends IMessageReceiver> handleClass, HashMap<String,Object> extraParam) {
   
        MessageModel messageModel = new MessageModel();
        messageModel.setHandleClazz(handleClass);
        messageModel.setBodyClass(messageBody.getClass());
        messageModel.setBodyContent(JSON.toJSONString(messageBody));
        if (extraParam != null) {
   
            for (String key:extraParam.keySet()) {
   
                messageModel.getExtraParam().put(key,extraParam.get(key));
            }
        }
        if(systemConfig.getMessageChannel().equals("redis")){
   
            redisUtil.sendMessage("message", JSON.toJSON(messageModel));
        }else{
   
            jmsMessagingTemplate.convertAndSend("message",JSON.toJSONString(messageModel));
        }
    }
}

集成Redis消息队列

pom配置

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>

连接配置

spring:
  redis:
    host: localhost
    port: 6379
    password:

操作工具类

    @Autowired
    private RedisTemplate redisTemplate;
    public void sendMessage(String channel, Object message) {
   
        redisTemplate.convertAndSend(channel, message);
    }

消息处理

@Component
@ConditionalOnProperty(name = "system.messageChannel", havingValue = "redis", matchIfMissing = true)
public class RedisMessageReceiver {
   
    public void receiveMessage(String message) {
   
        MessageModel messageModel = JSON.parseObject(message, MessageModel.class);
        IMessageReceiver receiver = SpringBootBeanUtil.getBean(messageModel.getHandleClazz());
        receiver.handleMessage(JSON.parseObject(messageModel.getBodyContent(), messageModel.getBodyClass()), messageModel.getExtraParam());
    }
}

配置注册

@Configuration
public class MessageCenter {
   
    @Bean
    @ConditionalOnProperty(name = "system.messageChannel", havingValue = "redis", matchIfMissing = true)
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
   
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        // 可以添加多个 messageListener,配置不同的交换机
        container.addMessageListener(listenerAdapter, new PatternTopic("message"));
        return container;
    }

    /**
     * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法
     *
     * @param receiver
     * @return
     */
    @Bean
    @ConditionalOnProperty(name = "system.messageChannel", havingValue = "redis", matchIfMissing = true)
    MessageListenerAdapter listenerAdapter(RedisMessageReceiver receiver) {
   
        return new MessageListenerAdapter(receiver, "receiveMessage");
    }
}

集成ActiveMQ消息队列

pom配置

        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-pool</artifactId>
            <version>5.15.0</version>
        </dependency>

注意:jdk1.8对应版本5.15.0

连接配置

spring:
  activemq:
    broker-url: tcp://127.0.0.1:61616 #MQ服务器地址
    user: admin
    password: admin
    pool:
      enabled: true

消息处理

@Component
@ConditionalOnProperty(name = "system.messageChannel", havingValue = "activemq", matchIfMissing = false)
public class ActiveMQMessageReceiver {
   
    @JmsListener(destination = "message", containerFactory = "customQueueListener")
    public void handleMessage(String message) {
   
        MessageModel messageModel = JSON.parseObject(message, MessageModel.class);
        IMessageReceiver receiver = SpringBootBeanUtil.getBean(messageModel.getHandleClazz());
        receiver.handleMessage(JSON.parseObject(messageModel.getBodyContent(), messageModel.getBodyClass()), messageModel.getExtraParam());
    }
}

配置注册

@Configuration
@EnableJms
public class MessageCenter {
   
    @Bean(name = "customQueueListener")
    @ConditionalOnProperty(name = "system.messageChannel", havingValue = "activemq", matchIfMissing = false)
    public JmsListenerContainerFactory<?> customQueueListener(ConnectionFactory connectionFactory) {
   
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setPubSubDomain(false);
        factory.setConnectionFactory(connectionFactory);
        //重连间隔时间
        factory.setRecoveryInterval(1000L);
        factory.setSessionAcknowledgeMode(Session.AUTO_ACKNOWLEDGE);
        //连接数
        factory.setConcurrency("5-10");
        //指定任务线程池
        factory.setTaskExecutor(new ThreadPoolExecutor(5, 10, 1, TimeUnit.MINUTES,
                new LinkedBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy()));
        return factory;
    }
}

使用示例

消息处理

@Service
public class RequestLogMessageReceiver implements IMessageReceiver{
   
    @Autowired
    private F_RequestLogService requestLogService;

    @Override
    public void handleMessage(Object bodyObject, HashMap extraParam) {
   
        F_RequestLogDO requestLogDO = (F_RequestLogDO)bodyObject;
        requestLogService.insert(requestLogDO);
    }
}

发送消息

@AutoWired
private MessageUtil messageUtil;
messageUtil.sendMessage(requestLogDO,RequestLogMessageReceiver.class,null);
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
14天前
|
XML Java 数据库连接
SpringBoot集成Flowable:打造强大的工作流管理系统
在企业级应用开发中,工作流管理是一个核心组件,它能够帮助我们定义、执行和管理业务流程。Flowable是一个开源的工作流和业务流程管理(BPM)平台,它提供了强大的工作流引擎和建模工具。结合SpringBoot,我们可以快速构建一个高效、灵活的工作流管理系统。本文将探讨如何将Flowable集成到SpringBoot应用中,并展示其强大的功能。
55 1
|
23天前
|
JSON Java API
springboot集成ElasticSearch使用completion实现补全功能
springboot集成ElasticSearch使用completion实现补全功能
24 1
|
14天前
|
XML 存储 Java
SpringBoot集成Flowable:构建强大的工作流引擎
在企业级应用开发中,工作流管理是核心功能之一。Flowable是一个开源的工作流引擎,它提供了BPMN 2.0规范的实现,并且与SpringBoot框架完美集成。本文将探讨如何使用SpringBoot和Flowable构建一个强大的工作流引擎,并分享一些实践技巧。
39 0
|
1月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
103 1
|
1月前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
278 11
|
2月前
|
XML Java 关系型数据库
springboot 集成 mybatis-plus 代码生成器
本文介绍了如何在Spring Boot项目中集成MyBatis-Plus代码生成器,包括导入相关依赖坐标、配置快速代码生成器以及自定义代码生成器模板的步骤和代码示例,旨在提高开发效率,快速生成Entity、Mapper、Mapper XML、Service、Controller等代码。
springboot 集成 mybatis-plus 代码生成器
|
2月前
|
Java Spring
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
本文介绍了如何在Spring Boot项目中集成Swagger 2.x和3.0版本,并提供了解决Swagger在Spring Boot中启动失败问题“Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerEx”的方法,包括配置yml文件和Spring Boot版本的降级。
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
|
1月前
|
Java Spring
springboot 学习十一:Spring Boot 优雅的集成 Lombok
这篇文章是关于如何在Spring Boot项目中集成Lombok,以简化JavaBean的编写,避免冗余代码,并提供了相关的配置步骤和常用注解的介绍。
99 0
|
3月前
|
NoSQL 关系型数据库 MySQL
SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘
SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘
150 2
下一篇
无影云桌面