/**
*
* <pre>
* 如果通过配置dubbo.service.shutdown.wait=20000(默认10000,10秒)这种方式,
* k8s.terminationGracePeriodSeconds=缩小时间间隔,
* 则会有线上的在途服务突然中断或者其他错误,
*
* 借助k8s的容器层的preStop来做灰度下线,并不影响线上服务,
*
* a. 先让nacos服务主动下线,
* b. 然后等待一段时间再关闭容器(尽量是5秒以上),
* c. 设置nacos的心跳超时(可选)
*
* 主动下线方法中,1调nacos实例下线(no run),2.调nacos的超时(仅在springcloud服务中能和),3.调dubbo的hoop shutdown接口
* 最后选择用3.dubbo的shutdown接口
*
* 不能做到绝对的灰度
*
* </pre>
*
* @param map
* @return 成功标记
*/
public boolean nacosDown(Map map) {
String serviceName = nacosDiscoveryProperties.getService();
String groupName = nacosDiscoveryProperties.getGroup();
String clusterName = nacosDiscoveryProperties.getClusterName();
String server = nacosDiscoveryProperties.getServerAddr();
String ip = nacosDiscoveryProperties.getIp();
int port = nacosDiscoveryProperties.getPort();
log.info("nacosDown deregister from nacos, serviceName:{}, groupName:{}, clusterName:{}, server:{}, ip:{}, port:{} ", serviceName, groupName, clusterName, server, ip, port);
try {
//由于它会比terminationGracePeriodSeconds先执行,所以如果立刻下线的话新的pods还没开好的话,
//a. 先让nacos服务主动下线,
// DubboShutdownHook.destroyAll();
//b. 然后等待一段时间再关闭容器(尽量是5秒以上),
Thread.sleep(waitTime * 1000);
//c. 设置nacos的心跳超时(可选)
nacosSetTimeout(nacosDiscoveryProperties);
log.info("nacosDown sucess deregister from nacos, serviceName:{}, clusterName:{}, ip:{}, port:{}", serviceName, clusterName, ip, port);
} catch (Exception e) {
e.printStackTrace();
}
log.info("nacosDown deregister from nacos, serviceName:{}, groupName:{}, clusterName:{}, server:{}, ip:{}, port:{} ", serviceName, groupName, clusterName, server, ip, port);
return true;
}
/**
* c. 设置nacos的心跳超时(可选)
* @param nacosDiscoveryProperties
*/
private void nacosSetTimeout(NacosDiscoveryProperties nacosDiscoveryProperties) {
String serviceName = nacosDiscoveryProperties.getService();
String clusterName = nacosDiscoveryProperties.getClusterName();
String server = nacosDiscoveryProperties.getServerAddr();
String ip = nacosDiscoveryProperties.getIp();
int port = nacosDiscoveryProperties.getPort();
String nameSpace = nacosDiscoveryProperties.getNamespace();
String[] split = server.split(",");
JSONObject variables = new JSONObject();
variables.put("namespaceId", nameSpace);
variables.put("serviceName", serviceName);
JSONArray instantsList = new JSONArray(); //实例ip
JSONObject instantsOne = new JSONObject();
instantsOne.put("ip", ip);
instantsOne.put("port", port);
instantsOne.put("clusterName", clusterName);
instantsList.add(instantsOne);
variables.put("instances", instantsList.toJSONString());
JSONObject metaDataOne = new JSONObject();
metaDataOne.put(PreservedMetadataKeys.HEART_BEAT_INTERVAL, "1000");
// 设置心跳超时时间,单位为秒,这里将心跳超时时间设为500毫秒
// 即服务端6秒收不到客户端心跳,会将该客户端注册的实例设为不健康:
metaDataOne.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, "500");
// 设置实例删除的超时时间,单位为秒,这里将实例删除超时时间设为500毫秒,
// 即服务端9秒收不到客户端心跳,会将该客户端注册的实例删除:
metaDataOne.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, "500");
variables.put("metadata", metaDataOne.toJSONString());
nacosMetaUrl = split[0] + nacosMetaUrl;
if (variables != null) {
nacosMetaUrl = nacosMetaUrl + "?" + asUrlVariables(variables);
}
log.info("nacosDown rest , nacosMetaUrl:{} ", nacosMetaUrl);
JSONObject result = RestUtil.put(nacosMetaUrl);
log.info("nacosDown rest ok , nacosMetaUrl:{}, result:{} ", nacosMetaUrl, result.toJSONString());
}
public static String asUrlVariables(JSONObject variables) {
Map<String, Object> source = variables.getInnerMap();
Iterator<String> it = source.keySet().iterator();
StringBuilder urlVariables;
String key;
String value;
for(urlVariables = new StringBuilder(); it.hasNext(); urlVariables.append("&").append(key).append("=").append(value)) {
key = (String)it.next();
value = "";
Object object = source.get(key);
if (object != null && !StrUtil.isEmpty(object.toString())) {
value = object.toString();
}
}
return urlVariables.substring(1);
}