高效异步处理:基于RocketMQ的延迟消费系统架构全解析

基于RocketMQ的延迟消费系统架构全解析

一、消息实体定义

二、生产者服务实现

三、消费者监听器实现

四、配置文件

五、Maven依赖

六、关键设计要点

[6.1 生产者发送策略](#6.1 生产者发送策略)

[6.2 消费者处理策略](#6.2 消费者处理策略)

[6.3 配置优化](#6.3 配置优化)

一、消息实体定义

java 复制代码
package jnpf.model.attendance.event;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Date;

/**
 * @author shitou
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AttendanceStatisticsSingleDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 租户Id
     */
    @NotBlank(message = "租户Id不能为空")
    private String tenantId;
    /**
     * 考勤组Id
     */
    @NotBlank(message = "考勤组Id不能为空")
    private String groupId;
    /**
     * 用户Id
     */
    @NotBlank(message = "用户Id不能为空")
    private String userId;
    /**
     * 日期
     */
    @NotNull(message = "日期不能为空")
    private Date day;
}

二、生产者服务实现

java 复制代码
    @NoDataSourceBind
    @Operation(summary = "模拟统计数据消息推送")
    @GetMapping("/mockStatisticsPush")
    public ActionResult<Boolean> mockStatisticsPush(@RequestParam("tenantId") String tenantId,
                                                    @RequestParam("groupId") String groupId,
                                                    @RequestParam("userId") String userId,
                                                    @RequestParam("day") String day) {
        AttendanceStatisticsSingleDto courseEventDTO = AttendanceStatisticsSingleDto.builder()
                .tenantId(tenantId)
                .groupId(groupId)
                .userId(userId)
                .day(DateUtil.parse(day))
                .build();
        Message<AttendanceStatisticsSingleDto> message = MessageBuilder.withPayload(courseEventDTO).build();
        rocketMqTemplate.syncSend(MessageTopicConstants.ATTENDANCE_STATISTICS_SINGLE_TOPIC, message, 3000L, 2);
        return ActionResult.success();
    }

三、消费者监听器实现

java 复制代码
package jnpf.attendance.event;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import jnpf.attendance.service.AttendanceDayStatisticsService;
import jnpf.constants.MessageTopicConstants;
import jnpf.model.attendance.event.AttendanceStatisticsSingleDto;
import jnpf.util.CustomTenantUtil;
import jnpf.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.spring.annotation.ConsumeMode;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * 监听单个用户的考勤日统计消息
 * 消费失败会重试最多2次(共3次),之后进入死信队列
 */
@Slf4j
@Component
@RocketMQMessageListener(
        topic = MessageTopicConstants.ATTENDANCE_STATISTICS_SINGLE_TOPIC,
        consumerGroup = MessageTopicConstants.ATTENDANCE_STATISTICS_SINGLE_CONSUMER_GROUP,
        consumeMode = ConsumeMode.CONCURRENTLY,
        consumeThreadNumber = 2,
        maxReconsumeTimes = 2)
public class StatisticsSingleMQListener implements RocketMQListener<AttendanceStatisticsSingleDto>, RocketMQPushConsumerLifecycleListener {

    @Autowired
    private CustomTenantUtil tenantUtil;
    @Resource
    private AttendanceDayStatisticsService attendanceDayStatisticsService;

    @Override
    public void onMessage(AttendanceStatisticsSingleDto singleDto) {
        if (Objects.isNull(singleDto)) {
            log.warn("接收到空消息,忽略处理");
            return;
        }
        log.error("接受到一条生成用户日统计消息,{}", JSONObject.toJSONString(singleDto));
        // 校验必要字段
        if (ObjectUtil.isNull(singleDto) || StringUtil.isEmpty(singleDto.getUserId()) ||
                StringUtil.isEmpty(singleDto.getTenantId()) || StringUtil.isEmpty(singleDto.getGroupId()) ||
                ObjectUtil.isNull(singleDto.getDay())) {
            log.error("生成用户日统计消息消费失败:消息格式无效,内容: {}", JSONObject.toJSONString(singleDto));
            return;
        }
        try {
            tenantUtil.checkOutTenant(singleDto.getTenantId());
            attendanceDayStatisticsService.statisticDataChange(singleDto.getGroupId(), singleDto.getUserId(), singleDto.getDay());
        } catch (Exception ex) {
            log.error("处理用户日统计消息失败,触发重试,消息 {}, 错误: ", JSONObject.toJSONString(singleDto), ex);
            throw new RuntimeException("处理用户日统计消息失败,触发重试", ex);
        }
    }

    @Override
    public void prepareStart(DefaultMQPushConsumer consumer) {
        // 优化消费参数 每次拉取的消息数量
        consumer.setPullBatchSize(16);
        // 顺序消费每次批量消费2条
        consumer.setConsumeMessageBatchMaxSize(2);
        // 设置消费间隔,避免过于频繁拉取
        consumer.setPullInterval(3000);
        // 设置消费超时时间(分钟)
        consumer.setConsumeTimeout(10);
        // 设置消费起始位置(从上次消费的位置继续)
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
        // 调整消费线程池最小线程数
        consumer.setConsumeThreadMin(2);
        // 调整消费线程池最大线程数
        consumer.setConsumeThreadMax(4);
    }
}

四、配置文件

application.yml:

yaml 复制代码
rocketmq:
  name-server: ${ROCKETMQ_NAME_SERVER:192.168.3.24:30094}
  producer:
    group: ${ROCKETMQ_PRODUCER_GROUP:fantaibao-producer-group}
    send-message-timeout: ${ROCKETMQ_SEND_MESSAGE_TIMEOUT:30000}
    max-message-size: ${ROCKETMQ_MAX_MESSAGE_SIZE:8388608}
 

消息主题常量:

java 复制代码
package jnpf.constants;

public interface MessageTopicConstants {
        //考勤统计单个生成-发送的延迟消息(等级是2(5秒))
    String ATTENDANCE_STATISTICS_SINGLE_TOPIC = "attendance-statistics-single-topic";
    String ATTENDANCE_STATISTICS_SINGLE_CONSUMER_GROUP = "attendance-statistics-single--consumer-group";
}

五、Maven依赖

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
    <version>2022.0.0.0</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.32</version>
</dependency>
 

六、关键设计要点

6.1 生产者发送策略

  1. 延迟发送:定时触发或削峰填谷

6.2 消费者处理策略

  1. 异步处理:使用线程池异步执行,避免阻塞消费线程

  2. 延迟执行:2秒延迟实现削峰填谷

  3. 参数校验:三层校验确保消息有效性

  4. 异常处理:异常抛出触发重试机制

6.3 配置优化

  1. 拉取参数:平衡拉取频率和消息堆积

  2. 超时设置:根据业务处理时间设置合理超时

  3. 重试策略:设置最大重试次数避免无限重试

这个实现提供了完整的基于RocketMQ的考勤统计批量处理方案,包含生产者、消费者业务服务实现,可以直接在项目中集成使用。

相关推荐
今天只学一颗糖5 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
若风的雨10 小时前
【deepseek】RT-Thread 对 PCIe 的支持情况
系统架构
我只会写Bug啊14 小时前
【软考】系统架构设计师-论文范文(三)
系统架构·软考·系统架构师·系统分析师·十大管理·信息项目管理工程师
我只会写Bug啊14 小时前
【软考】系统架构设计师-论文范文(二)
系统架构·软考·系统分析师·十大管理·信息项目管理
AI周红伟20 小时前
周红伟:ChatADS聊天广告,OpenAI广告系统架构设计:实时投放与访问扩展的工程实现
系统架构
C澒2 天前
电商供应链履约中台架构与业务全流程解析
前端·架构·系统架构·教育电商·交通物流
知识即是力量ol2 天前
亿级日活背后的技术挑战:如何构建高并发通用计数系统?——《亿级流量系统架构设计与实战》
redis·系统架构·高并发·计数系统·亿级日活
我只会写Bug啊2 天前
【软考】系统架构设计师-论文范文(一)
大数据·系统架构·信息系统项目管理师·架构设计·系统分析师
AIagenttest2 天前
2026年招聘系统架构演进:为何视觉语义智能体正在取代传统爬虫?(含ROI算力模型分析)
爬虫·系统架构