Java面试之微服务架构下服务发现与治理

服务发现失败时,eureka.client.fetch-registry和register-with-eureka必须配对检查,因二者是协作关系:前者控制是否拉取服务列表,后者控制是否注册自身,错配将导致注册失败或无法发现服务。

服务发现失败时,eureka.client.fetch-registryeureka.client.register-with-eureka 为什么必须配对检查

微服务启动后注册不到 Eureka Server,或无法拉取服务列表,八成是这两个布尔配置没对齐。它们不是独立开关,而是协作关系:fetch-registry 控制是否从 Server 拉取服务列表(影响 DiscoveryClient 的可用实例),register-with-eureka 控制是否把自己注册上去。

常见错误场景:

  • 网关服务设了 register-with-eureka: false(合理,不希望被其他服务调用),但忘了关 fetch-registry: true(必须开,否则找不到下游服务)
  • Eureka Server 自身也启用了 client,默认会尝试注册自己——若未在 eureka.client.service-url.defaultZone 中指向其他节点,就会报 Cannot execute request on any known server
  • 本地调试时禁用了注册(register-with-eureka: false),却仍依赖 @LoadBalanced RestTemplate,结果 ServiceInstanceListSupplier 返回空列表,调用直接抛 IllegalStateException: No instances available

@LoadBalanced RestTemplate 在 Spring Cloud 2025+ 之后为什么经常失效

Spring Cloud 2025.0.0(即 Ilford 版本)起,RestTemplate 的负载均衡能力不再由 LoadBalancerAutoConfiguration 全自动装配,而是依赖 BlockingLoadBalancerClient + ServiceInstanceListSupplier,且默认只支持 Reactor 环境下的 WebClient。若项目没显式引入 spring-cloud-starter-loadbalancer@LoadBalanced 注解将静默失效——不会报错,但请求始终发往 localhost:8080。

修复方式:

  • 确认依赖中存在 spring-cloud-starter-loadbalancer(不是 spring-cloud-starter-netflix-ribbon,Ribbon 已废弃)
  • 检查是否误删了 @Bean 定义:旧版常写 @Bean @LoadBalanced RestTemplate restTemplate(),新版仍需保留该 Bean 声明,否则自动配置不触发
  • 若使用 OpenFeign,要确保 spring-cloud-starter-openfeign 版本 ≥ 3.0.0,且 feign.loadbalancer.enabled=true(默认为 true,但显式声明更稳妥)

Consul 作为注册中心时,spring.cloud.consul.discovery.health-check-path 配置不对会导致服务持续被下线

Consul 默认通过 HTTP GET 请求服务的 /actuator/health 端点判断健康状态。如果 Spring Boot Actuator 版本 ≥ 3.x,/actuator/health 默认返回 200,但 Consul 发起的请求头不含 Accept: application/vnd.spring-boot.actuator.v3+json,导致响应体为空或 406 —— Consul 将其视为健康检查失败,反复踢出服务。

解决路径只有两条:

  • 降级 Actuator 版本(不推荐)
  • 改用兼容路径:在 application.yml 中显式指定 spring.cloud.consul.discovery.health-check-path: /actuator/health/showdetails(需配合 Actuator 3.x 的 management.endpoint.health.show-details=always
  • 或更彻底:关闭 Consul 自动健康检查,改用 TTL 模式——设置 spring.cloud.consul.discovery.health-check-ttl: 30s,并在服务内定时调用 ConsulClient#passTTL

Nacos 2.x 中 nacos.naming.cache.dir 不生效的真正原因

这个配置项在 Nacos 2.x 客户端(com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2.2.10-RC1 及以上)中已被完全忽略。Nacos 客户端内部改用内存缓存 + 本地磁盘映射(~/.nacos/naming/ 下的 JSON 文件),但该路径由客户端硬编码决定,不受 Spring Boot 配置控制。

如果你看到日志里反复出现 fail to update cache from remote server,或服务列表更新延迟明显,并非缓存目录问题,而是:

  • 客户端长轮询超时时间过短:nacos.discovery.watch-delay 默认 30000ms,但在高并发注册场景下可能被压垮,建议调大至 60000
  • Nacos Server 启用了鉴权但客户端未配 nacos.username/nacos.password,导致

    心跳请求 403,服务被误判下线
  • 服务名含下划线(如 user_service)——Nacos 2.x 默认开启严格模式,会拒绝注册,需在 Server 端配置 nacos.naming.validation.pattern=^[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9]$ 放宽限制

本地缓存位置实际固定为 ${user.home}/.nacos/naming/,强行修改系统属性 nacos.naming.cache.dir 不会改变行为,只会让日志输出误导性路径。