目录
(1.2)namingServer处理拉取服务实例列表的请求
示例代码:
public class MainConsumer {
private static Logger logger = LoggerFactory.getLogger(MainConsumer.class);
public static void main(String[] args) throws NacosException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
properties.put(PropertyKeyConst.USERNAME, "nacos");
properties.put(PropertyKeyConst.PASSWORD, "nacos");
Instance instance = new Instance();
instance.setIp("127.0.0.1");
instance.setPort(800);
instance.setWeight(2);
Map<String, String> map = new HashMap<String, String>();
map.put("netType", "external");
map.put("version", "2.0");
instance.setMetadata(map);
NamingService namingService = NacosFactory.createNamingService(properties);
namingService.registerInstance("nacos.service.consumer", instance);
// 拉取服务nacos.service.provider的所有实例列表
List<com.alibaba.nacos.api.naming.pojo.Instance> allInstances = namingService.getAllInstances("nacos.service.provider");
logger.info("allInstances:{}", allInstances);
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(1)获取服务实例列表
public List<Instance> getAllInstances(String serviceName) throws NacosException {
// 传递空的cluster列表
return getAllInstances(serviceName, new ArrayList<String>());
}
public List<Instance> getAllInstances(String serviceName, List<String> clusters) throws NacosException {
// 传递subscribe参数为true
// 代表订阅服务serviceName
return getAllInstances(serviceName, clusters, true);
}
public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters,
boolean subscribe) throws NacosException {
ServiceInfo serviceInfo;
if (subscribe) {
logger.info("获取服务[{}]的所有服务实例列表,并且订阅", serviceName);
serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName),
StringUtils.join(clusters, ","));
} else {
logger.info("获取服务[{}]的所有服务实例列表,但不订阅", serviceName);
serviceInfo = hostReactor
.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName),
StringUtils.join(clusters, ","));
}
List<Instance> list;
if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) {
return new ArrayList<Instance>();
}
return list;
}
public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());
String key = ServiceInfo.getKey(serviceName, clusters);
// 故障转移机制
if (failoverReactor.isFailoverSwitch()) {
return failoverReactor.getService(key);
}
// 从serviceInfoMap缓存中获取
ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
if (null == serviceObj) {
// 从serviceInfoMap中未读物到serviceInfo
// 先构造一个serviceInfo保存到serviceInfoMap中,然后去服务端拉取
serviceObj = new ServiceInfo(serviceName, clusters);
serviceInfoMap.put(serviceObj.getKey(), serviceObj);
// 在updatingMap中保存更新记录
updatingMap.put(serviceName, new Object());
// 从naming中获取服务信息
updateServiceNow(serviceName, clusters);
// 移除updatingMap中的更新记录
updatingMap.remove(serviceName);
} else if (updatingMap.containsKey(serviceName)) {
// serviceName正在更新...
// 然后默认wait(5s)
if (UPDATE_HOLD_INTERVAL > 0) {
// hold a moment waiting for update finish
synchronized (serviceObj) {
try {
serviceObj.wait(UPDATE_HOLD_INTERVAL);
} catch (InterruptedException e) {
NAMING_LOGGER
.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);
}
}
}
}
// 添加定时任务,定时去拉取serviceName的信息,默认1s一次
scheduleUpdateIfAbsent(serviceName, clusters);
// 从缓存中读取
return serviceInfoMap.get(serviceObj.getKey());
}
抛开故障转移机制不分析。
先尝试从缓存中获取,获取不到就会立即构造一个service对象,然后立即从namingServer中获取。接着就会提交一个任务,去定时拉取更新实例列表
(1.1)从naming拉取实例列表
private void updateServiceNow(String serviceName, String clusters) {
try {
updateService(serviceName, clusters);
} catch (NacosException e) {
NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e);
}
}
public void updateService(String serviceName, String clusters) throws NacosException {
// 从缓存中读取服务实例列表
ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
try {
// 发送拉取服务实例列表请求,并且传递了参数,healthOnly=false, 代表不只拉取健康的服务实例
String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);
if (StringUtils.isNotEmpty(result)) {
processServiceJson(result);
}
} finally {
// 通过oldService去唤醒前面wait的线程
if (oldService != null) {
synchronized (oldService) {
oldService.notifyAll();
}
}
}
}
发送拉取请求
public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly)
throws NacosException {
final Map<String, String> params = new HashMap<String, String>(8);
params.put(CommonParams.NAMESPACE_ID, namespaceId);
params.put(CommonParams.SERVICE_NAME, serviceName);
params.put("clusters", clusters);
params.put("udpPort", String.valueOf(udpPort));
params.put("clientIP", NetUtils.localIP());
params.put("healthyOnly", String.valueOf(healthyOnly));
logger.info("拉取服务实例列表[{}], 本地clientIp:{}, udpPort:{}", serviceName, params.get("clientIp"), params.get("udpPort"));
return reqApi(UtilAndComs.nacosUrlBase + "/instance/list", params, HttpMethod.GET);
}
请求的url:/nacos/v1/ns/instance/list get请求,
这里需要注意的是,请求参数中,有两个参数udpPort, clientIp, 联想上一节(2. nacos之服务注册_ZXH240651200的博客-CSDN博客)中的udp发送服务变化通知,好像有联系。
接着分析namingServer如何处理这个请求。
(1.2)namingServer处理拉取服务实例列表的请求
上面分析了get请求url:/nacos/v1/ns/instance/list
com.alibaba.nacos.naming.controllers.InstanceController#list
/**
* Get all instance of input service.
*
* @param request http request
* @return list of instance
* @throws Exception any error during list
*/
@GetMapping("/list")
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
public ObjectNode list(HttpServletRequest request) throws Exception {
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
NamingUtils.checkServiceNameFormat(serviceName);
// 客户端agent,对于java客户端来说,是客户端的版本号:
// 在客户端reqApi中构建header的时候,指定的
// header.addParam(HttpHeaderConsts.USER_AGENT_HEADER, UtilAndComs.VERSION);
// Nacos-Java-Client:v1.4.2
String agent = WebUtils.getUserAgent(request);
String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY);
// 提取clientIp和udpPort
String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY);
int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0"));
// 默认是""
String env = WebUtils.optional(request, "env", StringUtils.EMPTY);
// 默认是false
boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false"));
// 默认是unknow
String app = WebUtils.optional(request, "app", StringUtils.EMPTY);
// 默认是""
String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY);
// 默认是false
boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false"));
logger.info("拉取服务[{}] 详细信息", serviceName);
return doSrvIpxt(namespaceId, serviceName, agent, clusters, clientIP, udpPort, env, isCheck, app, tenant,
healthyOnly);
}
public ObjectNode doSrvIpxt(String namespaceId, String serviceName, String agent, String clusters, String clientIP,
int udpPort, String env, boolean isCheck, String app, String tid, boolean healthyOnly) throws Exception {
// java客户端来说,根据版本号构建clientInfo
ClientInfo clientInfo = new ClientInfo(agent);
ObjectNode result = JacksonUtils.createEmptyJsonNode();
// 通过namespaceId + serviceName从缓存中获取
Service service = serviceManager.getService(namespaceId, serviceName);
// 缓存时间,默认3s
long cacheMillis = switchDomain.getDefaultCacheMillis();
// now try to enable the push
try {
// udpPort >0 && 并且可以进行推送,java客户端,可以推送
if (udpPort > 0 && pushService.canEnablePush(agent)) {
// pushService保存client信息
pushService
.addClient(namespaceId, serviceName, clusters, agent, new InetSocketAddress(clientIP, udpPort),
pushDataSource, tid, app);
// 修改缓存时间,默认的推送缓存时间,默认10s
cacheMillis = switchDomain.getPushCacheMillis(serviceName);
}
} catch (Exception e) {
Loggers.SRV_LOG
.error("[NACOS-API] failed to added push client {}, {}:{}", clientInfo, clientIP, udpPort, e);
cacheMillis = switchDomain.getDefaultCacheMillis();
}
// service不存在,返回空集合
if (service == null) {
if (Loggers.SRV_LOG.isDebugEnabled()) {
Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName);
}
result.put("name", serviceName);
result.put("clusters", clusters);
result.put("cacheMillis", cacheMillis);
result.replace("hosts", JacksonUtils.createEmptyArrayNode());
return result;
}
// 检查服务是否可用,不可用会抛出异常
checkIfDisabled(service);
List<Instance> srvedIPs;
// 获取客户端入参clusters 所有的实例ip,
// 如果clusters为空,就是所有实例的ip
srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ",")));
// filter ips using selector:
// 通过selector过滤ips
// 默认是NoneSelecor,返回所有的srvedIPs
if (service.getSelector() != null && StringUtils.isNotBlank(clientIP)) {
srvedIPs = service.getSelector().select(clientIP, srvedIPs);
}
// 经过selector过滤之后,没有实例,构造返回结果
if (CollectionUtils.isEmpty(srvedIPs)) {
if (Loggers.SRV_LOG.isDebugEnabled()) {
Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName);
}
if (clientInfo.type == ClientInfo.ClientType.JAVA
&& clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
result.put("dom", serviceName);
} else {
result.put("dom", NamingUtils.getServiceName(serviceName));
}
result.put("name", serviceName);
result.put("cacheMillis", cacheMillis);
result.put("lastRefTime", System.currentTimeMillis());
result.put("checksum", service.getChecksum());
result.put("useSpecifiedURL", false);
result.put("clusters", clusters);
result.put("env", env);
result.set("hosts", JacksonUtils.createEmptyArrayNode());
result.set("metadata", JacksonUtils.transferToJsonNode(service.getMetadata()));
return result;
}
// 构建map, key是健康度,value是相应的实例列表
Map<Boolean, List<Instance>> ipMap = new HashMap<>(2);
ipMap.put(Boolean.TRUE, new ArrayList<>());
ipMap.put(Boolean.FALSE, new ArrayList<>());
for (Instance ip : srvedIPs) {
ipMap.get(ip.isHealthy()).add(ip);
}
if (isCheck) {
result.put("reachProtectThreshold", false);
}
// 服务保护阈值
double threshold = service.getProtectThreshold();
if ((float) ipMap.get(Boolean.TRUE).size() / srvedIPs.size() <= threshold) {
// 触发服务保护阈值
Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", serviceName);
if (isCheck) {
result.put("reachProtectThreshold", true);
}
// 不区分服务健康度,都是健康的服务
ipMap.get(Boolean.TRUE).addAll(ipMap.get(Boolean.FALSE));
ipMap.get(Boolean.FALSE).clear();
}
// 如果有检查标志,就直接返回了
// 默认是false
if (isCheck) {
result.put("protectThreshold", service.getProtectThreshold());
result.put("reachLocalSiteCallThreshold", false);
return JacksonUtils.createEmptyJsonNode();
}
ArrayNode hosts = JacksonUtils.createEmptyArrayNode();
for (Map.Entry<Boolean, List<Instance>> entry : ipMap.entrySet()) {
List<Instance> ips = entry.getValue();
// 如果只返回健康的服务,就跳过不健康的服务
if (healthyOnly && !entry.getKey()) {
continue;
}
// 构造返回结果
for (Instance instance : ips) {
// remove disabled instance:
// 剔除不可用的服务
if (!instance.isEnabled()) {
continue;
}
ObjectNode ipObj = JacksonUtils.createEmptyJsonNode();
ipObj.put("ip", instance.getIp());
ipObj.put("port", instance.getPort());
// deprecated since nacos 1.0.0:
ipObj.put("valid", entry.getKey());
ipObj.put("healthy", entry.getKey());
ipObj.put("marked", instance.isMarked());
ipObj.put("instanceId", instance.getInstanceId());
ipObj.set("metadata", JacksonUtils.transferToJsonNode(instance.getMetadata()));
ipObj.put("enabled", instance.isEnabled());
ipObj.put("weight", instance.getWeight());
ipObj.put("clusterName", instance.getClusterName());
if (clientInfo.type == ClientInfo.ClientType.JAVA
&& clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
ipObj.put("serviceName", instance.getServiceName());
} else {
ipObj.put("serviceName", NamingUtils.getServiceName(instance.getServiceName()));
}
ipObj.put("ephemeral", instance.isEphemeral());
hosts.add(ipObj);
}
}
result.replace("hosts", hosts);
if (clientInfo.type == ClientInfo.ClientType.JAVA
&& clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
result.put("dom", serviceName);
} else {
result.put("dom", NamingUtils.getServiceName(serviceName));
}
result.put("name", serviceName);
result.put("cacheMillis", cacheMillis);
result.put("lastRefTime", System.currentTimeMillis());
result.put("checksum", service.getChecksum());
result.put("useSpecifiedURL", false);
result.put("clusters", clusters);
result.put("env", env);
result.replace("metadata", JacksonUtils.transferToJsonNode(service.getMetadata()));
return result;
}
这里将客户端的udpport和clientIp信息,构造成pushclient并保存到pushService中,
客户端如果传递了healthyOnly=true,那么值返回健康的实例
如果配置了阈值保护,且触发了阈值保护:ipMap.get(Boolean.TRUE).size() / srvedIPs.size() <= threshold, 那么认为所有服务实例都是健康的,返回给客户端。
并且给客户端返回了cacheMillis,默认是3s,如果有udpport和clientIp信息,就调整为10s,这个字段的作用,在下面会分析。
回到上面的pushClient地方
public void addClient(String namespaceId, String serviceName, String clusters, String agent,
InetSocketAddress socketAddr, DataSource dataSource, String tenant, String app) {
// 构造PushClient
PushClient client = new PushClient(namespaceId, serviceName, clusters, agent, socketAddr, dataSource, tenant,
app);
// 添加publicClient
addClient(client);
}
public void addClient(PushClient client) {
// client is stored by key 'serviceName' because notify event is driven by serviceName change
// 构造namespaceId+serviceName
String serviceKey = UtilsAndCommons.assembleFullServiceName(client.getNamespaceId(), client.getServiceName());
ConcurrentMap<String, PushClient> clients = clientMap.get(serviceKey);
if (clients == null) {
clientMap.putIfAbsent(serviceKey, new ConcurrentHashMap<>(1024));
clients = clientMap.get(serviceKey);
}
// 如果pushclient已经存在了,就refresh()更新一下lastRefTime时间
// 这个lastRefTime的作用是,在进行发送通知消息的时候,如果lastRefTime超过10s未更新,就不会发送udp通知
PushClient oldClient = clients.get(client.toString());
if (oldClient != null) {
oldClient.refresh();
} else {
PushClient res = clients.putIfAbsent(client.toString(), client);
if (res != null) {
Loggers.PUSH.warn("client: {} already associated with key {}", res.getAddrStr(), res.toString());
}
Loggers.PUSH.debug("client: {} added for serviceName: {}", client.getAddrStr(), client.getServiceName());
}
}
这里就形成闭环了,服务请求其他服务实例列表的时候,带上了自己的clientIp+udpPort, 然后namingServer把clientIp+udpport构造成pushClient,当关心的服务发生变更时,就通过pushClient进行反向通知
(1.3)client接收并处理拉取到的服务实例列表结果
回到1.1部分,发送拉取服务实例请求后的部分,
com.alibaba.nacos.client.naming.core.HostReactor#updateService
public void updateService(String serviceName, String clusters) throws NacosException {
// 从缓存中读取服务实例列表
ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
try {
// 发送拉取服务实例列表请求,并且传递了参数,healthOnly=false, 代表不只拉取健康的服务实例
String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);
// 处理拉取到的结果
if (StringUtils.isNotEmpty(result)) {
processServiceJson(result);
}
} finally {
// 通过oldService去唤醒前面wait的线程
if (oldService != null) {
synchronized (oldService) {
oldService.notifyAll();
}
}
}
}
public ServiceInfo processServiceJson(String json) {
ServiceInfo serviceInfo = JacksonUtils.toObj(json, ServiceInfo.class);
String serviceKey = serviceInfo.getKey();
if (serviceKey == null) {
return null;
}
// 先从缓存中读取旧的缓存
ServiceInfo oldService = serviceInfoMap.get(serviceKey);
// 如果开启了pushEnmpty保护,并且拉取的serviceInfo不可用,就返回旧的缓存
if (pushEmptyProtection && !serviceInfo.validate()) {
//empty or error push, just ignore
return oldService;
}
// 默认没有发生变化
boolean changed = false;
if (oldService != null) {
// 判断刷新时间
if (oldService.getLastRefTime() > serviceInfo.getLastRefTime()) {
NAMING_LOGGER.warn("out of date data received, old-t: " + oldService.getLastRefTime() + ", new-t: "
+ serviceInfo.getLastRefTime());
}
// 将拉取到的serviceInfo保存到缓存中
serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
// 将旧的缓存hosts构造成map
Map<String, Instance> oldHostMap = new HashMap<String, Instance>(oldService.getHosts().size());
for (Instance host : oldService.getHosts()) {
oldHostMap.put(host.toInetAddr(), host);
}
// 将新的服务列表hosts,构造成map
Map<String, Instance> newHostMap = new HashMap<String, Instance>(serviceInfo.getHosts().size());
for (Instance host : serviceInfo.getHosts()) {
newHostMap.put(host.toInetAddr(), host);
}
//发生变化的instances
Set<Instance> modHosts = new HashSet<Instance>();
// 新的instances
Set<Instance> newHosts = new HashSet<Instance>();
// 删除的instances
Set<Instance> remvHosts = new HashSet<Instance>();
List<Map.Entry<String, Instance>> newServiceHosts = new ArrayList<Map.Entry<String, Instance>>(
newHostMap.entrySet());
for (Map.Entry<String, Instance> entry : newServiceHosts) {
Instance host = entry.getValue();
String key = entry.getKey();
// host属性发生变化
if (oldHostMap.containsKey(key) && !StringUtils
.equals(host.toString(), oldHostMap.get(key).toString())) {
modHosts.add(host);
continue;
}
// oldHost里面有的,就认为是新增的host
if (!oldHostMap.containsKey(key)) {
newHosts.add(host);
}
}
for (Map.Entry<String, Instance> entry : oldHostMap.entrySet()) {
Instance host = entry.getValue();
String key = entry.getKey();
if (newHostMap.containsKey(key)) {
continue;
}
// newHosts里面不包含的,认为是删除的host
if (!newHostMap.containsKey(key)) {
remvHosts.add(host);
}
}
if (newHosts.size() > 0) {
changed = true;
NAMING_LOGGER.info("new ips(" + newHosts.size() + ") service: " + serviceInfo.getKey() + " -> "
+ JacksonUtils.toJson(newHosts));
}
if (remvHosts.size() > 0) {
changed = true;
NAMING_LOGGER.info("removed ips(" + remvHosts.size() + ") service: " + serviceInfo.getKey() + " -> "
+ JacksonUtils.toJson(remvHosts));
}
if (modHosts.size() > 0) {
changed = true;
// 针对属性变化的host,更新beat信息
updateBeatInfo(modHosts);
NAMING_LOGGER.info("modified ips(" + modHosts.size() + ") service: " + serviceInfo.getKey() + " -> "
+ JacksonUtils.toJson(modHosts));
}
// 保存原始json
serviceInfo.setJsonFromServer(json);
if (newHosts.size() > 0 || remvHosts.size() > 0 || modHosts.size() > 0) {
// 发生变化,就publishEvent-->InstancesChangeEvent
NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(),
serviceInfo.getClusters(), serviceInfo.getHosts()));
DiskCache.write(serviceInfo, cacheDir);
}
} else {
//oldServices == null
changed = true;
NAMING_LOGGER.info("init new ips(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getKey() + " -> "
+ JacksonUtils.toJson(serviceInfo.getHosts()));
// 保存到缓存
serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
// 发送事件
NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(),
serviceInfo.getClusters(), serviceInfo.getHosts()));
serviceInfo.setJsonFromServer(json);
DiskCache.write(serviceInfo, cacheDir);
}
MetricsMonitor.getServiceInfoMapSizeMonitor().set(serviceInfoMap.size());
if (changed) {
NAMING_LOGGER.info("current ips:(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getKey() + " -> "
+ JacksonUtils.toJson(serviceInfo.getHosts()));
}
return serviceInfo;
}
得到结果之后,会分析出新增的instance,删除的instance,和属性发生变化的instance(会更新beat信息),如果整体发生了变化,会publishEvent(InstancesChangeEvent)。
(1.4)定时去naming拉取服务实例列表
在(1)中有个定时任务
com.alibaba.nacos.client.naming.core.HostReactor#getServiceInfo
// 添加定时任务,定时去拉取serviceName的信息,默认1s一次
scheduleUpdateIfAbsent(serviceName, clusters);
public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
// futureMap中已经有任务了,就不在添加了
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
return;
}
synchronized (futureMap) {
// 再次判断
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
return;
}
// 添加任务
ScheduledFuture<?> future = addTask(new UpdateTask(serviceName, clusters));
// 将future保存到futureMap中
futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
}
}
添加的UpdateTask
com.alibaba.nacos.client.naming.core.HostReactor.UpdateTask#run
public void run() {
//默认1s
long delayTime = DEFAULT_DELAY;
try {
// 获取服务信息缓存
ServiceInfo serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
// 获取为空,会立即拉取
if (serviceObj == null) {
updateService(serviceName, clusters);
return;
}
// 缓存的serviceInfo小于最后刷新时间(初始化为Long.MAX_VALUE),就立即拉取
if (serviceObj.getLastRefTime() <= lastRefTime) {
updateService(serviceName, clusters);
serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
} else {
// if serviceName already updated by push, we should not override it
// since the push data may be different from pull through force push
// 缓存信息已经是新的了,仅仅是刷新一下服务端的lastRefTime
refreshOnly(serviceName, clusters);
}
lastRefTime = serviceObj.getLastRefTime();
// 没有订阅本地事件,并且futureMap没有包含这个服务的key
if (!notifier.isSubscribed(serviceName, clusters) && !futureMap
.containsKey(ServiceInfo.getKey(serviceName, clusters))) {
// abort the update task
NAMING_LOGGER.info("update task is stopped, service:" + serviceName + ", clusters:" + clusters);
return;
}
// 如果service的实例列表为空,就增加failCount
if (CollectionUtils.isEmpty(serviceObj.getHosts())) {
incFailCount();
return;
}
// 获取naming返回的cacheMillis
delayTime = serviceObj.getCacheMillis();
// 充值failCount
resetFailCount();
} catch (Throwable e) {
incFailCount();
NAMING_LOGGER.warn("[NA] failed to update serviceName: " + serviceName, e);
} finally {
// 提交下一个事件,延时时间是min(delayTime << failCount, 60s)
// 有pushclient信息时,delayTime = 10s
// 无pushclient信息时,delayTime = 3s
// 正常情况下,failCount = 0
// 当发生异常情况是,failCount会递增,但是不会大于6
executor.schedule(this, Math.min(delayTime << failCount, DEFAULT_DELAY * 60), TimeUnit.MILLISECONDS);
}
}
每次updateTask执行完成之后,会将自己再次提交到线程池,延时时间做适当调整,主要根据naming返回的cacheMillis和本地的failCount,但是最终的延时时间不会大于60s.
在不出现异常,拉取的serviceObj.getHosts不为空的情况下,有push通知时,延迟为 10s;没有push通知是,延迟为1s。