Spring Cloud Alibaba如何实现服务的无损下线功能
解决方案
1 找到通过负载均衡组件获取可用服务信息的地方

通过一顿Debug之后,从上图中可知,我们获取到可用的服务实例信息,会从缓存中获取,那么如果当服务下线后,我们清空这个服务的缓存的信息,那么下次在获取这个服务的信息就是最新的了,那么问题就解决了。
2 解决思路

服务提供方或消费方,同时监听
Spring Cloud Config中的某个配置文件,比如lossless文件服务下线时, 第一步: 从
nacos上将自身这个实例下线。 第二步: 通过api更新lossless文件中的内容,记录服务下线的服务名加上时间戳。消费者或网关,监听到
lossless配置文件的变更,获取到服务名,然后将这个服务名对应的缓存清空。
部分实现代码
1 引入jar
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config com.alibaba.nacos nacos-spring-context 1.1.1 org.projectlombok lombok org.springframework.cloud spring-cloud-starter-loadbalancer org.springframework.cloud spring-cloud-starter-bootstrap org.springframework.boot spring-boot-starter-web
2 编写服务下线方法
@Component
@Slf4j
public class LosslessOfflineApi {
@Resource
private NacosConfigManager nacosConfigManager;
@Resource
private NacosServiceManager nacosServiceManager;
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;
/**
* 服务下线
*
* @throws NacosException NacosException
*/
public void offlineService() throws NacosException {
log.info("触发服务下线 serviceName:[{}]", nacosDiscoveryProperties.getService());
nacosServiceManager.nacosServiceShutDown();
nacosConfigManager.getConfigService()
.publishConfig(NacosConstant.DATA_ID, NacosConstant.GROUP,
nacosDiscoveryProperties.getService() + NacosConstant.SPLIT + System.currentTimeMillis());
}
}此处需要注意的是: 使用nacosConfigManager.getConfigService().publishConfig 发布配置,此处更新配置中的 服务名+时间戳,如果只是更新服务名,则可能不会触发监听事件。
3 监听配置变更,清除服务缓存
使@NacosConfigListener注解生效
1、引入配置
com.alibaba.nacos nacos-spring-context 1.1.1
2、开启注解
@Configuration
@EnableNacos(globalProperties =
@NacosProperties(serverAddr = "${spring.cloud.nacos.config.server-addr}")
)
public class NacosConfiguration {
}监听配置、清除缓存
@Component
@RequiredArgsConstructor
@Slf4j
public class ListenerConfigChange {
@Resource
private DefaultLoadBalancerCacheManager defaultLoadBalancerCacheManager;
@Resource
private NacosServiceManager nacosServiceManager;
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Resource
private NacosServiceDiscovery nacosServiceDiscovery;
@Resource
private NacosConfigManager nacosConfigManager;
@NacosConfigListener(groupId = GROUP, dataId = DATA_ID)
public void configChange(String config) {
log.warn("==>接收到 无损服务下线 配置变更:[{}]", config);
String serviceName = config.split(SPLIT)[0];
log.info("需要无损下线的服务名:[{}]", serviceName);
Cache cache = defaultLoadBalancerCacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
if (null != cache) {
cache.evict(serviceName);
log.info("失效serviceName:[{}]的缓存", serviceName);
}
}
}实现
1 服务准备
| 服务 | 端口 | 提供api | 备注 |
|---|---|---|---|
| nacos-lossless-gateway | 9001 | /consumer/** | 路由到consumer服务 |
| nacos-feign-consumer | 9002 | /fetchProviderServerInfo | 通过feign接口,获取provider服务的ip和port |
| nacos-provider-9003 | 9003 | /shutdown | 从nacos server上下线服务,发布配置变更 |
| nacos-provider-9004 | 9004 | /shutdown | 从nacos server上下线服务,发布配置变更 |
解释:
1、通过gateway访问 http://localhost:9001/consumer/fetchProviderServerInfo将会返回provider的ip和port信息。
2、http://localhost:9003/shutdown 将会将自己从nacos server下线,并且操作 nacos config,变更配置文件的内容。
3、gateway和consumer监听到配置变更,更新服务的缓存,从而下次访问,不会访问到这个下线的服务。
2 演示
