VortMall 微服务故障排查手册

8
类别: 
生产部署

VortMall 微服务故障排查手册

适用范围:VortMall-Api 全部 15 个业务微服务、网关、认证服务及公共组件。
技术栈:Java 21 / Spring Boot 4.0 / Spring Cloud 2025.1 / Spring Cloud Alibaba 2025.1 / MyBatis-Plus 3.5 / RocketMQ / Nacos / Seata / Redis / Elasticsearch / XXL-JOB


目录


一、服务启动失败

1.1 端口占用

现象:启动报 Address already in use: bind

排查

# Windows
netstat -ano | findstr :<端口号>
taskkill /PID <进程ID> /F

# Linux
lsof -i :<端口号>
kill -9 <PID>

各服务默认端口对照

服务端口
vortmall-gateway8000
vortmall-auth8001
vortmall-biz-product19901
vortmall-biz-payment19902
vortmall-biz-system19903
vortmall-biz-marketing19904
vortmall-biz-order19905
vortmall-biz-content19906
vortmall-biz-organize19907
vortmall-biz-logistics19908
vortmall-biz-aftersales19909
vortmall-biz-distribution19910
vortmall-biz-decoration19911
vortmall-biz-message19912
vortmall-biz-user19913
vortmall-biz-file19914
vortmall-biz-pos19915

1.2 Bean 创建失败

现象BeanCreationExceptionUnsatisfiedDependencyException

常见原因与解决

原因解决方案
缺少依赖包检查 pom.xml 是否引入对应 starter,执行 mvn dependency:tree 排查冲突
@Mapper 未扫描到确认 Mapper 接口在 com.vortmall.*.infrastructure 包下,或检查 @MapperScan 配置
条件装配不满足检查 @ConditionalOnProperty 对应的配置项(如 vortmall.outbox.enabled)是否设置
循环依赖Spring Boot 4.x 默认禁止循环依赖,使用 @Lazy 或重构解耦

1.3 配置文件加载失败

现象ConfigDataLocationNotFoundExceptionCould not resolve placeholder

排查步骤

  1. 确认 bootstrap.ymlspring.config.import 路径正确:
spring:
  config:
    import:
      - classpath:config.yml      # Nacos 公共配置(必须)
      - classpath:datasource.yml  # 数据源
      - classpath:cache.yml       # Redis 缓存
      - classpath:mq.yml          # RocketMQ(按需)
      - classpath:seata.yml       # Seata(按需)
      - classpath:job.yml         # XXL-JOB(按需)
  1. 确认 env.ymlvortmall.host 地址可达
  2. 检查 Nacos 配置中心中对应的 dataId 是否存在,namespace 是否与部署环境一致

1.4 Java 版本不匹配

现象UnsupportedClassVersionError--enable-preview 相关报错

解决:项目要求 Java 21,确认 JAVA_HOME 指向 JDK 21,且 IDE 编译级别设置为 21。


二、Nacos 注册与配置异常

2.1 服务注册失败

现象NacosException: failed to req API、服务列表中看不到实例

排查

  1. 确认 Nacos Server 已启动:curl http://<vortmall.host>:8848/nacos/
  2. 检查 config.yml 中配置:
    • server-addr 地址与实际 Nacos 服务地址一致
    • namespace 与 Nacos 控制台中使用的命名空间一致
  3. 检查网络连通性(防火墙、VPN)
  4. 确认服务没有被 spring.cloud.nacos.discovery.enabled: false 禁用

2.2 配置拉取失败

现象:启动时配置值为空或使用了默认值

排查

  1. 登录 Nacos 控制台 → 配置管理 → 确认当前命名空间正确
  2. 检查 dataIdgroup 是否匹配
  3. 共享配置(如 datasource-multiple.yml)是否已创建
  4. 检查 Nacos 客户端日志:logs/nacos/config.log

2.3 配置热更新不生效

现象:修改了 Nacos 配置但服务未感知

排查

  1. 确认使用了 @RefreshScope@ConfigurationProperties
  2. 检查是否是在 static 或构造函数中读取的配置(不支持动态刷新)
  3. 确认 Nacos 配置发布后 MD5 已变更

三、数据库连接异常

3.1 连接池初始化失败

现象DruidDataSource init errorCommunications link failure

