Nacos源码学习系列服务端第4篇服务注册之注册事件

Nacos通过前面3篇的讲解我们已经见识到了事件几乎充斥再各个方法里面各种事件让人眼花缭乱,服务注册要发布ClientRegisterServiceEvent和InstanceMetadataEvent事件, 客户端初次要发布ClientChangedEvent事件,而且一个事件的处理完毕又会触发另外一个事件,同时因为事件都是基于异步的处理方式所以调试起来不方便,基于Nacos事件机制的复杂性,这里我不一上来就讲解事件机制。而是先根据几个具体的事件及处理逻辑让大家对Nacos事件有一个逐步的了解,有了一定的基础后再学习Nacos复杂的事件机制

发布了哪些事件

public class EphemeralClientOperationServiceImpl implements ClientOperationService {
    ...
    @Override
    public void registerInstance(Service service, Instance instance, String clientId) {
        ...
        //注册完成发布事件
        NotifyCenter.publishEvent(new 
             ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
        NotifyCenter.publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, 
        //参考InstancePublishInfo genMetadataId() 方法
        //格式是实例的 ip:port:cluster
        instanceInfo.getMetadataId(), false));
    }
    ...
} 

public class AbstractClient {
    .... 

    @Override
    public boolean addServiceInstance(Service service, InstancePublishInfo 
              instancePublishInfo) {
        //  客户端务发布服务实例布信息
        if (null == publishers.put(service, instancePublishInfo)) {
            MetricsMonitor.incrementInstanceCount();
        }
        //统一事件中心:客户端变化事件  
        NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(this));
        return true;
    ....
    }
}

ClientRegisterServiceEvent


//服务注册 下线 订阅 取消订阅的事件处理类
public class ClientServiceIndexesManager extends SmartSubscriber {
    //服务的所有发布者列表
    private final ConcurrentMap<Service, Set<String>> publisherIndexes = new ConcurrentHashMap<>();

    //服务的所有订阅者列表
    private final ConcurrentMap<Service, Set<String>> subscriberIndexes = new ConcurrentHashMap<>();
    
   //这块涉及到Nacos的统一事件机制后面再解析 这边读者暂时只需要知道是去NotifyCenter 注册下自己 至于为啥注册 怎么注册 后面的逻辑是怎么样的 后续再讲解
    public ClientServiceIndexesManager() {
        NotifyCenter.registerSubscriber(this, 
                  NamingEventPublisherFactory.getInstance());
    }
    
    //得到某个服务的所有注册客户端
    public Collection<String> getAllClientsRegisteredService(Service service) {
        return publisherIndexes.containsKey(service) ? publisherIndexes.get(service) : 
             new ConcurrentHashSet<>();
    }
    
    //得到某个服务的所有订阅客户端
    public Collection<String> getAllClientsSubscribeService(Service service) {
        return subscriberIndexes.containsKey(service) ? subscriberIndexes.get(service) : 
             new ConcurrentHashSet<>();
    }
    //所有订阅的服务
    public Collection<Service> getSubscribedService() {
        return subscriberIndexes.keySet();
    }
    
    /**
     * Clear the service index without instances.
     *
     * @param service The service of the Nacos.
     */
    public void removePublisherIndexesByEmptyService(Service service) {
        if (publisherIndexes.containsKey(service) && publisherIndexes.get(service).isEmpty()) {
            publisherIndexes.remove(service);
        }
    }
    
    //该订阅器订阅哪些类型的事件
    @Override
    public List<Class<? extends Event>> subscribeTypes() {
        List<Class<? extends Event>> result = new LinkedList<>();
        result.add(ClientOperationEvent.ClientRegisterServiceEvent.class);
        result.add(ClientOperationEvent.ClientDeregisterServiceEvent.class);
        result.add(ClientOperationEvent.ClientSubscribeServiceEvent.class);
        result.add(ClientOperationEvent.ClientUnsubscribeServiceEvent.class);
        result.add(ClientEvent.ClientDisconnectEvent.class);
        return result;
    }
    
   //事件分2类
   //上线 下线 订阅 取消订阅属于 ClientOperationEvent 事件
    @Override
    public void onEvent(Event event) {
        if (event instanceof ClientEvent.ClientDisconnectEvent) {
            handleClientDisconnect((ClientEvent.ClientDisconnectEvent) event);
        } else if (event instanceof ClientOperationEvent) {
            handleClientOperation((ClientOperationEvent) event);
        }
    }
    
    //客户端下线事件的处理 
    private void handleClientDisconnect(ClientEvent.ClientDisconnectEvent event) {
        Client client = event.getClient();
        for (Service each : client.getAllSubscribeService()) {
            removeSubscriberIndexes(each, client.getClientId());
        }
        for (Service each : client.getAllPublishedService()) {
            removePublisherIndexes(each, client.getClientId());
        }
    }
    
    private void handleClientOperation(ClientOperationEvent event) {
        Service service = event.getService();
        String clientId = event.getClientId();
        //服务注册
        if (event instanceof ClientOperationEvent.ClientRegisterServiceEvent) {
            addPublisherIndexes(service, clientId);
        } else if (event instanceof ClientOperationEvent.ClientDeregisterServiceEvent) {
         //服务下线  
            removePublisherIndexes(service, clientId);
        } else if (event instanceof ClientOperationEvent.ClientSubscribeServiceEvent) {
         //服务订阅
            addSubscriberIndexes(service, clientId);
        } else if (event instanceof ClientOperationEvent.ClientUnsubscribeServiceEvent) 
           {
         //取消订阅
            removeSubscriberIndexes(service, clientId);
        }
    }
    
    //服务注册
    private void addPublisherIndexes(Service service, String clientId) {
        publisherIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());
        publisherIndexes.get(service).add(clientId);
        NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
    }
    
    //从发布服务列表中移除该服务的该clientid
    private void removePublisherIndexes(Service service, String clientId) {
        if (!publisherIndexes.containsKey(service)) {
            return;
        }
        publisherIndexes.get(service).remove(clientId);
        NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
    }
    
    private void addSubscriberIndexes(Service service, String clientId) {
        subscriberIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());
        // Fix #5404, Only first time add need notify event.
        if (subscriberIndexes.get(service).add(clientId)) {
            NotifyCenter.publishEvent(new ServiceEvent.ServiceSubscribedEvent(service, 
                clientId));
        }
    }
    
   //从该服务的订阅者列表中移除该clientid
    private void removeSubscriberIndexes(Service service, String clientId) {
        if (!subscriberIndexes.containsKey(service)) {
            return;
        }
        //某个服务没有订阅者是key也一并删除掉
        subscriberIndexes.get(service).remove(clientId);
        if (subscriberIndexes.get(service).isEmpty()) {
            subscriberIndexes.remove(service);
        }
    }
}

上面ClientServiceIndexesManager  这个就是 服务订阅 取消订阅 服务上下线4个事件的订阅者,这里面逻辑其实比较简单。核心就是维护了 subscriberIndexes  和 publisherIndexes 两个map。维护的是服务的订阅者信息和服务的发布者信息,其他方法也是对这2个map的元素的增加和删除操作。

 InstanceMetadataEvent

public class NamingMetadataManager extends SmartSubscriber {
    
    //存储过期的
    private final Set<ExpiredMetadataInfo> expiredMetadataInfos;
    
    private ConcurrentMap<Service, ServiceMetadata> serviceMetadataMap;
    
    //以service 为key 嵌套Map 中以instance 的 metadataId为key
    private ConcurrentMap<Service, ConcurrentMap<String, InstanceMetadata>> 
        instanceMetadataMap;

   ...
   
