SpringBoot集成ActiveMQ:异常处理与可靠消息消费实践(try-catch避免重试)

一、异常处理与可靠消息消费实践

SpringBoot集成ActiveMQ:异常处理与可靠消息消费实践(try-catch避免重试)


引言

在分布式系统中,消息队列(如ActiveMQ)常用于解耦生产者和消费者,提升系统的异步处理能力。但在实际应用中,消费者端的异常处理逻辑 往往容易被忽视,尤其是在要求 无论处理成功与否都必须确认消息并记录结果 的场景中。本文将基于SpringBoot和ActiveMQ,探讨如何在自动确认模式下,实现异常捕获、消息可靠消费与结果持久化的完整方案。


问题背景

  1. 消息必须被确认:无论消费者处理消息时是否抛出异常(包括业务异常和系统异常),消息都需被确认(从队列移除),避免触发重试机制。
  2. 记录处理结果:处理成功时记录成功状态,失败时记录失败原因(包括异常信息)。
  3. 避免手动确认的复杂性:尽量利用默认的自动确认模式,减少配置复杂度。

核心挑战

在默认的自动确认模式(AUTO_ACKNOWLEDGE)下,若消费方法抛出异常,消息会因未被确认而重新入队(触发重试)。因此,必须确保:

  • 异常不会传播到消息监听容器,否则容器会认为消息处理失败。

解决方案

1. 保持自动确认模式,但捕获所有异常

在消费方法中通过try-catch捕获所有异常,确保方法正常退出,从而让容器认为消息已成功处理。

关键代码示例
@Component
public class MessageConsumer {

    @Autowired
    private TaskRecordService taskRecordService;

    @JmsListener(destination = "your-queue")
    public void consumeMessage(TextMessage message) {
        String taskId = null;
        try {
            // 解析消息内容
            String payload = message.getText();
            Task task = parsePayload(payload);
            taskId = task.getId();

            // 处理业务逻辑(可能抛出异常)
            processTask(task);

            // 记录成功
            taskRecordService.recordSuccess(taskId);
        } catch (Exception e) {
            // 记录失败(包括业务异常和其他异常)
            if (taskId != null) {
                taskRecordService.recordFailure(taskId, e.getMessage());
            }
            // 关键点:不抛出异常,确保方法正常退出
        }
    }
}

2. 数据库记录的可靠性保障

2.1 记录结果
@Service
public class TaskRecordService {

    public void recordSuccess(String taskId) {
        TaskRecord record = new TaskRecord(taskId, "SUCCESS", null);
        taskRecordRepository.save(record);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void recordFailure(String taskId, String errorMessage) {
        TaskRecord record = new TaskRecord(taskId, "FAILED", errorMessage);
        taskRecordRepository.save(record);
    }
}
2.2 防御数据库操作异常

在极端情况下(如数据库宕机),需捕获记录操作自身的异常,避免异常传递到消费方法外层。

@JmsListener(destination = "your-queue")
public void consumeMessage(TextMessage message) {
    String taskId = null;
    try {
        // ... 业务处理逻辑 ...
        taskRecordService.recordSuccess(taskId);
    } catch (Exception e) {
        try {
            if (taskId != null) {
                taskRecordService.recordFailure(taskId, e.getMessage());
            }
        } catch (Exception dbEx) {
            // 记录日志,但不抛出异常
            log.error("记录任务失败时发生异常: {}", dbEx.getMessage());
        }
    }
}

通过本文的方案,开发者可以在SpringBoot中快速实现ActiveMQ消费者的异常处理与可靠消费,平衡开发效率与系统可靠性。


二、示例项目及代码

项目结构示例:

TaskQueueConsumer

package com.example.hello.activemq.consumer;

import com.example.hello.activemq.model.TaskMessage;
import com.example.hello.activemq.service.TaskMessageParseService;
import com.example.hello.activemq.service.TaskRecordService;
import com.example.hello.activemq.service.TaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

/**
 * 任务队列消费者
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class TaskQueueConsumer {

    private final TaskService taskService;
    private final TaskRecordService taskRecordService;
    private final TaskMessageParseService taskMessageParseService;

    /**
     * 任务队列消费者
     */
    @JmsListener(destination = "task-queue")
    private void receive(String message) {
        log.info("消息处理开始");

        // 解析消息
        log.info("消息内容(字符串): {}", message);
        TaskMessage taskMessage = taskMessageParseService.parse(message);
        log.info("解析消息内容为任务消息对象: {}", taskMessage);

        if (taskMessage == null) {
            log.info("消息处理结束:任务消息解析失败,提前返回");
            return;
        }

        // 处理业务逻辑
        processTask(taskMessage);

        log.info("消息处理结束");
    }

