使用undertow,服务停止后nacos下线注销延迟问题
- 1.场景描述
- 2.解决方案
- 2.1web服务器切换为tomcat
- 2.2做一个自己的注销补偿
- 2.3使用nacos内置注销(推荐)
整体问题是使用gateway作为网关时,服务下线之后。网关依然会转发到下线服务器上。这篇是解决了服务这端的问题,另一篇:spring cloud gateway+nacos 服务下线感知延迟,请求依然转发到下线服务是解决了网关端的问题。
1.场景描述
不太清楚是版本问题还是哪里配置。只是记录一下这次的问题。
nacos客户端:1.4.1
web服务器:undertow 版本 2.2.
服务停止后,nacos管理端查看服务未及时注销。
web服务器切换为tomcat,服务下线正常注销。
大概原因是因为nacos执行注销时需要的一个bean已经被先行销毁。导致注销失败。控制台有报错信息
2022-04-25 11:48:39.431 ERROR 21336 --- [extShutdownHook] : namingService unsubscribe failed, properties:NacosDiscoveryProperties{serverAddr='localhost:8848', endpoint='', namespace='dong-dev', watchDelay=30000, logName='', service='dong-sys-server-biz', weight=1.0, clusterName='DEFAULT', group='DEFAULT_GROUP', namingLoadCacheAtStart='false', metadata={=[ "dubbo://10.47.17.177:20880/?anyhost=true&application=dong-sys-server-biz&=10.47.17.177&=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=dong-sys-server-biz&interface=&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedURLs&pid=21336&=false&release=2.7.14&revision=2.2.&=ServiceBean:dong-sys-server-biz/:1.0.0&side=provider&timeout=13000×tamp=1650858511190&version=1.0.0" ], =0, =SPRING_CLOUD, S-Version=10.47.17.177}, registerEnabled=true, ip='10.47.17.177', networkInterface='', port=8082, secure=false, accessKey='', secretKey='', heartBeatInterval=null, heartBeatTimeout=null, ipDeleteTimeout=null}
: null
at (:430) ~[undertow-servlet-2.2.:2.2.]
at (:41) ~[spring-web-5.3.:5.3.3]
at (:130) ~[spring-context-support-1.0.:na]
at (:103) ~[spring-context-support-1.0.:na]
at (:57) ~[spring-context-support-1.0.:na]
at (:616) ~[spring-cloud-starter-alibaba-nacos-discovery-2021.:2021.1]
at (:610) ~[spring-cloud-starter-alibaba-nacos-discovery-2021.:2021.1]
at (:165) [spring-cloud-starter-alibaba-nacos-discovery-2021.:2021.1]
at (:97) [spring-cloud-starter-alibaba-nacos-discovery-2021.:2021.1]
at (:234) [spring-context-5.3.:5.3.3]
at $300(:54) [spring-context-5.3.:5.3.3]
at $(:373) [spring-context-5.3.:5.3.3]
at (:206) [spring-context-5.3.:5.3.3]
at (:129) [spring-context-5.3.:5.3.3]
at (:1072) [spring-context-5.3.:5.3.3]
at (:171) [spring-boot-2.4.:2.4.2]
at $(:996) [spring-context-5.3.:5.3.3]
2.解决方案
2.1web服务器切换为tomcat
直接替换,问题解决
2.2做一个自己的注销补偿
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* nacos注册中心补偿
* 执行#destroy()销毁之前,
* 可能#stop()已经被执行。
* 导致#getInitParameterNames发生NPE,从而无法正常从注册中心下线。
*
*/
@Slf4j
@Component
public class SelfNacosDiscovery {
@Value("${:public}")
private String namespaceId;
private String clusterName = "DEFAULT";
@Value("${:}")
private String serviceName;
@Value("${:8080}")
private String port;
@Value("${-addr:}")
private String nacosAddr;
@Autowired
private InetUtils inetUtils;
/**
* 补偿注销
* #deregisterService(, )
*@param
*@return
*/
public void deregisterService(){
HttpResponse execute = null;
try{
if(StrUtil.isAllNotBlank(namespaceId,serviceName,port,nacosAddr)){
String name = "DEFAULT_GROUP@@"+serviceName;
String ipAddress = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
Map<String, Object> params = new HashMap<String, Object>(8);
params.put("namespaceId", namespaceId);
params.put("serviceName", name);
params.put("clusterName", clusterName);
//
params.put("ip", ipAddress);
params.put("port", port);
params.put("ephemeral", "true");
log.info("nacos补偿注销流程:执行器信息namespaceId={},serviceName={},clusterName={},ip={},port={}",namespaceId,
name,clusterName,ipAddress,port);
HttpRequest request = HttpUtil.createRequest(Method.DELETE, nacosAddr + "/nacos/v1/ns/instance");
execute = request.form(params).execute();
log.info("nacos补偿注销流程结果:{}",execute.body());
}else{
log.warn("nacos补偿注销流程未执行:参数不全!");
}
}catch (Exception e){
log.error("nacos补偿注销流程 异常",e);
}finally {
if(execute!=null){
execute.close();
}
}
}
}
2.3使用nacos内置注销(推荐)
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Slf4j
@Component
public class SelfNacosDiscovery {
@Resource
private AbstractAutoServiceRegistration abstractAutoServiceRegistration;
@EventListener(ContextClosedEvent.class)
public void doDeregister() {
log.info("nacos补偿注销流程,开始");
try {
abstractAutoServiceRegistration.destroy();
}catch (Exception e){
}
log.info("nacos补偿注销流程,结束");
}
}
20220609更新
2.3的方式发现服务停止会打印异常。如下:
: Error creating bean with name 'selfNacosDiscovery': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
at
....
SelfNacosDiscovery 稍作修改即可:
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Slf4j
@Component
public class SelfNacosDiscovery implements ApplicationListener<ContextClosedEvent>{
@Resource
private AbstractAutoServiceRegistration abstractAutoServiceRegistration;
@EventListener(ContextClosedEvent.class)
public void doDeregister() {
log.info("nacos补偿注销流程,开始");
try {
abstractAutoServiceRegistration.destroy();
}catch (Exception e){
}
log.info("nacos补偿注销流程,结束");
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
doDeregister();
}
}