1. 背景
发布版本的时候存在如下情况导致调用失败
时刻 | 动作 |
---|---|
T1 | 服务A从nacos获取服务B实例1 |
T2 | 服务A负载均衡选择到实例1 |
T3 | 服务B实例1下线 |
T4 | 服务A调用服务B 实例1 |
2. 解决方法
1. 确保服务A拿到的实例一定是正常的
- 服务B实例先从nacos下线。
- 等待数秒后服务A知晓服务B实例下线。
- 服务A调用服务B正常的实例。
2. 重试
3. 核心代码
代码仓库nacos-deregister-instance-actuator
1. 定义Endpoint
package com.daxiyan.nacos;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
@Endpoint(id = "nacos-deregister-instance")
public class NacosDeRegisterInstanceEndpoint {
public static Logger logger = LoggerFactory.getLogger(NacosDeRegisterInstanceEndpoint.class);
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
public NacosDeRegisterInstanceEndpoint(NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
}
@ReadOperation
public String deregisterInstance() {
String serviceName = nacosDiscoveryProperties.getService();
String groupName = nacosDiscoveryProperties.getGroup();
String clusterName = nacosDiscoveryProperties.getClusterName();
String ip = nacosDiscoveryProperties.getIp();
int port = nacosDiscoveryProperties.getPort();
logger.info("deregister from nacos, serviceName:{}, groupName:{}, clusterName:{}, ip:{}, port:{}", serviceName, groupName, clusterName, ip, port);
try {
nacosDiscoveryProperties.namingServiceInstance().deregisterInstance(serviceName, groupName, ip, port, clusterName);
} catch (NacosException e) {
logger.error("deregister from nacos error", e);
return "error";
}
return "success";
}
}
2. 自动装配类
package com.daxiyan.nacos;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
@ConditionalOnClass(Endpoint.class)
@ConditionalOnBean(type = "com.alibaba.cloud.nacos.NacosDiscoveryProperties")
public class NacosDeRegisterInstanceEndpointAutoConfiguration {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@ConditionalOnMissingBean
@Bean
public NacosDeRegisterInstanceEndpoint nacosDeRegisterEndpoint() {
return new NacosDeRegisterInstanceEndpoint(nacosDiscoveryProperties);
}
}
3. META-INT/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.daxiyan.nacos.NacosDeRegisterInstanceEndpointAutoConfiguration
4.使用
本地安装jar
mvn install
maven pom添加依赖
maven pom.xml
<dependency>
<groupId>com.daxiyan.nacos</groupId>
<artifactId>nacos-deregister-instance-actuator</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
引入冲突解决
排序依赖
<dependency>
<groupId>com.daxiyan.nacos</groupId>
<artifactId>nacos-deregister-instance-actuator</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</exclusion>
</exclusions>
</dependency>
触发
bootstrap.yml 配置
#监控配置
management:
endpoints:
web:
base-path: "/actuator"
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
server:
port: 9090
curl http://localhost:{management.server.port}/actuator/nacos-deregister-instance
其他
搜索spring cloud应该提供这种通用的服务下线功能,后来的确看到了
org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
package org.springframework.cloud.client.serviceregistry.endpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
/**
* Endpoint to display and set the service instance status using the ServiceRegistry.
*
* @author Spencer Gibb
*/
@SuppressWarnings("unchecked")
@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));
}
}
调用
curl -i -H "Content-Type: application/json" -X POST -d '{"status":"DOWN"}' http://localhost:9090/actuator/service-registry