微服务(Nacos、eureka、consul)优雅上下线方案汇总
点击勘误issues (opens new window),哪吒感谢大家的阅读
# 微服务(Nacos、eureka、consul)优雅上下线方案汇总
# 前提
必须是集群,多个实例
# 优雅的本质
一个服务有多个(如A、B 两个实例)实例提供服务时,客户端请求服务时会访问此服务的多个实例,当进行A实例进行部署时将流量切换到B实例,然后再关闭或重新发布 ;如何实现这个功能呢?
所以本质就是模拟人工在控制台触发“下线”功能(此实例还是提供服务),过一会儿等流量全部切到其他实例再部署&Kill实例;可以理解为此实例说:“我即将要先下线了,请访问另外实例”。
# 为什么要过一会儿?
可能客户端缓存的服务提供的实例Ip列表导致无效的请求或服务挂掉后瞬间的访问不到服务的情况
在部署脚本里面植入1、下线脚本;2 增加缓冲时间(如60s),默认Nacos客户端缓存实例列表是30s;
# 方案1 注册中心自带的方法
我们使用的这个方式,简单无需配置,默认支持;
下线
curl -X PUT "http://nacos服务ip:port/nacos/v1/ns/instance?serviceName=服务名&ip=172.25.135.221&port=8667&namespaceId=preprod&weight=0&enabled=false"
上线
curl -X PUT "http:/nacos服务ip:port/nacos/v1/ns/instance?serviceName=服务名&ip=172.25.135.221&port=8667&namespaceId=preprod&weight=1&enabled=true"
# 方案2 基于/service-registry端点
如果应用支持Spring Cloud部署那就更好了。Spring Cloud提供了/service-registry端点。但从名字就可以知道专门针对服务注册实现的一个端点。
在配置文件中开启/service-registry端点:
management:
endpoints:
web:
exposure:
include: service-registry
base-path: /actuator
endpoint:
serviceregistry:
enabled: true
访问http://localhost:8667/actuator 端点可以查看到开启了如下端点:
{
"_links": {
"self": {
"href": "http://localhost:8667/actuator",
"templated": false
},
"serviceregistry": {
"href": "http://localhost:8667/actuator/service-registry",
"templated": false
}
}
}
通过curl命令来进行服务状态的修改:上线是UP,下线是DOWN
curl -X "POST" "http://172.25.129.191:8667/actuator/service-registry?status=DOWN" -H "Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8"
执行上述命令之前,查看Nacos对应实例状态为:
比方案1 多了一个步骤 需要配置一下开启一下才行;
其本质就是如下代码,去关闭了Nacos控制的状态
@Endpoint(id = "service-registry")
public class ServiceRegistryEndpoint {
private final ServiceRegistry serviceRegistry;
private Registration registration;
public ServiceRegistryEndpoint(ServiceRegistry<?> serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
public void setRegistration(Registration registration) {
this.registration = registration;
}
@WriteOperation
public ResponseEntity<?> setStatus(String status) {
Assert.notNull(status, "status may not by null");
if (this.registration == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body("no registration found");
}
this.serviceRegistry.setStatus(this.registration, status);
return ResponseEntity.ok().build();
}
@ReadOperation
public ResponseEntity getStatus() {
if (this.registration == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body("no registration found");
}
return ResponseEntity.ok()
.body(this.serviceRegistry.getStatus(this.registration));
}
}
调用的Endpoint便是通过上面代码实现的。所以不仅Nacos,只要基于Spring Cloud集成的注册中心,本质上都是支持这种方式的服务下线的。
# 方案3 个性化定义下线方式,本质还是调用方案2的
不需要配置,实现钩子方法即可
在实例启动的时默认都会进行注册实例到注册中心,如下官方实现方式:NacosServiceRegistryAutoConfiguration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled",
matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class,
NacosDiscoveryAutoConfiguration.class })
public class NacosServiceRegistryAutoConfiguration {
@Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration(
NacosDiscoveryProperties nacosDiscoveryProperties,
ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context);
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
NacosServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
}
你只需要知道如何拿到注册实例即可,默认就可以获取,实现自己的对外接口即可
@Resource
private NacosServiceRegistry nacosServiceRegistry;
@Resource
private NacosRegistration nacosRegistration;
@GetMapping(value = "/api/nacos/{status}")
public String deregisterInstanceStatus(@PathVariable String status) {
if (!status.equalsIgnoreCase("UP") && !status.equalsIgnoreCase("DOWN")) {
log.warn("can't support status {},please choose UP or DOWN", status);
return "please choose UP or DOWN,can't support status: "+status;
}
try {
nacosServiceRegistry.setStatus(nacosRegistration,status);
} catch (Exception e) {
log.error(" deregisterInstanceStatus nacos error", e);
return "error:"+e.getMessage();
}
return "success";
}