排查

  1. 确认 MySQL 服务已启动且端口可达(默认 3306)
  2. 检查数据源配置(datasource.yml 或 Nacos 中的配置):
    • url 格式:jdbc:mysql://<host>:3306/<db-name>?...
    • 用户名/密码正确
  3. 检查数据库是否存在(各服务对应的库名见下表)

各服务数据库名对照

服务数据库名
vortmall-biz-productvortmall-product
vortmall-biz-ordervortmall-order
vortmall-biz-uservortmall-user
vortmall-biz-paymentvortmall-payment
vortmall-biz-systemvortmall-system
vortmall-biz-marketingvortmall-marketing
vortmall-biz-contentvortmall-content
vortmall-biz-messagevortmall-message
vortmall-biz-aftersalesvortmall-aftersales
vortmall-biz-decorationvortmall-decoration
vortmall-biz-distributionvortmall-distribution
vortmall-biz-organizevortmall-organize
vortmall-biz-logisticsvortmall-logistics
vortmall-biz-filevortmall-file
vortmall-biz-posvortmall-pos

3.2 连接池耗尽

现象GetConnectionTimeoutExceptionCannot get a connection, pool error Timeout waiting for idle object

排查

  1. 检查 Druid 监控(如已开启):/druid/datasource.html
  2. 排查慢 SQL:开启 Druid 的 stat 过滤器
  3. 排查连接泄漏:检查是否有未关闭的手动 Connection
  4. 临时方案:增大 maxActive(默认 20),但需同步排查根因
spring:
  datasource:
    druid:
      max-active: 50
      max-wait: 60000

3.3 慢 SQL 导致超时

现象:接口响应慢、QueryTimeoutException

排查

  1. 开启 MyBatis-Plus SQL 日志:
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  1. 检查是否缺少索引:EXPLAIN SELECT ...
  2. 检查是否存在全表扫描、N+1 查询
  3. 使用 Druid 的 wallstat 过滤器监控

3.4 MyBatis-Plus 常见问题

问题解决方案
Invalid bound statement检查 XML 文件路径与 Mapper 接口包路径一致,确认 mapper-locations 配置
Table 'xxx' doesn't exist检查实体类 @TableName 注解与实际表名是否一致
字段映射异常检查 @TableField 注解、驼峰转换配置 map-underscore-to-camel-case

四、Redis 缓存异常

4.1 连接失败

现象RedisConnectionFailureExceptionUnable to connect to Redis

排查

  1. 确认 Redis 已启动:redis-cli -h <host> -p 6379 ping
  2. 检查 cache.yml 中的连接配置
  3. 检查密码是否正确
  4. 检查 Redis 最大连接数:redis-cli info clients

4.2 缓存穿透 / 击穿 / 雪崩

问题现象解决
缓存穿透大量查询不存在的数据,请求打到数据库缓存空值、布隆过滤器
缓存击穿热点 key 过期瞬间大量请求涌入使用 vortmall-common-lock 分布式锁保护加载逻辑
缓存雪崩大量 key 同时过期TTL 添加随机偏移量

4.3 Redisson 分布式锁问题

现象vortmall-common-lock 获取锁超时或死锁

排查

  1. 检查 Redis 连接是否正常
  2. 检查锁的 leaseTime 是否过短(业务未执行完锁已释放)
  3. 检查是否有异常导致锁未正确释放
  4. 通过 redis-cli 查看锁 key 状态:GET <lock-key>

4.4 Sa-Token 会话 Redis 异常

现象:登录后立即失效、NotLoginException

排查

  1. 确认网关和业务服务使用的 Redis 是同一实例
  2. 检查 sa-token.token-name 配置一致性
  3. 检查 Redis key 前缀是否冲突

五、消息队列(RocketMQ)异常

5.1 Producer 发送失败

现象RemotingTooMuchRequestExceptionMQBrokerException: No route info of this topic

排查

  1. 确认 RocketMQ NameServer 已启动:${vortmall.host}:9876
  2. 确认 Broker 已注册到 NameServer
  3. 检查 Topic 是否已创建(生产环境建议关闭自动创建):
# 查看 Topic 列表
mqadmin topicList -n <nameserver>:9876
# 创建 Topic
mqadmin updateTopic -n <nameserver>:9876 -b <broker>:10911 -t <TOPIC_NAME>
  1. 检查 mq.ymlname-server 地址是否正确

