微服务(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";
    }

  

 

 

参考资源:

https://zhuanlan.zhihu.com/p/586909971  部分可以参考,他是直接注销实例,不是下线

https://blog.csdn.net/agonie201218/article/details/124144873  Nacos本地缓存 PK 微服务优雅下线

https://blog.51cto.com/u_9177933/3013415  eureka、consul的其他注册中心的方案

https://www.zzsucai.com/forum/read-5230.html

https://segmentfault.com/a/1190000043819832#item-3-9  spring boot 2.2.1以上版本的方式

posted @ 2023-10-11 20:05  黎明&岁月  阅读(4114)  评论(0)    收藏  举报