• nacos 发布配置


    server 保留 2 份配置文件,一份在 mysql,一份在本地磁盘,同时在内存中缓存配置文件的 md5 值。当客户端获取配置时,server 直接返回本地磁盘文件,使用的是 sendFile api

    FileInputStream fis = null;
    fis.getChannel().transferTo(0L, fis.getChannel().size(), Channels.newChannel(response.getOutputStream()));

    用户发布配置
    ConfigController#publishConfig

    // 更新数据库
    persistService.insertOrUpdate(srcIp, srcUser, configInfo, time, configAdvanceInfo, false);
    // 发布事件
    EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, time.getTime()));

    整个事件流

    public class EventDispatcher {
    
        /**
         * add event listener
         */
        static public void addEventListener(AbstractEventListener listener) {
            for (Class<? extends Event> type : listener.interest()) {
                getEntry(type).listeners.addIfAbsent(listener);
            }
        }
    
        /**
         * fire event, notify listeners.
         */
        static public void fireEvent(Event event) {
            if (null == event) {
                throw new IllegalArgumentException();
            }
    
            for (AbstractEventListener listener : getEntry(event.getClass()).listeners) {
                try {
                    listener.onEvent(event);
                } catch (Exception e) {
                    log.error(e.toString(), e);
                }
            }
        }
    
        /**
         * For only test purpose
         */
        static public void clear() {
            LISTENER_HUB.clear();
        }
    
        /**
         * get event listener for eventType. Add Entry if not exist.
         * 获取事件监听器,如果没有则新建 Entry
         */
        static Entry getEntry(Class<? extends Event> eventType) {
            for (; ; ) {
                for (Entry entry : LISTENER_HUB) {
                    if (entry.eventType == eventType) {
                        return entry;
                    }
                }
    
                Entry tmp = new Entry(eventType);
                /**
                 *  false means already exists
                 */
                if (LISTENER_HUB.addIfAbsent(tmp)) {
                    return tmp;
                }
            }
        }
    
        // 把事件和监听器关联起来
        static private class Entry {
            final Class<? extends Event> eventType;
            final CopyOnWriteArrayList<AbstractEventListener> listeners;
    
            Entry(Class<? extends Event> type) {
                eventType = type;
                listeners = new CopyOnWriteArrayList<AbstractEventListener>();
            }
    
            @Override
            public boolean equals(Object obj) {
                if (null == obj || obj.getClass() != getClass()) {
                    return false;
                }
                if (this == obj) {
                    return true;
                }
                return eventType == ((Entry)obj).eventType;
            }
    
            @Override
            public int hashCode() {
                return super.hashCode();
            }
    
        }
    
        static private final Logger log = LoggerFactory.getLogger(EventDispatcher.class);
    
        static final CopyOnWriteArrayList<Entry> LISTENER_HUB = new CopyOnWriteArrayList<Entry>();
    
        public interface Event {
        }
    
        static public abstract class AbstractEventListener {
    
            public AbstractEventListener() {
                // 执行 AsyncNotifyService 构造函数,把事件和监听器关联起来
                EventDispatcher.addEventListener(this);
            }
    
            /**
             * 感兴趣的事件列表
             *
             * @return event list
             */
            abstract public List<Class<? extends Event>> interest();
    
            /**
             * 处理事件
             *
             * @param event event
             */
            abstract public void onEvent(Event event);
        }
    
    }

    AsyncNotifyService

    @Service
    public class AsyncNotifyService extends AbstractEventListener {
    
        @Override
        public List<Class<? extends Event>> interest() {
            List<Class<? extends Event>> types = new ArrayList<Class<? extends Event>>();
            // 触发配置变更同步通知
            types.add(ConfigDataChangeEvent.class);
            return types;
        }
        
        @Override
        public void onEvent(Event event) {
    
            // 并发产生 ConfigDataChangeEvent
            if (event instanceof ConfigDataChangeEvent) {
                ConfigDataChangeEvent evt = (ConfigDataChangeEvent) event;
                long dumpTs = evt.lastModifiedTs;
                String dataId = evt.dataId;
                String group = evt.group;
                String tenant = evt.tenant;
                String tag = evt.tag;
                List<?> ipList = serverListService.getServerList();
    
                // 其实这里任何类型队列都可以
                Queue<NotifySingleTask> queue = new LinkedList<NotifySingleTask>();
                for (int i = 0; i < ipList.size(); i++) {
                    queue.add(new NotifySingleTask(dataId, group, tenant, tag, dumpTs, (String) ipList.get(i), evt.isBeta));
                }
                EXECUTOR.execute(new AsyncTask(httpclient, queue));
            }
        }
    }

    由以上可见,ConfigDataChangeEvent 事件由 AsyncNotifyService.onEvent 负责

    向集群中所有节点(包括自己)发送请求

    /v1/cs/communication/dataChange?dataId=oo.yml&group=xx&tenant=dev

    节点处理配置变更请求

    CommunicationController#notifyConfigInfo

    用数据库中的数据更新磁盘上的文件缓存

    dumpService.dump(dataId, group, tenant, tag, lastModifiedTs, handleIp);
    // DumpService#dump
    public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp,
                     boolean isBeta) {
        String groupKey = GroupKey2.getKey(dataId, group, tenant);
        dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));
    }
    @Service
    public class DumpService {
    
        @Autowired
        private Environment env;
    
        @Autowired
        PersistService persistService;
    
        @PostConstruct
        public void init() {
            LogUtil.defaultLog.warn("DumpService start");
            DumpProcessor processor = new DumpProcessor(this);
            dumpTaskMgr = new TaskManager("com.alibaba.nacos.server.DumpTaskManager");
            // 设置默认处理器
            dumpTaskMgr.setDefaultTaskProcessor(processor);
        }
    }

    TaskManager

    public TaskManager(String name) {
        this.name = name;
        if (null != name && name.length() > 0) {
            this.processingThread = new Thread(new ProcessRunnable(), name);
        } else {
            this.processingThread = new Thread(new ProcessRunnable());
        }
        this.processingThread.setDaemon(true);
        this.closed.set(false);
        this.processingThread.start();
    }
    
    
    class ProcessRunnable implements Runnable {
    
        @Override
        public void run() {
            while (!TaskManager.this.closed.get()) {
                try {
                    Thread.sleep(100);
                    TaskManager.this.process();
                } catch (Throwable e) {
                }
            }
    
        }
    
    }
    
    public void addTask(String type, AbstractTask task) {
        this.lock.lock();
        try {
            AbstractTask oldTask = tasks.put(type, task);
            MetricsMonitor.getDumpTaskMonitor().set(tasks.size());
            if (null != oldTask) {
                task.merge(oldTask);
            }
        } finally {
            this.lock.unlock();
        }
    }
    
    protected void process() {
        for (Map.Entry<String, AbstractTask> entry : this.tasks.entrySet()) {
            AbstractTask task = null;
            this.lock.lock();
            try {
                // 获取任务
                task = entry.getValue();
                if (null != task) {
                    if (!task.shouldProcess()) {
                        // 任务当前不需要被执行,直接跳过
                        continue;
                    }
                    // 先将任务从任务Map中删除
                    this.tasks.remove(entry.getKey());
                    MetricsMonitor.getDumpTaskMonitor().set(tasks.size());
                }
            } finally {
                this.lock.unlock();
            }
    
            if (null != task) {
                // 获取任务处理器
                TaskProcessor processor = this.taskProcessors.get(entry.getKey());
                if (null == processor) {
                    // DumpTask 使用的是默认处理器,即 DumpProcessor
                    processor = this.getDefaultTaskProcessor();
                }
                if (null != processor) {
                    boolean result = false;
                    try {
                        // 处理任务
                        result = processor.process(entry.getKey(), task);
                    } catch (Throwable t) {
                        log.error("task_fail", "处理task失败", t);
                    }
                    if (!result) {
                        // 任务处理失败,设置最后处理时间
                        task.setLastProcessTime(System.currentTimeMillis());
    
                        // 将任务重新加入到任务Map中
                        this.addTask(entry.getKey(), task);
                    }
                }
            }
        }
    
        if (tasks.isEmpty()) {
            this.lock.lock();
            try {
                this.notEmpty.signalAll();
            } finally {
                this.lock.unlock();
            }
        }
    }

    因此执行的是 DumpProcessor#process,读取数据库中的配置,更新本地磁盘文件,同时生成 LocalDataChangeEvent 事件。

    处理 LocalDataChangeEvent 事件

    // com.alibaba.nacos.config.server.service.LongPollingService#onEvent
    public void onEvent(Event event) {
        if (isFixedPolling()) {
            // ignore
        } else {
            if (event instanceof LocalDataChangeEvent) {
                LocalDataChangeEvent evt = (LocalDataChangeEvent)event;
                scheduler.execute(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps));
            }
        }
    }

    取消线程池中的定时任务,发送响应(变化的配置文件 id)给客户端

    com.alibaba.nacos.config.server.service.LongPollingService.DataChangeTask#run

    客观地讲,nacos 的代码细节不优雅,还在发展中。

  • 相关阅读:
    上传高德地图-express框架
    express不是内部命令
    elasticSearch 安装
    elasticSearch 分布式安装
    mongoDB基本操作
    [是题解哦] 洛谷 P1865 A % B Problem
    [是题解哦] 洛谷 P1531 I Hate It
    [是题解哦] 洛谷 P1195 口袋的天空
    [是题解哦] 洛谷 P1536 村村通
    [是模板哦] 快速读入
  • 原文地址:https://www.cnblogs.com/allenwas3/p/11662317.html
Copyright © 2020-2023  润新知