5.2 Consumer 消费失败

现象:消息重复消费、消费延迟

排查

  1. 检查消费者 Consumer<T> Function Bean 是否正确注册
  2. 检查 group 配置(同一 group 下的实例会负载均衡)
  3. 检查消费逻辑是否抛出异常导致重试:
    • RocketMQ 默认重试 16 次
    • 确保消费逻辑幂等(利用 MessageBody.identifier
  4. 查看消费积压:
mqadmin consumerProgress -n <nameserver>:9876 -g <消费者组>

5.3 消息积压

现象:消费者消费速度跟不上生产者

排查与处理

  1. 确认积压量:通过 RocketMQ Dashboard 或命令查看各 Queue 的消费偏移量
  2. 扩容消费者:增加消费者实例数(不能超过 Queue 数量)
  3. 排查消费瓶颈
    • 单条消息处理耗时过长 → 优化业务逻辑或异步化
    • 数据库/外部 API 慢 → 排查下游瓶颈
    • 消费异常重试 → 检查错误日志
  4. 临时方案:调整消费线程数
spring:
  cloud:
    stream:
      rocketmq:
        bindings:
          <binding-name>:
            consumer:
              maxConcurrency: 20

六、Outbox 可靠消息发送异常

6.1 消息滞留在 Outbox 表中

现象outbox_message 表中大量 PENDING 状态记录

排查

  1. 检查 XXL-JOB 中对应的 Outbox 扫描任务是否在运行
  2. 检查 AbstractOutBoxJobHandler 的日志输出
  3. 确认 StreamProducervortmall-common-mq)可用
  4. 检查 vortmall.outbox.enabled 是否为 true

6.2 Outbox 消息发送后状态异常

现象:消息已发到 MQ 但 Outbox 表状态仍为 PENDING