    private void processTask(TaskMessage taskMessage) {
        try {
            // 处理业务逻辑(可能抛出异常)
            taskService.processTask(taskMessage);

            // 业务处理成功,记录任务执行明细(执行成功)到数据库
            taskRecordService.recordSuccess(taskMessage);
        } catch (Exception e) {
            // 关键点:捕获异常后不抛出,确保方法正常退出
            log.info("任务执行失败:", e);

            try {
                // 业务处理失败,记录任务执行明细(执行失败)到数据库
                taskRecordService.recordFailure(taskMessage, e.getMessage());
            } catch (Exception ex) {
                log.error("记录任务失败时发生异常:", ex);
            }

        }
    }

}

TaskService

package com.example.hello.activemq.service;

import com.example.hello.activemq.model.TaskMessage;

public interface TaskService {
    void processTask(TaskMessage taskMessage);
}

TaskServiceImpl

package com.example.hello.activemq.service.impl;

import com.example.hello.activemq.model.TaskMessage;
import com.example.hello.activemq.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDate;

@Slf4j
@Service
public class TaskServiceImpl implements TaskService {

    /**
     * 处理业务逻辑(可能抛出异常)
     */
    @Override
    public void processTask(TaskMessage taskMessage) {
        // 模拟特定条件下抛出异常
        LocalDate taskDate = taskMessage.getTaskDate();
        LocalDate beginDate = LocalDate.of(2025, 1, 1);
        if (taskDate != null && taskDate.isBefore(beginDate)) {
            String message = String.format("【任务日期】小于【开始日期】,任务日期=%s,开始日期=%s", taskDate, beginDate);
            throw new RuntimeException(message);
        }

        log.info("业务逻辑正常执行");
    }

}

TaskRecordService

package com.example.hello.activemq.service;

import com.example.hello.activemq.model.TaskMessage;

/**
 * 任务记录服务
 * <br>
 * 将任务执行结果记录到数据库中。
 */
public interface TaskRecordService {
    void recordSuccess(TaskMessage taskMessage);

    void recordFailure(TaskMessage taskMessage, String errorMessage);

    void recordFailure(String rawMessage, String errorMessage);
}

TaskRecordServiceImpl

package com.example.hello.activemq.service.impl;

import com.example.hello.activemq.enumeration.TaskStatus;
import com.example.hello.activemq.mapper.TaskRecordMapper;
import com.example.hello.activemq.model.TaskMessage;
import com.example.hello.activemq.model.TaskRecord;
import com.example.hello.activemq.service.TaskRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

@Slf4j
@Service
@RequiredArgsConstructor
public class TaskRecordServiceImpl implements TaskRecordService {

    private final TaskRecordMapper taskRecordMapper;

    @Override
    public void recordSuccess(TaskMessage taskMessage) {
        TaskRecord taskRecord = getTaskRecord(taskMessage);
        taskRecord.setStatus(TaskStatus.SUCCESS.getCode());
        taskRecordMapper.insert(taskRecord);

        log.info("记录任务执行明细(执行成功)到数据库。任务记录:{}", taskRecord);
    }

