Nacos源码学习系列服务端第8篇服务查询之实例查询

目录

流程图

服务查询(InstanceOperatorClientImpl)

 ServiceInfo存取服务(ServiceStorage) 

 ServiceInfo

服务清理任务(EmptyServiceAutoCleanerV2)

 总结

我们接着上篇的服务订阅部分,本篇我们继续讲解服务查询的实例列表是如果查询出来的 

流程图

 

服务查询(InstanceOperatorClientImpl)

public class InstanceOperatorClientImpl implements InstanceOperator {

    //客户端管理器
    private final ClientManager clientManager;
    //客户端操作服务 有2种具体实现
    // EphemeralClientOperationServiceImpl 
    // PersistenceClientOperationServiceImpl
    private final ClientOperationService clientOperationService;
    //服务存储服务 后面我们会讲 ServiceStorage 跟 
    // ClientServiceIndexesManager 有什么区别 都是管理服务的
    private final ServiceStorage serviceStorage;
    //元数据管理器   
    private final NamingMetadataManager metadataManager;
    
    private final NamingMetadataOperateService metadataOperateService;
    //可以理解成全局的配置开关
    private final SwitchDomain switchDomain;
    //udp推送服务 
    private final UdpPushService pushService;

  @Override
    public ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber 
          subscriber, String cluster,  boolean healthOnly) {
        //构建service
        Service service = getService(namespaceId, serviceName, true);
        // 判断客户端sdk是否支持推送功能
        if (subscriber.getPort() > 0 && pushService.canEnablePush(subscriber.getAgent())) {
            String clientId = IpPortBasedClient.getClientId(subscriber.getAddrStr(), true);
            //创建客户端这里不在赘述创建客户端的逻辑需要了解客户端的朋友异步到前面
            //的客户端章节
            createIpPortClientIfAbsent(clientId);
            //EphemeralClientOperationServiceImpl 完成服务注册功能
            clientOperationService.subscribeService(service, subscriber, clientId);
        }
       
        //查询服务信息包括所有的实例列表
        ServiceInfo serviceInfo = serviceStorage.getData(service);
        //查询服务的元数据信息  
        ServiceMetadata serviceMetadata = 
                metadataManager.getServiceMetadata(service).orElse(null);
       //对实例列表根据集群名称/是否可用/是否健康/自定义过滤器/阈值保护层曾过滤
        ServiceInfo result = ServiceUtil
                .selectInstancesWithHealthyProtection(serviceInfo, serviceMetadata, 
                   cluster, healthOnly, true, subscriber.getIp());
        // adapt for v1.x sdk
        result.setName(NamingUtils.getGroupedName(result.getName(), 
                  result.getGroupName()));
        return result;
    }
}

 首先从serviceStroage里面查询服务信息ServiceInfo, 这个对象跟之前经常跟我们见面的service对象由本质的区别。之前的service对象在客户端管理器, 服务管理器serviceManager,服务注册订阅表管理ClientServiceIndexesManager 被大量使用。这些功能要从记忆中串联起来。

接着从metadataManager获取服务的元数据信息,注意是使用元数据里面的selector,用于

ServiceUtil.selectInstancesWithHealthyProtection()方法对查询实例列表做筛选。

 ServiceInfo存取服务(ServiceStorage) 

public class ServiceStorage {
    
   
    private final ClientServiceIndexesManager serviceIndexesManager;
    
    private final ClientManager clientManager;
    
    private final SwitchDomain switchDomain;
    
    private final NamingMetadataManager metadataManager;
    
    private final ConcurrentMap<Service, ServiceInfo> serviceDataIndexes;
    
    private final ConcurrentMap<Service, Set<String>> serviceClusterIndex;
    
    ...
    //服务的集群列表
    public Set<String> getClusters(Service service) {
        return serviceClusterIndex.getOrDefault(service, new HashSet<>());
    }
    
    //从serviceDataIndexes获取service数据如果么有
    //通过getPush构建一个ServiceInfo
    public ServiceInfo getData(Service service) {
        return serviceDataIndexes.containsKey(service) ? 
        serviceDataIndexes.get(service) : getPushData(service);
    }
    //如果服务管理器中没有这个服务就返回一个空的serviceInfo
    public ServiceInfo getPushData(Service service) {
        ServiceInfo result = emptyServiceInfo(service);
        if (!ServiceManager.getInstance().containSingleton(service)) {
            return result;
        }
        //从服务注册表查询所有的实例并设置给ServiceInfo
        result.setHosts(getAllInstancesFromIndex(service));
        //数保存到serviceDataIndexes  
        serviceDataIndexes.put(service, result);
        return result;
    }

