Nacos2.X 源码分析:为订阅方推送、服务健康检查、集群数据同步、grpc客户端服务端初始化

建议先看下面这篇文章,再看本文
Nacos2.X 服务注册、服务发现详解



为订阅者进行推送

从上面可知,我们在进行服务注册时最后会发布一个ServiceChangedEvent事件。在服务发现时,进行订阅服务的最后会发布一个ServiceSubscribedEvent事件。

我们能大致猜测:当一个服务的实例进行了更新,那么是不是应该推送给所有订阅了该服务的客户端?当我订阅了某个服务,是不是应该将该服务信息推送给我?

我们接下来就来看看这两个事件是如何进行处理的。

Nacos2.X 为订阅者进行推送 详细流程
在这里插入图片描述



服务注册与服务订阅流程中发布的事件最终会被NamingSubscriberServiceV2Impl.onEvent()中进行处理:

  • 如果是服务改变,那么就要推送给所有的订阅者
  • 如果是服务订阅者事件,那么就只给这一个订阅者进行推送即可
  • 最终都会调用到NacosDelayTaskExecuteEngine类的addTask()方法
@Override
public void onEvent(Event event) {
   
   
    if (!upgradeJudgement.isUseGrpcFeatures()) {
   
   
        return;
    }
    if (event instanceof ServiceEvent.ServiceChangedEvent) {
   
   
        // 如果是服务改变,那么就要推送给所有的订阅者,所以下面创建PushDelayTask对象的构造方法中只有两个参数
        ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event;
        Service service = serviceChangedEvent.getService();
        // 创建PushDelayTask对象的构造方法中只有两个参数
        delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay()));
        
    } else if (event instanceof ServiceEvent.ServiceSubscribedEvent) {
   
   
        // 如果是服务订阅者事件,那么就只给这一个订阅者进行推送即可,所以下面创建PushDelayTask对象的构造方法中只有三个参数
        ServiceEvent.ServiceSubscribedEvent subscribedEvent = (ServiceEvent.ServiceSubscribedEvent) event;
        Service service = subscribedEvent.getService();
        // 创建PushDelayTask对象的构造方法中只有三个参数,最后一个参数是订阅者的ClientId
        delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay(),
                                                           subscribedEvent.getClientId()));
    }
}

// 我们来看看PushDelayTask对象的两个构造方法
public class PushDelayTask extends AbstractDelayTask {
   
   
	...
    
    public PushDelayTask(Service service, long delay) {
   
   
        this.service = service;
        // 推送给所有订阅者标识为true,推送给某个目标订阅者为null
        pushToAll = true;
        targetClients = null;
        
        setTaskInterval(delay);
        setLastProcessTime(System.currentTimeMillis());
    }
    
    public PushDelayTask(Service service, long delay, String targetClient) {
   
   
        this.service = service;
		// 推送给所有订阅者标识为false,推送给某个目标订阅者为构造方法传过来的ClientId
        this.pushToAll = false;
        this.targetClients = new HashSet<>(1);
        this.targetClients.add(targetClient);
        
        setTaskInterval(delay);
        setLastProcessTime(System.currentTimeMillis());
    }
}



我们现在再来看看NacosDelayTaskExecuteEngine类的addTask()方法中是做了什么:

  • 构造方法中创建一个定时任务,定期的去执行processTasks()方法
  • addTask()方法就把我们上方PushDelayTask存入tasks这个集合中。注意PushDelayTask它是AbstractDelayTask的子类
  • processTasks()方法中会从tasks集合中取出所有的AbstractDelayTask,循环处理,通过AbstractDelayTask找各自对应的处理类去进行处理
public class NacosDelayTaskExecuteEngine extends AbstractNacosTaskExecuteEngine<AbstractDelayTask> {
   
   
    
    protected final ConcurrentHashMap<Object, AbstractDelayTask> tasks;
    
    ......
    