    @Override
    public void recordFailure(TaskMessage taskMessage, String errorMessage) {
        TaskRecord taskRecord = getTaskRecord(taskMessage);
        taskRecord.setStatus(TaskStatus.FAILURE.getCode());
        taskRecord.setErrorMessage(errorMessage);
        taskRecordMapper.insert(taskRecord);

        log.info("记录任务执行明细(执行失败)到数据库。任务记录:{}", taskRecord);
    }

    @Override
    public void recordFailure(String rawMessage, String errorMessage) {
        TaskRecord taskRecord = new TaskRecord();
        // 使用时间作为主键,便于测试
        taskRecord.setId(LocalDateTime.now().toString());
        taskRecord.setStatus(TaskStatus.FAILURE.getCode());
        taskRecord.setErrorMessage(errorMessage);
        taskRecord.setTaskRawMessage(rawMessage);
        taskRecord.setCreateTime(LocalDateTime.now());
        taskRecordMapper.insert(taskRecord);

        log.info("记录反序列化失败到数据库。任务记录:{}", taskRecord);

    }

    private TaskRecord getTaskRecord(TaskMessage taskMessage) {
        TaskRecord taskRecord = new TaskRecord();
        // 使用时间作为主键,便于测试
        taskRecord.setId(LocalDateTime.now().toString());
        taskRecord.setTaskId(taskMessage.getId());
        taskRecord.setTaskCode(taskMessage.getTaskCode());
        taskRecord.setTaskName(taskMessage.getTaskName());
        taskRecord.setTaskDate(taskMessage.getTaskDate());
        taskRecord.setAccount(taskMessage.getAccount());
        taskRecord.setCreateTime(LocalDateTime.now());
        return taskRecord;
    }
}

TaskMessageParseService

package com.example.hello.activemq.service;

import com.example.hello.activemq.model.TaskMessage;

public interface TaskMessageParseService {
    TaskMessage parse(String message);
}

TaskMessageParseServiceImpl

package com.example.hello.activemq.service.impl;

import com.example.hello.activemq.model.TaskMessage;
import com.example.hello.activemq.service.TaskMessageParseService;
import com.example.hello.activemq.service.TaskRecordService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 任务消息解析服务(使用JSON反序列化)
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TaskMessageParseServiceImpl implements TaskMessageParseService {
    private final ObjectMapper objectMapper;
    private final TaskRecordService taskRecordService;

    /**
     * 解析消息内容(JSON字符串)为任务消息对象
     */
    @Override
    public TaskMessage parse(String message) {
        try {
            return objectMapper.readValue(message, TaskMessage.class);
        } catch (JsonProcessingException e) {
            // 反序列化失败,记录原始消息到数据库
            log.info("反序列化失败。原始消息:{}。异常详细信息(含堆栈):", message, e);
            taskRecordService.recordFailure(message, e.getMessage());
            return null;
        }
    }

}

TaskRecordMapper

package com.example.hello.activemq.mapper;

import com.example.hello.activemq.model.TaskRecord;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface TaskRecordMapper {

    int insert(TaskRecord taskRecord);

}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.hello.activemq.mapper.TaskRecordMapper">

    <insert id="insert" parameterType="com.example.hello.activemq.model.TaskRecord">
        INSERT INTO task_record (
            id,
            status,
            error_message,
            task_id,
            task_code,
            task_name,
            task_date,
            account,
            task_raw_message,
            create_time
        )
        VALUES (
            #{id},
            #{status},
            #{errorMessage},
            #{taskId},
            #{taskCode},
            #{taskName},
            #{taskDate},
            #{account},
            #{taskRawMessage},
            #{createTime}
        )
    </insert>

</mapper>

实体类

TaskMessage

package com.example.hello.activemq.model;

import lombok.Data;

import java.time.LocalDate;

/**
 * 任务消息
 */
@Data
public class TaskMessage {

    /**
     * 主键(雪花算法)
     */
    private String id;

    /**
     * 任务编号
     */
    private String taskCode;

    /**
     * 任务名称
     */
    private String taskName;

    /**
     * 任务日期
     */
    private LocalDate taskDate;

    /**
     * 账号
     */
    private String account;

}

