📋 问题描述
现象
- 问题:8个新添加的字段在页面刷新后时有时无
- 环境:本地测试正常,服务器部署后出现问题
- 特点:后端API直接测试正常,通过网关访问时出现间歇性问题
技术栈
- 后端:Spring Boot + Spring Cloud + Nacos
- 网关:Spring Cloud Gateway
- 服务发现:Nacos Discovery
- ORM:MyBatis-Plus
- 序列化:Jackson
🔍 排查过程
阶段一:怀疑后端代码问题
1. 检查 MyBatis-Plus 字段映射
尝试:
- 检查
@TableField注解 - 检查 MyBatis-Plus 全局字段策略配置
- 添加
call-setters-on-nulls: true配置
结果:❌ 不是原因
2. 检查 Jackson 序列化配置
尝试:
java
// 在实体类上添加注解
@JsonInclude(JsonInclude.Include.ALWAYS)
public class Shops implements Serializable {
// ...
}
yaml
# application.yml
spring:
jackson:
default-property-inclusion: always
serialization:
write-null-map-values: true
deserialization:
fail-on-unknown-properties: false
遇到的问题:
- 初始配置中使用了无效的配置项
write-nulls-as-null: true,导致启动失败 - 修复后配置正确,但问题仍然存在
结果:❌ 配置正确,但问题仍存在
3. 检查 JAR 包打包问题
发现的问题:
xml
<!-- pom.xml 缺少 spring-boot-maven-plugin -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
错误信息:
no main manifest attribute, in chqm-0.0.1-SNAPSHOT.jar
修复:添加插件后重新打包
结果:✅ JAR包正常,但问题仍存在
阶段二:发现环境差异
4. 直接测试后端 API
测试命令:
bash
# 在服务器上直接调用API
curl -X GET "http://localhost:9090/shops/xxxx"
结果:✅ API响应包含所有8个字段
结论:后端代码和配置都是正确的,问题不在后端
5. 检查 Nacos 配置
发现:
yaml
spring:
config:
import:
- nacos:chqm-server.yaml
怀疑:Nacos配置可能覆盖了本地配置
结果:需要进一步检查
阶段三:定位根本原因 🎯
6. 检查 Nacos 服务实例
关键发现:
bash
# 检查Nacos中的服务实例
curl -s "http://xxxx.xxxx.xxxx.xxxx:8848/nacos/v1/ns/instance/list?serviceName=chqm-server&namespaceId=public"
结果 :发现Nacos中注册了3个 chqm-server 实例:
xxxx.xxxx.xxxx.xxxx:9090❌ 旧版本xxxx.xxxx.xxxx.xxxx:9090❌ 旧版本xxxx.xxxx.xxxx.xxxx:9090✅ 最新版本
网关配置:
yaml
spring:
cloud:
gateway:
routes:
- id: chqm_route
uri: lb://xxxx-server # 负载均衡
predicates:
- Path=/xxxx/**
问题根源:
- 网关使用
lb://xxxx-server进行负载均衡 - 请求被随机分发到3个实例
- 部分实例是旧版本,不包含新字段
- 导致刷新时可能路由到不同实例,字段时有时无
✅ 解决方案
步骤1:停止其他服务器上的服务
bash
# 在服务器A上
pkill -9 -f xxxx-0.0.1-SNAPSHOT.jar
# 在服务器B上
pkill -9 -f xxxx-0.0.1-SNAPSHOT.jar
步骤2:从 Nacos 注销多余实例
bash
# 等待ephemeral实例自动注销(心跳超时15-30秒)
# 或手动注销
curl -X DELETE "http://xxxx.xxxx.xxxx.xxxx:8848/nacos/v1/ns/instance?serviceName=xxxx-server&ip=xxxx.xxxx.xxxx.xxxx&port=9090&namespaceId=public&ephemeral=true" -u xxxx:xxxx
curl -X DELETE "http://xxxx.xxxx.xxxx.xxxx:8848/nacos/v1/ns/instance?serviceName=xxxx-server&ip=xxxx.xxxx.xxxx.xxxx&port=9090&namespaceId=public&ephemeral=true" -u xxxx:xxxx
步骤3:验证结果
bash
# 检查实例列表(应该只剩下最新版本的实例)
curl -s "http://xxxx.xxxx.xxxx.xxxx:8848/nacos/v1/ns/instance/list?serviceName=xxxx-server&namespaceId=public" | grep -o '"ip":"[^"]*"' | sort | uniq
最终结果:
- ✅ Nacos中只剩下最新版本的实例
- ✅ 所有请求都路由到最新版本的服务
- ✅ 前端刷新后,8个字段稳定出现
💡 技术要点总结
1. 微服务负载均衡机制
yaml
# Spring Cloud Gateway 使用 lb:// 进行负载均衡
uri: lb://xxxx-server
lb://表示使用负载均衡- 请求会被分发到所有注册的实例
- 如果实例版本不一致,会导致行为不一致
2. Nacos 服务注册机制
- Ephemeral 实例:服务停止后会自动注销(心跳超时)
- 健康检查:不健康的实例会被标记,但可能仍会被路由
- 实例管理:可以通过API手动注销或禁用实例
3. 版本一致性的重要性
- 多实例部署时,必须确保所有实例版本一致
- 新版本部署时,应该先停止旧版本,再启动新版本
- 或者使用灰度发布策略,逐步替换实例
🛡️ 预防措施
1. 部署前检查清单
- 检查所有服务器上的服务版本是否一致
- 检查Nacos中的服务实例数量
- 确认所有实例都是最新版本
2. 监控和告警
- 定期检查Nacos中的服务实例数量
- 监控服务版本信息
- 设置告警,当实例数量异常时通知
3. 部署策略
- 蓝绿部署:先部署新版本,验证后切换流量
- 灰度发布:逐步替换实例,降低风险
- 版本标签:使用版本标签区分不同版本
4. 健康检查
- 确保负载均衡只路由到健康的实例
- 配置合理的健康检查间隔
- 及时下线不健康的实例
🔗 相关资源
📝 后记
这个问题从发现到解决,经历了从代码排查到环境排查的完整过程。最终发现是微服务架构中常见的版本一致性问题。这个案例提醒我们:
- 不要忽视环境差异:本地正常不代表生产环境正常
- 系统化排查很重要:按照逻辑顺序排查,避免盲目尝试
- 微服务架构需要更细致的运维:版本管理、实例监控都很重要
希望这个案例能帮助遇到类似问题的开发者快速定位和解决问题!
注意 :本文档中的IP地址、服务器信息等敏感信息已用
xxxx替代,实际使用时请替换为真实信息。