Nacos源码学习系列服务端第11篇元数据管理之数据更新

上一篇我们讲述了元数据处理器是怎么注册到JRaftProtocol的,用户提交更新请求后,又是如果把请求提交给JraftProtocol,以及JraftProtocol是如何分发请求的。

本篇,我们讲请求处理器接收到任务和是如果处理本地元数据的。

 元数据更新处理器

@Component
public class InstanceMetadataProcessor extends RequestProcessor4CP {
    //元数据管理器统一的入口
    private final NamingMetadataManager namingMetadataManager;
    
    private final Serializer serializer;
    
    private final Type processType;
    
    private final ReentrantReadWriteLock lock;
    
    private final ReentrantReadWriteLock.ReadLock readLock;
    
   ....
    
    @Override
    public List<SnapshotOperation> loadSnapshotOperate() {
        //从本地硬盘加载所有的元数据操作 
        //lock 用于加载时的锁定避免再加载的时候对元数据的crud
        return Collections.singletonList(new 
        InstanceMetadataSnapshotOperation(namingMetadataManager, lock));
    }
    

    @Override
    public Response onRequest(ReadRequest request) {
        return null;
    }
    
    //数据更新请求处理
    @Override
    public Response onApply(WriteRequest request) {
        MetadataOperation<InstanceMetadata> op =             
             serializer.deserialize(request.getData().
            toByteArray(), processType);
        //解决跟快照的读写并发问题    
        readLock.lock();
        try {
            switch (DataOperation.valueOf(request.getOperation())) {
                case ADD:
                case CHANGE:
                    //添加和修改都用更新操作    
                    updateInstanceMetadata(op);
                    break;
                case DELETE:
                    deleteInstanceMetadata(op);
                    break;
                default:
                    //默认不做任何处理
                    return Response.newBuilder().setSuccess(false)
                            .setErrMsg("Unsupported operation " + 
                     request.getOperation()).build();
            }
            return Response.newBuilder().setSuccess(true).build();
        } catch (Exception e) {
            ....
        } finally {
            readLock.unlock();
        }
    }
    
    private void updateInstanceMetadata(MetadataOperation<InstanceMetadata> op) {
        //构建service 
        Service service = Service.newService(op.getNamespace(), op.getGroup(),             
             op.getServiceName());
        //调用namingMetadataManager处理更新请求
        namingMetadataManager.updateInstanceMetadata(service, op.getTag(),             
             op.getMetadata());
        //服务变更事件 通知所有的订阅者 服务变更事件篇以及讲解过了 大家回顾一下
        NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
    }
    
    private void deleteInstanceMetadata(MetadataOperation<InstanceMetadata> op) {
        Service service = Service.newService(op.getNamespace(), op.getGroup(),         
             op.getServiceName());
        //调用namingMetadataManager删除动作
         namingMetadataManager.removeInstanceMetadata(service, op.getTag());
    }

    //常量值是: naming_instance_metadata 
    //作用后续结合Jraft 再讲
    @Override
    public String group() {
        return Constants.INSTANCE_METADATA;
    }
}

上面我们看到onApply方法没有做什么逻辑,直接转发给NamingMetadataManager 处理。

NamingMetadataManager管理器类似于之前的ClientManager ServerManager 和ClientServiceIndexesManager一样都是统一的管理入口类。看到这里暂停下,还记得这些类的功能和关联关系吗?

下面我们看NamingMetadataManager

 元数据管理器(NamingMetadataManager )

@Component
public class NamingMetadataManager extends SmartSubscriber {
    //维护过期的元数据信息 包括服务和实例的
    private final Set<ExpiredMetadataInfo> expiredMetadataInfos;
    //维护服务的元数据
    private ConcurrentMap<Service, ServiceMetadata> serviceMetadataMap;
    //维护实例的元数据
    private ConcurrentMap<Service, ConcurrentMap<String, InstanceMetadata>>     
      instanceMetadataMap;
    
    private static final int INITIAL_CAPACITY = 1;
    
    public NamingMetadataManager() {
        //1<<10 = 1024
        serviceMetadataMap = new ConcurrentHashMap<>(1 << 10);
        instanceMetadataMap = new ConcurrentHashMap<>(1 << 10);
        expiredMetadataInfos = new ConcurrentHashSet<>();
        //向NotifyCenter注册自己 这块等后面讲到统一事件中心再详细讲解
        //此处略过
        NotifyCenter.registerSubscriber(this, 
               NamingEventPublisherFactory.getInstance());
    }
    
    //是否包括给服务的元数据
    public boolean containServiceMetadata(Service service) {
        return serviceMetadataMap.containsKey(service);
    }
    
    //是否包含实例的运输局
    public boolean containInstanceMetadata(Service service, String metadataId) {
        return instanceMetadataMap.containsKey(service) &&             
         instanceMetadataMap.get(service).containsKey(metadataId);
    }
    