    //分别从2个map中移除service记录
    public void removeData(Service service) {
        serviceDataIndexes.remove(service);
        serviceClusterIndex.remove(service);
    }
    
   //根据service中的属性创建新的ServiceInfo
    private ServiceInfo emptyServiceInfo(Service service) {
        ServiceInfo result = new ServiceInfo();
        result.setName(service.getName());
        result.setGroupName(service.getGroup());
        result.setLastRefTime(System.currentTimeMillis());
        result.setCacheMillis(switchDomain.getDefaultPushCacheMillis());
        return result;
    }
    
    private List<Instance> getAllInstancesFromIndex(Service service) {
        Set<Instance> result = new HashSet<>();
        Set<String> clusters = new HashSet<>();
        //轮询发布该服务的所有clientId 
        for (String each :  serviceIndexesManager.
                              getAllClientsRegisteredService(service)) {

            //根据clientId查询对应发布的实例信息
            Optional<InstancePublishInfo> instancePublishInfo = getInstanceInfo(each, 
                    service);
            if (instancePublishInfo.isPresent()) {
                Instance instance = parseInstance(service, instancePublishInfo.get());
                result.add(instance);
                clusters.add(instance.getClusterName());
            }
        }
        // cache clusters of this service
        serviceClusterIndex.put(service, clusters);
        return new LinkedList<>(result);
    }
    
    //根据clientId从客户端管理器中查询Client
    //client查询服务的所有发布信息类型InstancePublishInfo(跟Instance属性差异不大)
    private Optional<InstancePublishInfo> getInstanceInfo(String clientId, Service service) {
        Client client = clientManager.getClient(clientId);
        if (null == client) {
            return Optional.empty();
        }
        return Optional.ofNullable(client.getInstancePublishInfo(service));
    }
    //解析InstancePublishInfo 成 Instance
    private Instance parseInstance(Service service, InstancePublishInfo instanceInfo) {
        //属性赋值
        Instance result = InstanceUtil.parseToApiInstance(service, instanceInfo);
        //查询实例的元数据并更新Instance里面的元数据
        Optional<InstanceMetadata> metadata = metadataManager
                .getInstanceMetadata(service, instanceInfo.getMetadataId());
        metadata.ifPresent(instanceMetadata -> 
            InstanceUtil.updateInstanceMetadata(result, instanceMetadata));
        return result;
    }
}

从上面的代码可以看出ServiceStorage 用于根据service 存取对应的ServiceInfo.以及service 下的集群列表 是在服务第一次查询的时候构建ServiceInfo的 可以理解成对外的。

ServiceManager 存储的是service<->service信息 以及 命名空间下的service 列表

service 是服务第一次构建的时候注册到ServiceManager 的 可以理解成对内的。 

以及clientServiceIndexesManager 三者都有自动清理的功能而且都是通过 EmptyServiceAutoCleanerV2 类来实现的,文末给出这个类的实现读者仔细阅读下。

ServiceInfo 目前为止可以理解为返回给客户端的服务详情信息

ServiceInfo 的结构如下:

 ServiceInfo

public class ServiceInfo {
    
    @JsonIgnore
    private String jsonFromServer = EMPTY;
    
    private static final String EMPTY = "";
    //该字段暂未发现使用的地方
    private static final String ALL_IPS = "000--00-ALL_IPS--00--000";
    
    public static final String SPLITER = "@@";
    
    private static final String DEFAULT_CHARSET = "UTF-8";

    //服务名称
    private String name;
    
    private String groupName;
    //集群名称
    private String clusters;
    
    private long cacheMillis = 1000L;

    //服务下的实例列表
    private List<Instance> hosts = new ArrayList<Instance>();
    //最近一次访问的时间
    private long lastRefTime = 0L;
    
    private String checksum = "";
    //allIPs 目前么有找到使用的地方
    private volatile boolean allIPs = false;
    //是否触达到了服务实例的保护阈值 0-1 之间的小数
    private volatile boolean reachProtectionThreshold = false;
    