TaskRecord

package com.example.hello.activemq.model;

import lombok.Data;

import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 任务记录
 */
@Data
public class TaskRecord {

    /**
     * 主键(雪花算法)
     */
    private String id;

    /**
     * 状态:1=成功,0=失败
     */
    private Integer status;

    /**
     * 错误信息:任务执行失败时记录错误信息
     */
    private String errorMessage;

    /**
     * 任务ID(雪花算法)
     */
    private String taskId;

    /**
     * 任务编号
     */
    private String taskCode;

    /**
     * 任务名称
     */
    private String taskName;

    /**
     * 任务日期
     */
    private LocalDate taskDate;

    /**
     * 账号
     */
    private String account;

    /**
     * 任务原始消息
     */
    private String taskRawMessage;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

}

枚举类

TaskStatus

package com.example.hello.activemq.enumeration;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 任务状态枚举
 */
@Getter
@AllArgsConstructor
public enum TaskStatus {

    SUCCESS(1, "成功"),
    FAILURE(0, "失败");

    private final Integer code;
    private final String description;

}

JSON:消息内容字符串

{
  "id": "1234567890123456789",
  "taskCode": "123456-20250101-20250101083000",
  "taskName": "任务-123456-20250101-20250101083000",
  "taskDate": "2025-01-01",
  "account": "123456"
}

DDL:任务记录表

CREATE TABLE task_record (
    id VARCHAR(64) NOT NULL COMMENT '主键(雪花算法)',
    status TINYINT NOT NULL COMMENT '状态:1=成功,0=失败',
    error_message TEXT COMMENT '错误信息',
    task_id VARCHAR(64) COMMENT '任务ID(雪花算法)',
    task_code VARCHAR(255) COMMENT '任务编号',
    task_name VARCHAR(255) COMMENT '任务名称',
    task_date DATE COMMENT '任务日期',
    account VARCHAR(255) COMMENT '账号',
    task_raw_message TEXT COMMENT '任务原始消息',
    create_time DATETIME NOT NULL COMMENT '创建时间',
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='任务记录';

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.4</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>hello-activemq</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello-activemq</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>21</java.version>
    </properties>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

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

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.4</version>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

项目启动状态

ActiveMQ启动成功

MySQL启动成功

应用启动成功


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.4.4)

2025-04-19T16:37:58.154+08:00  INFO 9584 --- [hello-activemq] [           main] c.e.h.activemq.HelloActivemqApplication  : Starting HelloActivemqApplication using Java 21.0.1 with PID 9584 (E:\hello-world\hello-activemq\target\classes started by SongGuanxun in E:\hello-world\hello-activemq)
2025-04-19T16:37:58.170+08:00  INFO 9584 --- [hello-activemq] [           main] c.e.h.activemq.HelloActivemqApplication  : No active profile set, falling back to 1 default profile: "default"
2025-04-19T16:38:03.830+08:00  INFO 9584 --- [hello-activemq] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2025-04-19T16:38:03.902+08:00  INFO 9584 --- [hello-activemq] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2025-04-19T16:38:03.904+08:00  INFO 9584 --- [hello-activemq] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.39]
2025-04-19T16:38:04.093+08:00  INFO 9584 --- [hello-activemq] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2025-04-19T16:38:04.098+08:00  INFO 9584 --- [hello-activemq] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 5662 ms
2025-04-19T16:38:07.997+08:00  INFO 9584 --- [hello-activemq] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2025-04-19T16:38:08.738+08:00  INFO 9584 --- [hello-activemq] [           main] c.e.h.activemq.HelloActivemqApplication  : Started HelloActivemqApplication in 12.669 seconds (process running for 15.55)

三、场景测试与验证方案

测试一:业务执行成功

通过ActiveMQ的控制面板,发送符合任务执行成功条件的消息字符串。消费者在收到消息后,正常执行业务逻辑,然后将任务执行成功的状态记录到指定的任务记录表中。

在整个接收消息、执行任务和记录任务成功状态的过程中,不会有报错。

发送消息

消息消费正常

日志打印

2025-04-19T21:41:47.509+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息处理开始
2025-04-19T21:41:47.535+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息内容(字符串): {
  "id": "1234567890123456789",
  "taskCode": "123456-20250101-20250101083000",
  "taskName": "任务-123456-20250101-20250101083000",
  "taskDate": "2025-01-01",
  "account": "123456"
}
2025-04-19T21:41:48.070+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 解析消息内容为任务消息对象: TaskMessage(id=1234567890123456789, taskCode=123456-20250101-20250101083000, taskName=任务-123456-20250101-20250101083000, taskDate=2025-01-01, account=123456)
2025-04-19T21:41:48.079+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.service.impl.TaskServiceImpl     : 业务逻辑正常执行
2025-04-19T21:41:48.269+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2025-04-19T21:41:50.545+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@64c25740
2025-04-19T21:41:50.566+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2025-04-19T21:41:50.893+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.s.impl.TaskRecordServiceImpl     : 记录任务执行明细(执行成功)到数据库。任务记录:TaskRecord(id=2025-04-19T21:41:48.080351700, status=1, errorMessage=null, taskId=1234567890123456789, taskCode=123456-20250101-20250101083000, taskName=任务-123456-20250101-20250101083000, taskDate=2025-01-01, account=123456, taskRawMessage=null, createTime=2025-04-19T21:41:48.080351700)
2025-04-19T21:41:50.908+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息处理结束

数据库记录

测试二:业务执行失败

模拟业务执行失败,当不符合业务条件时,没有通过业务校验,导致抛出业务异常。此时,不会触发重试,消息会被 try-catch 捕获,然后将任务执行失败的信息记录到指定的 任务记录表 中。

发送消息

{
  "id": "1234567890123456789",
  "taskCode": "123456-20241231-20250101083000",
  "taskName": "任务-123456-20241231-20250101083000",
  "taskDate": "2024-12-31",
  "account": "123456"
}

消息消费正常

日志打印

2025-04-19T23:51:51.229+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息处理开始
2025-04-19T23:51:51.231+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息内容(字符串): {
  "id": "1234567890123456789",
  "taskCode": "123456-20241231-20250101083000",
  "taskName": "任务-123456-20241231-20250101083000",
  "taskDate": "2024-12-31",
  "account": "123456"
}
2025-04-19T23:51:51.248+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 解析消息内容为任务消息对象: TaskMessage(id=1234567890123456789, taskCode=123456-20241231-20250101083000, taskName=任务-123456-20241231-20250101083000, taskDate=2024-12-31, account=123456)
2025-04-19T23:51:51.253+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 任务执行失败:

java.lang.RuntimeException: 【任务日期】小于【开始日期】,任务日期=2024-12-31,开始日期=2025-01-01
	at com.example.hello.activemq.service.impl.TaskServiceImpl.processTask(TaskServiceImpl.java:24) ~[classes/:na]
	at com.example.hello.activemq.consumer.TaskQueueConsumer.processTask(TaskQueueConsumer.java:50) ~[classes/:na]
	at com.example.hello.activemq.consumer.TaskQueueConsumer.receive(TaskQueueConsumer.java:42) ~[classes/:na]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:169) ~[spring-messaging-6.2.5.jar:6.2.5]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:119) ~[spring-messaging-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:110) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:84) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:790) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:747) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:725) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:333) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:270) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1420) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1410) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1287) ~[spring-jms-6.2.5.jar:6.2.5]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

2025-04-19T23:51:51.458+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.s.impl.TaskRecordServiceImpl     : 记录任务执行明细(执行失败)到数据库。任务记录:TaskRecord(id=2025-04-19T23:51:51.370400600, status=0, errorMessage=【任务日期】小于【开始日期】,任务日期=2024-12-31,开始日期=2025-01-01, taskId=1234567890123456789, taskCode=123456-20241231-20250101083000, taskName=任务-123456-20241231-20250101083000, taskDate=2024-12-31, account=123456, taskRawMessage=null, createTime=2025-04-19T23:51:51.370400600)
2025-04-19T23:51:51.460+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息处理结束