    // 构造方法中会执行一个定时任务
    public NacosDelayTaskExecuteEngine(String name, int initCapacity, Logger logger, long processInterval) {
   
   
        super(logger);
        tasks = new ConcurrentHashMap<>(initCapacity);
        processingExecutor = ExecutorFactory.newSingleScheduledExecutorService(new NameThreadFactory(name));
        // 该类初始化时会执行一个定时任务:ProcessRunnable,在该类的run()方法中会调用下面processTasks()方法
        processingExecutor
            .scheduleWithFixedDelay(new ProcessRunnable(), processInterval, processInterval, TimeUnit.MILLISECONDS);
    }
    
    ......

    // addTask()方法中仅仅是把我们传递过来的AbstractDelayTask对象存入一个Map集合中
    @Override
    public void addTask(Object key, AbstractDelayTask newTask) {
   
   
        lock.lock();
        try {
   
   
            AbstractDelayTask existTask = tasks.get(key);
            if (null != existTask) {
   
   
                newTask.merge(existTask);
            }
            // 这里会把AbstractDelayTask存进去
            tasks.put(key, newTask);
        } finally {
   
   
            lock.unlock();
        }
    }

    
    // 这里会每个一段时间定时执行,从集合中取出所有的AbstractDelayTask去进行处理
    protected void processTasks() {
   
   
        // 这里会把AbstractDelayTask全取出来进行处理
        Collection<Object> keys = getAllTaskKeys();
        // 遍历
        for (Object taskKey : keys) {
   
   
            AbstractDelayTask task = removeTask(taskKey);
            if (null == task) {
   
   
                continue;
            }
            // 根据不同不同类型的AbstractDelayTask,找对应的处理类
            NacosTaskProcessor processor = getProcessor(taskKey);
            if (null == processor) {
   
   
                getEngineLog().error("processor not found for task, so discarded. " + task);
                continue;
            }
            try {
   
   
                
                // 再调用各自对应的处理类的process()方法
                if (!processor.process(task)) {
   
   
                    retryFailedTask(taskKey, task);
                }
            } catch (Throwable e) {
   
   
                getEngineLog().error("Nacos task execute error ", e);
                retryFailedTask(taskKey, task);
            }
        }
    }
    
    ......
}



因为我们这里的是AbstractDelayTask的子类PushDelayTask类,它对应的处理类是PushDelayTaskProcessor。如下所示,就会进入到这里的process()

  • 该类会创建PushExecuteTask任务并执行
private static class PushDelayTaskProcessor implements NacosTaskProcessor {
   
   
    
    ...
    
    @Override
    public boolean process(NacosTask task) {
   
   
        // 强转,转换为最初的PushDelayTask类型
        PushDelayTask pushDelayTask = (PushDelayTask) task;
        // 取出service对象
        Service service = pushDelayTask.getService();
        // 从下面的方法名我们就可以看出来这又是异步执行一个task任务,所以我们重点关注new PushExecuteTask()这个类的run()方法
        NamingExecuteTaskDispatcher.getInstance()
                .dispatchAndExecuteTask(service, new PushExecuteTask(service, executeEngine, pushDelayTask));
        return true;
    }
}



PushExecuteTask类的run()方法

  • 根据 delayTask.isPushToAll() 来判断是否要推送当前服务的所有订阅者,返回所有订阅者或某一个订阅者
  • 推送NacosClient
public class PushExecuteTask extends AbstractExecuteTask {
   
   
    
    private final Service service;
    
    private final PushDelayTaskExecuteEngine delayTaskEngine;
    
    private final PushDelayTask delayTask;
    
    public PushExecuteTask(Service service, PushDelayTaskExecuteEngine delayTaskEngine, PushDelayTask delayTask) {
   
   
        this.service = service;
        this.delayTaskEngine = delayTaskEngine;
        this.delayTask = delayTask;
    }
    
    @Override
    public void run() {
   
   
        // 将封装了service信息的wrapper推送给客户端
        try {
   
   
            PushDataWrapper wrapper = generatePushData();
            ClientManager clientManager = delayTaskEngine.getClientManager();
            // 根据 delayTask.isPushToAll() 来判断是否要推送当前服务的所有订阅者,返回所有订阅者或某一个订阅者
            for (String each : getTargetClientIds()) {
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值