     ....
     // clusters 可以为空
     //groupName@@name 或者 groupName@@name@clusters 格式的key的解析
     
    public ServiceInfo(String key) {
        int maxIndex = 2;
        int clusterIndex = 2;
        int serviceNameIndex = 1;
        int groupIndex = 0;
        
        String[] keys = key.split(Constants.SERVICE_INFO_SPLITER);
        if (keys.length >= maxIndex + 1) {
            this.groupName = keys[groupIndex];
            this.name = keys[serviceNameIndex];
            this.clusters = keys[clusterIndex];
        } else if (keys.length == maxIndex) {
            this.groupName = keys[groupIndex];
            this.name = keys[serviceNameIndex];
        } else {
            //defensive programming
            throw new IllegalArgumentException("Cann't parse out 'groupName',but it must not be null!");
        }
    }
    
    
    public boolean expired() {
        return System.currentTimeMillis() - lastRefTime > cacheMillis;
    }
    
   
        
     //校验是否存在健康的服务实例
    public boolean validate() {
        if (isAllIPs()) {
            return true;
        }
        
        if (hosts == null) {
            return false;
        }
        
        List<Instance> validHosts = new ArrayList<Instance>();
        for (Instance host : hosts) {
            if (!host.isHealthy()) {
                continue;
            }
            
            for (int i = 0; i < host.getWeight(); i++) {
                validHosts.add(host);
            }
        }
        return !validHosts.isEmpty();
    }
    
   
    
    ....
}

服务清理任务(EmptyServiceAutoCleanerV2)

@Component
public class EmptyServiceAutoCleanerV2 extends AbstractNamingCleaner {
    
    private static final String EMPTY_SERVICE = "emptyService";
    
    private final ClientServiceIndexesManager clientServiceIndexesManager;
    
    private final ServiceStorage serviceStorage;
    
    public EmptyServiceAutoCleanerV2(ClientServiceIndexesManager 
            clientServiceIndexesManager,
            ServiceStorage serviceStorage) {
        this.clientServiceIndexesManager = clientServiceIndexesManager;
        this.serviceStorage = serviceStorage;
        //60秒执行一次服务的清理工作 
        GlobalExecutor.scheduleExpiredClientCleaner(this, TimeUnit.SECONDS.toMillis(30),
                GlobalConfig.getEmptyServiceCleanInterval(), TimeUnit.MILLISECONDS);
        
    }
    
    @Override
    public String getType() {
        return EMPTY_SERVICE;
    }
    
    //服务清理流程
    @Override
    public void doClean() {
        ServiceManager serviceManager = ServiceManager.getInstance();
        // Parallel flow opening threshold
        int parallelSize = 100;
        //轮询所有的命名空间
        for (String each : serviceManager.getAllNamespaces()) {
            //每个命名空间下的服务列表
            Set<Service> services = serviceManager.getSingletons(each);
            //大于100个用并行流处理
            Stream<Service> stream = services.size() > parallelSize ? 
            services.parallelStream() : services.stream();
            //对每一个服务执行清理操作 
            stream.forEach(this::cleanEmptyService);
        }
    }
    
    private void cleanEmptyService(Service service) {
        Collection<String> registeredService = 
                 clientServiceIndexesManager.getAllClientsRegisteredService(service);
       //满足条件:没有找到服务实例且服务更新已过期
       //分别移除clientServiceIndexesManager ServiceManager ServiceStorage
        if (registeredService.isEmpty() && isTimeExpired(service)) {
            clientServiceIndexesManager.removePublisherIndexesByEmptyService(service);
            ServiceManager.getInstance().removeSingleton(service);
            serviceStorage.removeData(service);
            
            NotifyCenter.publishEvent(new MetadataEvent.ServiceMetadataEvent(service, true));
        }
    }
    
    private boolean isTimeExpired(Service service) {
        long currentTimeMillis = System.currentTimeMillis();
        return currentTimeMillis - service.getLastUpdatedTime() >= 
               GlobalConfig.getEmptyServiceExpiredTime();
    }
}

 总结


到目前为止我们经常遇到一个元数据管理器,使用到的地方都是一带而过,讲完服务查询后我们开篇讲一下元数据管理器是做什么用的。

下一篇我们继续讲解服务查询的最后一部分服务的过滤与阈值保护机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值