   //该订阅之支持3种事件类型
   //实例元数据变更事件
   //服务的元数据变更事件:
   //触发时机:
   //1、ServiceManage 中 空服务自动清理(检测到服务下没有实例) EmptyServiceAutoCleanerV2
   //2、(v1升级v2 双写移除服务)DoubleWriteServiceRemovalToV2Task
   //客户端断连事件
   //触发时机在EphemeralIpPortClientManager 讲解的时候有一个 5s 定时执行一次 
   //ExpiredClientCleaner任务的定时器定时清理么任何发布和订阅的客户端且已经到了过期时机 
   @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;
    }
    
    //3类事件的处理入口
    @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);
        }
    }
    
    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));
            }
        }
    }
    
    //检查serviceMetadataMap中是否包含了该service
    //如果包含该service且event.isExpired()为true创建一个只包含service 和 createTime信息的 
    //ExpiredMetadataInfo对象
    //如果包含该service且event.isExpired()为false 则移除该ExpiredMetadataInfo 对象
    //
    private void handleServiceMetadataEvent(MetadataEvent.ServiceMetadataEvent event) {
        Service service = event.getService();
        if (containServiceMetadata(service)) {
            updateExpiredInfo(event.isExpired(), 
                 ExpiredMetadataInfo.newExpiredServiceMetadata(service));
        }
    }
    
    //方法的意思如果instanceMetadataMap存储了实例的元数据信息根据event的isExpired属性
    //确定是否要创建一个基于service 和 metadataId的过期对象信息
    //服务注册上线的时候 isExpired 为false
    //服务主动下线 isExpired 为true
    //服务端心跳检查触发delete ip后 isExpired 为true 
    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()));
        }
    }
    
    //如果是过期就创建一个过期信息否则移除掉原有的过期信息
    //留个疑问学习完后面的章节后回头来解读:为什么不直接删除instanceMetadataMap里面 
    //metadataId对应的数据 而是做一个看似浪费内存和性能的操作呢?
    private void updateExpiredInfo(boolean expired, ExpiredMetadataInfo 
         expiredMetadataInfo) {
        if (expired) {
            expiredMetadataInfos.add(expiredMetadataInfo);
        } else {
            expiredMetadataInfos.remove(expiredMetadataInfo);
        }
    }
   
    //参考InstancePublishInfo genMetadataId() 方法
    //格式是实例的 ip:port:cluster
    public boolean containInstanceMetadata(Service service, String metadataId) {
        return instanceMetadataMap.containsKey(service) && 
             instanceMetadataMap.get(service).containsKey(metadataId);
    }
    
    //判断serviceMetadataMap中是否存在该service 
    public boolean containServiceMetadata(Service service) {
        return serviceMetadataMap.containsKey(service);
    }
}

//过期元数据封装类
public class ExpiredMetadataInfo {
    //服务信息
    private final Service service;
    //元数据id
    private final String metadataId;
    //对象的创建时间
    private final long createTime;
    ....
}

InstanceMetadataEvent 处理3类任务这里我们着重介绍MetadataEvent.InstanceMetadataEvent的处理逻辑简单来说就是再服务下线(主动下线和被动下线) 的时候在 expiredMetadataInfos 信息加一个过期对象(包含service metadataId)

被动下线就是我们前面章节讲的服务端心跳任务检查到30s后服务没有响应就下线服务的逻辑。

这里留一个疑问:

    为什么不直接删除instanceMetadataMap里面,metadataId对应的数据 而是做一个看似浪费内存和性能的操作呢?

服务元数据变更事件:
   触发时机:
   1、ServiceManager 中 空服务自动清理(检测到服务下没有实例)     EmptyServiceAutoCleanerV2
   2、(v1升级v2 双写移除服务)DoubleWriteServiceRemovalToV2Task
   这块等后面讲解到具体的场景的时候再具体解决,这里大家一笔带过就可以。

客户端断连事件
   触发时机:

   EphemeralIpPortClientManager 讲解的时候有一个 5s 定时执行一次 
   ExpiredClientCleaner任务的定时器定时清理么任何发布和订阅的客户端且已经到了过期时机读者可以前往客户端管理器那一篇看具体的逻辑

总结


下一篇我们继续讲解服务注册过程中出现的事件及嵌套事件并做下总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值