数据库记录

测试三:JSON消息字符串解析异常

模拟在消费方法中,出现非业务执行失败,比如消息字符串无法正常解析为Java对象,导致抛出运行时异常。此时,不会触发重试,消息会被 try-catch 捕获,然后将任务执行失败的信息记录到指定的 任务记录表 中。

发送消息

消息消费正常

日志打印

2025-04-20T00:04:12.512+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息处理开始
2025-04-20T00:04:12.513+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息内容(字符串): Enter some text here for the message body...
2025-04-20T00:04:12.520+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.s.i.TaskMessageParseServiceImpl  : 反序列化失败。原始消息:Enter some text here for the message body...。异常详细信息(含堆栈):

com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'Enter': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 6]
	at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584) ~[jackson-core-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610) ~[jackson-core-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2618) ~[jackson-core-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:825) ~[jackson-core-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.java:3017) ~[jackson-core-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleOddValue(ReaderBasedJsonParser.java:2051) ~[jackson-core-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:780) ~[jackson-core-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:5018) ~[jackson-databind-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4921) ~[jackson-databind-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3868) ~[jackson-databind-2.18.3.jar:2.18.3]
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3836) ~[jackson-databind-2.18.3.jar:2.18.3]
	at com.example.hello.activemq.service.impl.TaskMessageParseServiceImpl.parse(TaskMessageParseServiceImpl.java:28) ~[classes/:na]
	at com.example.hello.activemq.consumer.TaskQueueConsumer.receive(TaskQueueConsumer.java:33) ~[classes/:na]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:169) ~[spring-messaging-6.2.5.jar:6.2.5]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:119) ~[spring-messaging-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:110) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:84) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:790) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:747) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:725) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:333) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:270) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1420) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1410) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1287) ~[spring-jms-6.2.5.jar:6.2.5]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

2025-04-20T00:04:12.567+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.s.impl.TaskRecordServiceImpl     : 记录反序列化失败到数据库。任务记录:TaskRecord(id=2025-04-20T00:04:12.537009, status=0, errorMessage=Unrecognized token 'Enter': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 6], taskId=null, taskCode=null, taskName=null, taskDate=null, account=null, taskRawMessage=Enter some text here for the message body..., createTime=2025-04-20T00:04:12.537995400)
2025-04-20T00:04:12.568+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 解析消息内容为任务消息对象: null
2025-04-20T00:04:12.569+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息处理结束:任务消息解析失败,提前返回

数据库记录

在这里插入图片描述

测试四:记录任务数据库写入异常

模拟在消费方法中,出现数据库异常(比如,数据库连接异常),导致数据库写入失败,不会触发重试。消息会被 try-catch 捕获,打印对应日志。

注意,记录任务执行明细(执行失败)到数据库 这段代码必须要使用 try-catch 处理。否则,一旦出现数据库异常时,写入数据库失败,会将异常向外抛出到 消息监听容器 ,导致触发重试。

            try {
                // 业务处理失败,记录任务执行明细(执行失败)到数据库
                taskRecordService.recordFailure(taskMessage, e.getMessage());
            } catch (Exception ex) {
                log.error("记录任务失败时发生异常:", ex);
            }

模拟数据库宕机

模拟数据库宕机,关闭MySQL数据库。

发送消息

消息消费正常

日志打印