排查

  1. 检查 OutBoxServiceImpl.processAndSend 的事务提交是否正常
  2. 检查数据库连接是否在发送 MQ 后断开
  3. 查看 maxRetry 配置(vortmall.outbox.defaultMaxRetry

6.3 Outbox 表数据清理

正常情况下,成功发送的 Outbox 记录会被定时清理(retentionDays 控制保留天数)。如果表数据膨胀:

  1. 确认清理任务在 XXL-JOB 中已配置并正常运行
  2. 手动清理:
DELETE FROM outbox_message
WHERE status = 'SUCCESS'
  AND update_time < DATE_SUB(NOW(), INTERVAL 7 DAY);

七、网关路由异常

7.1 503 Service Unavailable

现象:网关返回 503,LoadBalancerNotFoundException

排查

  1. 确认目标服务已注册到 Nacos(Nacos 控制台 → 服务列表)
  2. 确认网关 application.yml 中路由配置的 uri 与服务注册名一致:lb://vortmall-biz-product
  3. 检查网关和业务服务是否注册在同一 Nacos 命名空间中

7.2 CORS 跨域问题

现象:前端报 Access-Control-Allow-Origin 错误

排查

  1. 检查网关 CorsConfig 配置
  2. 确认没有重复设置 CORS 头(网关已配 DedupeResponseHeader 过滤器)
  3. 如果业务服务也设置了 CORS,会导致响应头重复 → 业务服务不应单独配置 CORS

7.3 请求超时

现象:网关返回 504 Gateway Timeout

排查

  1. 确认下游服务响应时间
  2. 调整网关超时配置:
spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 10000
        response-timeout: 30s

7.4 请求体过大

现象:上传文件时报 DataBufferLimitException: Exceeded limit on max bytes to buffer

解决:网关已配置 max-in-memory-size: 20MB,如需更大:

spring:
  codec:
    max-in-memory-size: 50MB

八、Feign 服务间调用异常

8.1 调用超时

现象ReadTimeoutExceptionfeign.RetryableException

排查

  1. 确认被调用服务是否健康
  2. 调整 Feign 超时配置:
spring:
  cloud:
    openfeign:
      client:
        config:
          default:
            connectTimeout: 10000
            readTimeout: 30000

8.2 Fallback 未触发

现象:Feign 调用失败直接抛异常,未执行 Fallback

排查

  1. 确认 spring.cloud.openfeign.circuitbreaker.enabled: true
  2. 确认 Resilience4j 相关依赖已引入(spring-cloud-starter-circuitbreaker-resilience4j
  3. 确认 FeignClient 正确设置了 fallbackfallbackFactory

8.3 服务发现找不到实例

现象Load balancer does not contain an instance for the service

排查

  1. 确认被调用服务的 spring.application.name 与 FeignClient 中 name / value 一致
  2. 确认调用方和被调用方注册在同一 Nacos 命名空间和 group 下
  3. 确认被调用服务实例健康(Nacos → 服务详情 → 查看实例状态)

8.4 序列化/反序列化异常

现象HttpMessageConversionException、字段丢失

排查

  1. 确认请求和响应 DTO 字段一致(注意 Feign 模块中定义的 DTO 与业务模块中可能不同步)
  2. 本项目使用 fastjson2,检查是否有 Jackson 注解混用导致的行为差异
  3. 日期格式、枚举序列化方式是否一致

九、Seata 分布式事务异常

9.1 Seata Server 连接失败

现象RegistryExceptioncan not connect to seata-server

排查

  1. 确认 Seata Server 已启动
  2. 检查 seata.yml 配置:
    • registry.type: nacos
    • Nacos 地址:${vortmall.host}:8848
    • namespace:seata_server
  3. 在 Nacos 控制台确认 Seata Server 已注册(切换到 seata_server namespace)

9.2 全局事务回滚失败

现象:部分分支事务未回滚、数据不一致

排查

  1. 检查 Seata Server 日志
  2. 确认 undo_log 表存在于各业务数据库中
  3. 确认 enable-auto-data-source-proxy: true 生效
  4. 排查分支事务是否有修改了全局锁范围外的数据

9.3 事务超时

现象GlobalTransactionException: Could not found global transaction

排查

  1. 排查事务执行时间是否过长
  2. 调整 Seata 超时配置
  3. 考虑将长耗时操作拆出事务(如发送 MQ、调用外部 API)

9.4 使用注意

  • @GlobalTransactional 标注在 Application Service 层
  • 避免在事务中做耗时 I/O(HTTP 调用、文件上传)
  • tx-service-group 必须各服务一致(当前统一为 default_tx_group

十、XXL-JOB 定时任务异常

10.1 执行器注册失败

现象:XXL-JOB Admin 中看不到执行器

排查

  1. 确认 XXL-JOB Admin 已启动:http://<vortmall.host>:8082/xxl-job-admin
  2. 检查 job.yml 配置:
    • adminAddresses 地址正确
    • accessToken 与 Admin 端一致
    • appName 格式:${spring.application.name}-executor
  3. 检查执行器端口(默认随机 9900-9999)是否被占用或被防火墙拦截

10.2 任务执行失败

现象:XXL-JOB Admin 中任务状态为失败

排查

  1. 在 Admin 中查看任务执行日志(调度日志 → 执行日志)
  2. 检查服务端日志(logs/xxl-job/jobhandler/ 目录)
  3. 确认 @XxlJob("handlerName") 中的名称与 Admin 端任务配置一致

十一、Elasticsearch 搜索异常

11.1 连接失败

现象ElasticsearchExceptionConnection refused

排查

  1. 确认 ES 集群已启动(默认端口 9200)
  2. 检查 es.yml 中的连接配置
  3. 确认 ES 版本兼容性(项目使用 8.17.2 客户端)

11.2 索引不存在

现象index_not_found_exception

排查

  1. 确认索引是否已创建:curl <es-host>:9200/_cat/indices?v
  2. 检查索引名是否与业务代码中定义的一致
  3. 手动创建索引或等待数据同步任务创建

11.3 数据同步延迟

现象:数据库已更新但搜索结果未变

排查

  1. 检查 Canal binlog 消费是否正常(vortmall-biz-product 使用 Stream 消费 Canal 事件)
  2. ES 默认 refresh 间隔 1s,可手动刷新:POST /<index>/_refresh
  3. 排查同步消费者日志

11.4 通过 Elasticsearch 查看系统日志

系统运行日志通过 Logstash 采集后写入 Elasticsearch,可通过以下方式查看和检索。

查看集群健康状态

curl <es-host>:9200/_cluster/health?pretty

查看所有日志索引

curl <es-host>:9200/_cat/indices?v&s=index

日志索引通常按日期命名,格式如 vortmall-log-YYYY.MM.DD

按关键字搜索日志

# 搜索包含 "Exception" 的日志(最近 100 条)
curl -X GET "<es-host>:9200/vortmall-log-*/_search?pretty" \
  -H "Content-Type: application/json" \
  -d '{
    "size": 100,
    "sort": [{"@timestamp": "desc"}],
    "query": {
      "match": {
        "message": "Exception"
      }
    }
  }'

按服务名过滤日志

# 查看指定服务的错误日志
curl -X GET "<es-host>:9200/vortmall-log-*/_search?pretty" \
  -H "Content-Type: application/json" \
  -d '{
    "size": 50,
    "sort": [{"@timestamp": "desc"}],
    "query": {
      "bool": {
        "must": [
          {"match": {"app_name": "vortmall-biz-order"}},
          {"match": {"level": "ERROR"}}
        ]
      }
    }
  }'

按时间范围查询日志

# 查询最近 1 小时内的错误日志
curl -X GET "<es-host>:9200/vortmall-log-*/_search?pretty" \
  -H "Content-Type: application/json" \
  -d '{
    "size": 50,
    "sort": [{"@timestamp": "desc"}],
    "query": {
      "bool": {
        "must": [
          {"match": {"level": "ERROR"}}
        ],
        "filter": [
          {"range": {"@timestamp": {"gte": "now-1h", "lte": "now"}}}
        ]
      }
    }
  }'