    //查询服务的元数据
    public Optional<ServiceMetadata> getServiceMetadata(Service service) {
        return Optional.ofNullable(serviceMetadataMap.get(service));
    }
    
   //查询实例的元数据
    public Optional<InstanceMetadata> getInstanceMetadata(Service service, String 
         metadataId) {
        ConcurrentMap<String, InstanceMetadata> instanceMetadataMapForService = 
          instanceMetadataMap.get(service);
        if (null == instanceMetadataMapForService) {
            return Optional.empty();
        }
        return Optional.ofNullable(instanceMetadataMapForService.get(metadataId));
    }
    
   //更新服务的元数据 看到这里应该明白为什么update 和 add 都是调用这个方法了
   //新增server的版本号
    public void updateServiceMetadata(Service service, ServiceMetadata serviceMetadata) 
    {
        service.incrementRevision();
        serviceMetadataMap.put(service, serviceMetadata);
    }
   //更新实例元数据
   //实例元数据是再server为key的子map中 如果子map不存在就new一个
    public void updateInstanceMetadata(Service service, String metadataId, 
       InstanceMetadata instanceMetadata) {
        if (!instanceMetadataMap.containsKey(service)) {
            instanceMetadataMap.putIfAbsent(service, new ConcurrentHashMap<> 
            (INITIAL_CAPACITY));
        }
        instanceMetadataMap.get(service).put(metadataId, instanceMetadata);
    }
    
    /**
     * Remove service metadata.
     *
     * @param service service
     */
    public void removeServiceMetadata(Service service) {
        serviceMetadataMap.remove(service);
        expiredMetadataInfos.remove(ExpiredMetadataInfo.newExpiredServiceMetadata(service));
    }
    
     //移除服务下的某个实例元数据
     //如果给服务下么有么有任务实例就删除服务的元数据
    public void removeInstanceMetadata(Service service, String metadataId) {
        ConcurrentMap<String, InstanceMetadata> instanceMetadataMapForService = instanceMetadataMap.get(service);
        instanceMetadataMapForService.remove(metadataId);
        if (instanceMetadataMapForService.isEmpty()) {
            serviceMetadataMap.remove(service);
        }
        expiredMetadataInfos.remove(ExpiredMetadataInfo.newExpiredInstanceMetadata(service, metadataId));
    }
    
   //从当前的serviceMetadataMap创建快照
   //在导出快照的时候crud是不允许操作的 这个通过前面讲的读写锁来控制
    public Map<Service, ServiceMetadata> getServiceMetadataSnapshot() {
        ConcurrentMap<Service, ServiceMetadata> result = new ConcurrentHashMap<>                
        (serviceMetadataMap.size());
        result.putAll(serviceMetadataMap);
        return result;
    }
    
      //从当前的instanceMetadataMap创建快照
      //在导出快照的时候cud是不允许操作的 这个通过前面讲的读写锁来控制
      //导出时完毕不影响查询操作 
   public Map<Service, ConcurrentMap<String, InstanceMetadata>> 
        getInstanceMetadataSnapshot() {
        ConcurrentMap<Service, ConcurrentMap<String, InstanceMetadata>> result = new 
             ConcurrentHashMap<>(
                instanceMetadataMap.size());
        result.putAll(instanceMetadataMap);
        return result;
    }
    
    //加载服务的元数据快照
    //加载时不可以做cud操作同导出快照一样都是由读写锁控制
    //加载时完毕不影响查询操作
    //最后清空老的数据
    public void loadServiceMetadataSnapshot(ConcurrentMap<Service, ServiceMetadata> 
          snapshot) {
        for (Service each : snapshot.keySet()) {
            ServiceManager.getInstance().getSingleton(each);
        }
        ConcurrentMap<Service, ServiceMetadata> oldSnapshot = serviceMetadataMap;
        serviceMetadataMap = snapshot;
        oldSnapshot.clear();
    }
    
    //加载实例数据 同上
    public void loadInstanceMetadataSnapshot(ConcurrentMap<Service, 
             ConcurrentMap<String, InstanceMetadata>> snapshot) {
        ConcurrentMap<Service, ConcurrentMap<String, InstanceMetadata>> oldSnapshot = 
        instanceMetadataMap;
        instanceMetadataMap = snapshot;
        oldSnapshot.clear();
    }
    
    public Set<ExpiredMetadataInfo> getExpiredMetadataInfos() {
        return expiredMetadataInfos;
    }
    
   //监听元数据变更事件及客户端断开事件
    @Override
    public List<Class<? extends Event>> subscribeTypes() {
        List<Class<? extends Event>> result = new LinkedList<>();
        result.add(MetadataEvent.InstanceMetadataEvent.class);
        result.add(MetadataEvent.ServiceMetadataEvent.class);
        result.add(ClientEvent.ClientDisconnectEvent.class);
        return result;
    }
    
    //处理事件
    @Override
    public void onEvent(Event event) {
        if (event instanceof MetadataEvent.InstanceMetadataEvent) {
            handleInstanceMetadataEvent((MetadataEvent.InstanceMetadataEvent) event);
        } else if (event instanceof MetadataEvent.ServiceMetadataEvent) {
            handleServiceMetadataEvent((MetadataEvent.ServiceMetadataEvent) event);
        } else {
            handleClientDisconnectEvent((ClientEvent.ClientDisconnectEvent) event);
        }
    }
    
    //客户端delete后 找到它所有发布的服务实例
    //从InstancePublishInfo中metadataId, 此处回想下之前讲的的Client对象时管理什么的
    //如果元数据管理器中有metadataId的数据就移除掉
    private void handleClientDisconnectEvent(ClientEvent.ClientDisconnectEvent event) {
        for (Service each : event.getClient().getAllPublishedService()) {
            String metadataId = 
              event.getClient().getInstancePublishInfo(each).getMetadataId();
            if (containInstanceMetadata(each, metadataId)) {
                updateExpiredInfo(true, 
                   ExpiredMetadataInfo.newExpiredInstanceMetadata(each, metadataId));
            }
        }
    }
    
    //只有包含才更新
    //注意不包含的情况不能本地添加 因为要保证集群的数据一致性
    //这里更新并不是更新元数据内容 而是根据服务的上下线 判断要不要加上元数据的过期信息
    private void handleServiceMetadataEvent(MetadataEvent.ServiceMetadataEvent event) {
        Service service = event.getService();
        if (containServiceMetadata(service)) {
            updateExpiredInfo(event.isExpired(), 
            ExpiredMetadataInfo.newExpiredServiceMetadata(service));
        }
    }
    
    //只有包含才更新
    //注意不包含的情况不能本地添加 因为要保证集群的数据一致性
    //这里更新并不是更新元数据内容 而是根据服务的上下线 判断要不要加上元数据的过期信息
    private void handleInstanceMetadataEvent(MetadataEvent.InstanceMetadataEvent event) 
     {
        Service service = event.getService();
        String metadataId = event.getMetadataId();
        if (containInstanceMetadata(service, metadataId)) {
            updateExpiredInfo(event.isExpired(),
                    ExpiredMetadataInfo.newExpiredInstanceMetadata(event.getService(), 
                    event.getMetadataId()));
        }
    }
    
    //服务下线或者客户端delete掉后expired 值为true
    //服务上线后expired为false
    private void updateExpiredInfo(boolean expired, ExpiredMetadataInfo 
         expiredMetadataInfo) {
        if (expired) {
            expiredMetadataInfos.add(expiredMetadataInfo);
        } else {
            expiredMetadataInfos.remove(expiredMetadataInfo);
        }
    }
}

NamingMetadataManager概括一下包括哪些功能呢?

服务和实例袁术的增删改查这是最基本的,还有快照的加载和导出功能。

订阅了服务和实例元数据变更事件 这些事件还记得什么时候发出的吗?

同时订阅了客户端下线的事件 以及3类事件的处理方式。

那么最后给大家看下InstanceMetadata 和 ServiceMetadate的结构是什么样子的。

 元数据实体类

public class ServiceMetadata implements Serializable {
    
    private static final long serialVersionUID = -6605609934135069566L;
    
    /**
     * Service is ephemeral or persistence.
     */
    private boolean ephemeral = true;
    
    /**
     * protect threshold.
     */
    private float protectThreshold = 0.0F;
    
    /**
     * Type of {@link Selector}.
     */
    private Selector selector = new NoneSelector();
    
    private Map<String, String> extendData = new ConcurrentHashMap<>(1);
    
    private Map<String, ClusterMetadata> clusters = new ConcurrentHashMap<>(1);

}
public class InstanceMetadata implements Serializable {
    
    private static final long serialVersionUID = -8477858617353459226L;
    
    /**
     * instance weight.
     */
    private double weight = 1.0D;
    
    /**
     * If instance is enabled to accept request.
     */
    private boolean enabled = true;
    
    private Map<String, Object> extendData = new ConcurrentHashMap<>(1);
}

 

public class ExpiredMetadataInfo {
    
    private final Service service;
    
    private final String metadataId;
    
    private final long createTime;
    
    private ExpiredMetadataInfo(Service service, String metadataId) {
        this.service = service;
        this.metadataId = metadataId;
        this.createTime = System.currentTimeMillis();
    }
}

上面3个类的数据大部分对我们来说非常熟悉了。不用再赘述了。不懂得同学回头翻翻之前得文章看看。

这里单独说服务下面2个熟悉 
// 暂时大家知道是做筛选器使用场景得后面碰到再细讲,还记得服务实例查询得过滤吗用的就这个东西
private Selector selector = new NoneSelector();
 //存储服务下集群元数据 key:集群名称    
private Map<String, ClusterMetadata> clusters = new ConcurrentHashMap<>(1);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值