2025-04-20T00:25:02.943+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息处理开始
2025-04-20T00:25:02.944+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息内容(字符串): {
  "id": "1234567890123456789",
  "taskCode": "123456-20250101-20250101083000",
  "taskName": "任务-123456-20250101-20250101083000",
  "taskDate": "2025-01-01",
  "account": "123456"
}
2025-04-20T00:25:02.946+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 解析消息内容为任务消息对象: TaskMessage(id=1234567890123456789, taskCode=123456-20250101-20250101083000, taskName=任务-123456-20250101-20250101083000, taskDate=2025-01-01, account=123456)
2025-04-20T00:25:02.946+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.service.impl.TaskServiceImpl     : 业务逻辑正常执行
2025-04-20T00:25:03.244+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@73c43b25 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:03.257+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@52ec7b7 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:03.273+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@15222032 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:03.285+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@47ed2ca0 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:03.292+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@2afec909 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:03.300+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@599eb09e (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:03.307+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@5db9382f (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:03.318+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@739f8714 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:03.322+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@2f75beef (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:03.331+08:00  WARN 3876 --- [hello-activemq] [ntContainer#1-1] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@343e1df (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
2025-04-20T00:25:33.023+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 任务执行失败:

org.mybatis.spring.MyBatisSystemException: 
### Error updating database.  Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
### The error may exist in file [E:\hello-world\hello-activemq\target\classes\mapper\TaskRecordMapper.xml]
### The error may involve com.example.hello.activemq.mapper.TaskRecordMapper.insert
### The error occurred while executing an update
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:99) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:347) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at jdk.proxy2/jdk.proxy2.$Proxy61.insert(Unknown Source) ~[na:na]
	at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:224) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:62) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:141) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86) ~[mybatis-3.5.17.jar:3.5.17]
	at jdk.proxy2/jdk.proxy2.$Proxy62.insert(Unknown Source) ~[na:na]
	at com.example.hello.activemq.service.impl.TaskRecordServiceImpl.recordSuccess(TaskRecordServiceImpl.java:25) ~[classes/:na]
	at com.example.hello.activemq.consumer.TaskQueueConsumer.processTask(TaskQueueConsumer.java:53) ~[classes/:na]
	at com.example.hello.activemq.consumer.TaskQueueConsumer.receive(TaskQueueConsumer.java:42) ~[classes/:na]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:169) ~[spring-messaging-6.2.5.jar:6.2.5]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:119) ~[spring-messaging-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:110) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:84) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:790) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:747) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:725) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:333) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:270) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1420) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1410) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1287) ~[spring-jms-6.2.5.jar:6.2.5]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
### The error may exist in file [E:\hello-world\hello-activemq\target\classes\mapper\TaskRecordMapper.xml]
### The error may involve com.example.hello.activemq.mapper.TaskRecordMapper.insert
### The error occurred while executing an update
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:199) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:184) ~[mybatis-3.5.17.jar:3.5.17]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:333) ~[mybatis-spring-3.0.4.jar:3.0.4]
	... 24 common frames omitted
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
	at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:84) ~[spring-jdbc-6.2.5.jar:6.2.5]
	at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:77) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:64) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:348) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:89) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197) ~[mybatis-3.5.17.jar:3.5.17]
	... 28 common frames omitted
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30009ms (total=0, active=0, idle=0, waiting=0)
	at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:686) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:179) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:144) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:127) ~[HikariCP-5.1.0.jar:na]
	at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:160) ~[spring-jdbc-6.2.5.jar:6.2.5]
	at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:118) ~[spring-jdbc-6.2.5.jar:6.2.5]
	at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:81) ~[spring-jdbc-6.2.5.jar:6.2.5]
	... 36 common frames omitted
Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
	at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:165) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:55) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:837) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:420) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:238) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:180) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:724) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:703) ~[HikariCP-5.1.0.jar:na]
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
	... 1 common frames omitted
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) ~[na:na]
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) ~[na:na]
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486) ~[na:na]
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:52) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:95) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:140) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:156) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:79) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.NativeSession.connect(NativeSession.java:142) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:961) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:825) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	... 13 common frames omitted
Caused by: java.net.ConnectException: Connection refused: no further information
	at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682) ~[na:na]
	at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542) ~[na:na]
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592) ~[na:na]
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) ~[na:na]
	at java.base/java.net.Socket.connect(Socket.java:751) ~[na:na]
	at com.mysql.cj.protocol.StandardSocketFactory.connect(StandardSocketFactory.java:144) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:53) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	... 16 common frames omitted

2025-04-20T00:26:03.051+08:00 ERROR 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 记录任务失败时发生异常:

org.mybatis.spring.MyBatisSystemException: 
### Error updating database.  Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
### The error may exist in file [E:\hello-world\hello-activemq\target\classes\mapper\TaskRecordMapper.xml]
### The error may involve com.example.hello.activemq.mapper.TaskRecordMapper.insert
### The error occurred while executing an update
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:99) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:347) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at jdk.proxy2/jdk.proxy2.$Proxy61.insert(Unknown Source) ~[na:na]
	at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:224) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:62) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:141) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86) ~[mybatis-3.5.17.jar:3.5.17]
	at jdk.proxy2/jdk.proxy2.$Proxy62.insert(Unknown Source) ~[na:na]
	at com.example.hello.activemq.service.impl.TaskRecordServiceImpl.recordFailure(TaskRecordServiceImpl.java:35) ~[classes/:na]
	at com.example.hello.activemq.consumer.TaskQueueConsumer.processTask(TaskQueueConsumer.java:60) ~[classes/:na]
	at com.example.hello.activemq.consumer.TaskQueueConsumer.receive(TaskQueueConsumer.java:42) ~[classes/:na]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:169) ~[spring-messaging-6.2.5.jar:6.2.5]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:119) ~[spring-messaging-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:110) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:84) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:790) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:747) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:725) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:333) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:270) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1420) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1410) ~[spring-jms-6.2.5.jar:6.2.5]
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1287) ~[spring-jms-6.2.5.jar:6.2.5]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
### The error may exist in file [E:\hello-world\hello-activemq\target\classes\mapper\TaskRecordMapper.xml]
### The error may involve com.example.hello.activemq.mapper.TaskRecordMapper.insert
### The error occurred while executing an update
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:199) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:184) ~[mybatis-3.5.17.jar:3.5.17]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:333) ~[mybatis-spring-3.0.4.jar:3.0.4]
	... 24 common frames omitted
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection
	at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:84) ~[spring-jdbc-6.2.5.jar:6.2.5]
	at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:77) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:64) ~[mybatis-spring-3.0.4.jar:3.0.4]
	at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:348) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:89) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76) ~[mybatis-3.5.17.jar:3.5.17]
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197) ~[mybatis-3.5.17.jar:3.5.17]
	... 28 common frames omitted
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30010ms (total=0, active=0, idle=0, waiting=0)
	at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:686) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:179) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:144) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:127) ~[HikariCP-5.1.0.jar:na]
	at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:160) ~[spring-jdbc-6.2.5.jar:6.2.5]
	at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:118) ~[spring-jdbc-6.2.5.jar:6.2.5]
	at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:81) ~[spring-jdbc-6.2.5.jar:6.2.5]
	... 36 common frames omitted
Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
	at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:165) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:55) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:837) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:420) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:238) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:180) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:724) ~[HikariCP-5.1.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:703) ~[HikariCP-5.1.0.jar:na]
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
	... 1 common frames omitted
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) ~[na:na]
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) ~[na:na]
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486) ~[na:na]
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:52) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:95) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:140) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:156) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:79) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.NativeSession.connect(NativeSession.java:142) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:961) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:825) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	... 13 common frames omitted
Caused by: java.net.ConnectException: Connection refused: no further information
	at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na]
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682) ~[na:na]
	at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542) ~[na:na]
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592) ~[na:na]
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) ~[na:na]
	at java.base/java.net.Socket.connect(Socket.java:751) ~[na:na]
	at com.mysql.cj.protocol.StandardSocketFactory.connect(StandardSocketFactory.java:144) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:53) ~[mysql-connector-j-9.1.0.jar:9.1.0]
	... 16 common frames omitted

2025-04-20T00:26:03.059+08:00  INFO 3876 --- [hello-activemq] [ntContainer#1-1] c.e.h.a.consumer.TaskQueueConsumer       : 消息处理结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋冠巡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值