通过 Kibana 查看日志(如果已部署):

  1. 访问 Kibana:http://<kibana-host>:5601
  2. 进入 Discover 页面
  3. 选择日志索引模式(如 vortmall-log-*
  4. 使用 KQL 语法搜索,示例:
    • 搜索错误日志:level: "ERROR"
    • 按服务过滤:app_name: "vortmall-biz-order" AND level: "ERROR"
    • 搜索特定异常:message: "NullPointerException"
    • 组合条件:app_name: "vortmall-biz-payment" AND message: "timeout" AND @timestamp >= "2026-03-27"

日志清理(磁盘空间不足时):

# 删除 30 天前的日志索引
curl -X DELETE "<es-host>:9200/vortmall-log-2026.02.*"

# 查看索引占用磁盘空间
curl "<es-host>:9200/_cat/indices/vortmall-log-*?v&s=store.size:desc&h=index,store.size"

十二、Sa-Token 认证授权异常

12.1 Token 无效或过期

现象NotLoginException: Token无效

排查

  1. 检查 Token 是否已过期(sa-token.timeout 配置)
  2. 确认 Token 是否通过正确的 Header 传递(检查 sa-token.token-name
  3. 确认 Redis 中 Token 对应的会话数据存在

12.2 网关与服务 Token 不互通

现象:网关校验通过但服务端拿不到登录信息

排查

  1. 确认网关使用 sa-token-reactor-spring-boot3-starter(响应式)
  2. 确认业务服务使用 sa-token-spring-boot3-starter(Servlet)
  3. 确认两端连接同一 Redis、使用相同 Token 配置
  4. 检查网关 AuthContextFilter 是否正确传递了用户上下文

12.3 权限不足

现象NotPermissionExceptionNotRoleException

排查

  1. 检查用户角色权限数据是否正确
  2. 确认接口上的权限注解(@SaCheckPermission)与权限数据匹配
  3. 检查权限缓存是否过期需要刷新

附录:故障处理流程

发现异常
  │
  ├─ 服务不可用? ──→ 检查 Nacos 注册状态 → 检查服务进程 → 查看启动日志
  │
  ├─ 接口报错? ──→ 查看网关日志 → 定位目标服务 → 查看服务错误日志
  │
  ├─ 数据异常? ──→ 检查数据库直连 → 检查缓存一致性 → 检查 MQ 消费状态
  │
  ├─ 性能问题? ──→ 慢 SQL 排查 → 连接池状态 → MQ 积压 → ES 日志检索
  │
  └─ 分布式事务? ──→ Seata Server 日志 → undo_log 表 → 各分支事务状态
评论 0
/ 1000
0
0
收藏