SpringCloud升级手册-服务发现延迟问题记录(Load balancer does not contain an instance for the service)
系统升级过程中出现Nacos服务注册失败,表现为Feign调用报503错误。问题分析发现是服务发现延迟导致,通过以下措施解决:1.将码表初始化从@PostConstruct改为CommandLineRunner异步执行,避免阻塞主线程;2.配置线程池优化异步任务处理;3.调整Feign超时时间设置。总结建议定期检查依赖版本兼容性,并参考官方文档进行配置。该案例展示了在微服务升级过程中如何解决服务发
目录
一、问题概述
对系统进行升级时发生了失败。具体问题表现为在升级过程中,nacos注册中心部分服务注册失败,启动日志报错:
feign.FeignException$ServiceUnavailable: [503] during [POST],
Load balancer does not contain an instance for the service XXXXX ,
No servers available for service: XXXXX,
系统无法正常启动,失败的服务经过手动重启一次或多次,可以成功启动,因存在问题,进行了版本回退,最终导致整个升级过程未能完成。

升级技术清单
|
序号 |
类型 |
版本现状 |
新版本选择 |
|
1 |
SpringBoot |
2.3.2.RELEASE |
2.7.18 |
|
2 |
SpringCloud |
Hoxton.SR9 |
2021.0.9 |
|
3 |
SpringCloudAlibaba |
2.2.6.RELEASE |
2021.0.1.0 |
|
4 |
nacos-client |
1.4.2 |
2021.0.1.0内嵌1.4.2 |
|
5 |
Nacos 服务端 |
2.2.3 |
不变 |
二、问题分析与解决
问题排查过程中,排除了架构版本依赖关系不兼容、版本冲突等问题,问题可能在系统启动调用微服务获取码表的过程中,存在一定的服务发现延迟问题,即对码表初始化方法调用的时机进行了调整和相关配置调整。
1.码表初始化方法调用的时机调整
- 使用CommandLineRunner 代替@PostConstruct
- 加入异步任务@Async,不阻塞主线程
- 对注册中心服务和调用服务实例进行有效性检测
2.设置Feign超时时间
1.码表初始化方法调用的时机调整
调整调用时机
@Component
public class CommandLineRunnerEx implements CommandLineRunner {
private final ServiceDiscoveryUtil serviceDiscoveryUtil;
// 使用构造函数注入 serviceDiscoveryUtil
@Autowired
public CommandLineRunnerEx(ServiceDiscoveryUtil serviceDiscoveryUtil) {
this.serviceDiscoveryUtil= serviceDiscoveryUtil;
}
/**
* 服务检测
* 加入异步任务,使用 @Async 注解后,Spring 会在异步线程中执行 run() 方法,因此不会阻塞主线程或启动过程。
*/
@Async
@Override
public void run(String... args) throws Exception {
while (!serviceDiscoveryUtil.isServiceAvailable(“SHOP”)&&!serviceDiscoveryUtil.hasInstances(“SHOP”)) {
log.warn("等待SHOP 服务可用...");
try {
TimeUnit.SECONDS.sleep(5); // 每5秒检查一次服务,直到服务可用。
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 处理中断
break; // 如果线程被中断,退出循环
}
}
init();
}
/**
* 初始化字典组件。
* 注释@PostConstruct注解,改为应用启动后初始化码表,实现CommandLineRunner接口
* 把码表初始化代码放在run方法中执行
*/
// @PostConstruct //1在构造函数执行完之后执行
public static void init() {
//获取码表,调用SHOP相关服务。。。
}
}
线程池设置
/**
* @description: 线程池设置
*/
@Configuration
public class ThreadPoolConfig {
/**
* 使用 Spring 管理线程池的生命周期,单例
*/
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);// 核心线程池大小,如果任务量频繁且需要快速响应,适当调整核心线程以减少线程创建开销。
executor.setMaxPoolSize(10);// 最大线程池大小
executor.setQueueCapacity(500); // 队列容量,默认有界队列LinkedBlockingQueue
executor.setThreadNamePrefix("消费帮扶ADMIN服务线程-"); // 线程名称前缀
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); //拒绝策略,当任务数量超过队列容量时,直接抛出异常
executor.setKeepAliveSeconds(60);//60秒后空闲的非核心线程会被销毁
executor.setAllowCoreThreadTimeOut(true);//核心线程在空闲时间超过 keepAliveTime 后是否可以被销毁。
//executor.setPrestartAllCoreThreads(false);//是否在创建 ThreadPoolTaskExecutor 时立即启动所有核心线程
executor.initialize(); // // 初始化线程池
log.info("--服务线程- executor init--");
return executor;
}
}
服务检测
@Component
public class ServiceDiscoveryUtil {
//服务发现,获取服务的实例列表及元数据。
@Autowired
private DiscoveryClient discoveryClient;
//服务调用时的负载均衡,选择一个实例进行调用
@Autowired
private LoadBalancerClient loadBalancerClient;
/**
* Spring Cloud 提供了 LoadBalancerClient 接口,可以用来通过负载均衡器获取注册到 Nacos 上的服务信息。可以在代码中使用它来获取服务实例的信息。需要引入spring-cloud-starter-loadbalancer依赖
*/
public boolean hasInstances(String serviceId) {
ServiceInstance instances = loadBalancerClient.choose(serviceId);
if (instances != null) {
System.out.println("可调用服务实例信息: " +serviceId+":"+ instances.getHost() + ":" + instances.getPort());
return true;
} else {
System.out.println("没有"+serviceId+"服务实例可调用!");
return false;
}
}
//检查服务是否可用
public boolean isServiceAvailable(String serviceId) {
List<ServiceInstance> instances = discoveryClient.getInstances(serviceId); // 获取所有该服务的实例
if (instances != null && !instances.isEmpty()) {
System.out.println("注册中心服务实例名:"+serviceId+",个数:"+instances.size());
return true;
}else{
System.out.println("注册中心没有发现服务实例:"+serviceId);
return false;
}
}
}
2.Feign超时时间设置
Spring Cloud 2020.0.0后续版本删除掉了Netflix除Eureka外的所有组件,Netflix组件替代方案:
|
Netflix |
推荐替代品 |
|
Hystrix |
Resilience4j |
|
Hystrix Dashboard / Turbine |
Micrometer + Monitoring System |
|
Ribbon |
Spring Cloud Loadbalancer |
|
Zuul 1 |
Spring Cloud Gateway |
|
Archaius 1 |
Spring Boot外部化配置 + Spring Cloud配置 |
负载均衡Ribbon被替换成了Spring Cloud Loadbalancer,所以之前的Ribbon相关配置理论是失效的。

而Feign的默认超时时间是1秒修改超时时间。

三、总结
定期检查项目中的依赖版本,并与官方文档保持同步,确保项目使用的版本始终是经过充分验证的兼容版本。
spring-cloud-alibaba版本说明:
版本说明 · alibaba/spring-cloud-alibaba Wiki · GitHub
SpringCloud与SpringBoot版本说明:
能提供思路的技术文档:
Spring Cloud Feign启动Load balancer does not have available server for client分析-CSDN博客
扒一扒Nacos、OpenFeign、Ribbon、loadbalancer组件协调工作的原理_openfeign loadbalancer-CSDN博客
Spring Cloud 2020.0.0发布 再见了Netflix_spring cloud 哪个版本移除了netflix-CSDN博客
更多推荐


所有评论(0)