需要处理的问题
- 至少有一个服务可用
K8S 配置滚动部署策略 - 服务下线后不再被调度
- 服务关闭时主动下线 nacos
- 服务关闭时清理应用里的 loadbalance 实例列表缓存
- 之前进来的请求可以返回
延迟下线,最大可能保证功能结束
业务服务配置 nacos 优雅停机
优雅停机配置与最大等待时间
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 40s
cloud:
loadbalancer:
# 负载均衡缓存配置
cache:
enabled: true
ttl: 10s
监听器代码
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@Component
@Slf4j
public class NacosShutdownEvent implements ApplicationListener<ContextClosedEvent>, PriorityOrdered {
@Resource
private NacosServiceManager nacosServiceManager;
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@SneakyThrows
@Override
public void onApplicationEvent(ContextClosedEvent event) {
// 获取服务实例
NamingService namingService = nacosServiceManager.getNamingService();
String service = nacosDiscoveryProperties.getService();
List<Instance> instances = namingService.getAllInstances(service);
String ip = nacosDiscoveryProperties.getIp();
log.info("nacosDiscoveryPropertiesIP:{}", ip);
for (Instance instance : instances) {
String instanceIp = instance.getIp();
log.info("instanceIp,{}", instanceIp);
if (instanceIp.equals(nacosDiscoveryProperties.getIp())) {
namingService.deregisterInstance(service, instance);
log.info("deregisterInstance service,{},{}", service, ip);
}
}
Thread.sleep(25 * 1000);
log.info("end stop nacos");
}
@Override
public int getOrder() {
return 0;
}
}
网关
在网关配置过期时间
spring:
cloud:
loadbalancer:
# 负载均衡缓存配置
cache:
enabled: true
ttl: 10s
测试
使用jemeter 持续请求,观察在部署期间请求不报错
K8S
terminationGracePeriodSeconds:60s 这里的时间要大于springboot 里的睡眠时间
k8s 有停机前的处理步骤,可以在那里配置下线 + nacos 与睡眠等待,我采用的是springboot 监听器的方案,便于开发与观察。
其它
同样的方法,编写其他中间件,比如 kafka、xxl 的优雅停机逻辑,并且调整顺序,越小优先执行。
可以在关闭前打印当前活跃的 tomcat 容器中的线程数与队列,用于评估等待时间,待完善。
版本:
springboot 2.6.13
nacos 2.2.0