原理:
Nacos没有想eureka一样,在服务端提供API供调用进行服务下线,Nacos的实现方式是通过在客户端提供方法,我们自己封装API进行调用,然后客户端会进行2个步骤:
1,如果是临时服务,客户端会把自己发送续约保活心跳的缓存实例给删除掉。
2,向服务端发起删除服务请求到服务端。
第一步:操作完后,客户端不再向服务端发送心跳续约保活,因此给服务端造成客户端已死现象。
第二步:操作完后,服务端接收到该请求,会将该服务从服务列表删除掉,同时向各健康客户端发布一个change事件,告知服务列表更新了。
这2步操作完成之后,nacos服务端和客户端之间,互不通信。Nacos服务端列表没有该服务健康列表,其他服务无法调用该服务。
服务再次上线方法:
如果后续服务想要再次上线,要么重启服务,要么调用nacos提供的服务注册API,或者自己封装服务注册API。
代码:
1,用户在客户端 自实现服务优雅下线的API
/**
* Nacos 服务列表端的服务实例主动下线
* @return
*/
@RequestMapping("/api/nacos/deRegisterServer")
@GetMapping
public boolean deRegisterServer()
{
Instance instance = new Instance();
instance.setIp(nacosDiscoveryProperties.getIp());
// instance.setPort(port);
instance.setPort(nacosDiscoveryProperties.getPort());
instance.setClusterName(nacosDiscoveryProperties.getClusterName());
instance.setServiceName(nacosDiscoveryProperties.getService());
try {
nacosRegistration.getNacosNamingService().deregisterInstance(nacosDiscoveryProperties.getService(),instance);
return true;
} catch (NacosException e) {
e.printStackTrace();
return false;
}
}
2,Nacos客户端提供的下线方法deregisterInstance
@Override
public void deregisterInstance(String serviceName, Instance instance) throws NacosException {
deregisterInstance(serviceName, Constants.DEFAULT_GROUP, instance);
}
@Override
public void deregisterInstance(String serviceName, String groupName, Instance instance) throws NacosException {
if (instance.isEphemeral()) {
beatReactor.removeBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), instance.getIp(), instance.getPort());
}
serverProxy.deregisterService(NamingUtils.getGroupedName(serviceName, groupName), instance);
}
3,原理中的第一步操作
如果是临时服务,客户端会把自己发送续约保活心跳的缓存实例给删除掉。具体内容如下:
public void removeBeatInfo(String serviceName, String ip, int port) {
NAMING_LOGGER.info("[BEAT] removing beat: {}:{}:{} from beat map.", serviceName, ip, port);
BeatInfo beatInfo = dom2Beat.remove(buildKey(serviceName, ip, port));
if (beatInfo == null) {
return;
}
beatInfo.setStopped(true);
MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}
dom2Beat是一个map集合,里面存放了BeatInfo对象,这个对象封装当前服务实例的ip,port等信息。
/**
* @author nkorange
*/
public class BeatInfo {
private int port;
private String ip;
private double weight;
private String serviceName;
private String cluster;
private Map<String, String> metadata;
private volatile boolean scheduled;
private volatile long period;
private volatile boolean stopped;
4,原理中的第二步
向服务端发起删除服务请求到服务端。具体内容如下
public void deregisterService(String serviceName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}",
namespaceId, serviceName, instance);
final Map<String, String> params = new HashMap<String, String>(8);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, serviceName);
params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
params.put("ip", instance.getIp());
params.put("port", String.valueOf(instance.getPort()));
params.put("ephemeral", String.valueOf(instance.isEphemeral()));
reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, HttpMethod.DELETE);
}
最终执行到如下代码,客户端向服务端发送http请求,执行delete方法。
public String callServer(String api, Map<String, String> params, String curServer, String method)
throws NacosException {
long start = System.currentTimeMillis();
long end = 0;
checkSignature(params);
List<String> headers = builderHeaders();
String url;
if (curServer.startsWith(UtilAndComs.HTTPS) || curServer.startsWith(UtilAndComs.HTTP)) {
url = curServer + api;
} else {
if (!curServer.contains(UtilAndComs.SERVER_ADDR_IP_SPLITER)) {
curServer = curServer + UtilAndComs.SERVER_ADDR_IP_SPLITER + serverPort;
}
url = HttpClient.getPrefix() + curServer + api;
}
HttpClient.HttpResult result = HttpClient.request(url, headers, params, UtilAndComs.ENCODING, method);
end = System.currentTimeMillis();
MetricsMonitor.getNamingRequestMonitor(method, url, String.valueOf(result.code))
.observe(end - start);
if (HttpURLConnection.HTTP_OK == result.code) {
return result.content;
}
if (HttpURLConnection.HTTP_NOT_MODIFIED == result.code) {
return StringUtils.EMPTY;
}
throw new NacosException(NacosException.SERVER_ERROR, "failed to req API:"
+ curServer + api + ". code:"
+ result.code + " msg: " + result.content);
}
服务下线服务后nacos后台管理服务列表:
服务再次上线,个人封装API:
/**
* Nacos 服务列表端的服务实例上线
* @return
*/
@RequestMapping("/api/nacos/rgisterServer")
@GetMapping
public boolean rgisterServer()
{
Instance instance = new Instance();
instance.setIp(nacosDiscoveryProperties.getIp());
// instance.setPort(port);
instance.setPort(nacosDiscoveryProperties.getPort());
instance.setClusterName(nacosDiscoveryProperties.getClusterName());
instance.setServiceName(nacosDiscoveryProperties.getService());
try {
nacosRegistration.getNacosNamingService().registerInstance(nacosDiscoveryProperties.getService(),instance);
return true;
} catch (NacosException e) {
e.printStackTrace();
return false;
}
}
服务上线服务后nacos后台管理服务列表: