/** * Copyright 2016 vip.com. * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * </p> */ package com.vip.saturn.job.internal.failover; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.NodeCacheListener; import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vip.saturn.job.basic.JobScheduler; import com.vip.saturn.job.internal.config.ConfigurationService; import com.vip.saturn.job.internal.execution.ExecutionNode; import com.vip.saturn.job.internal.execution.ExecutionService; import com.vip.saturn.job.internal.listener.AbstractJobListener; import com.vip.saturn.job.internal.listener.AbstractListenerManager; import com.vip.saturn.job.internal.storage.JobNodePath; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * 失效转移监听管理器. */ public class FailoverListenerManager extends AbstractListenerManager { static Logger log = LoggerFactory.getLogger(FailoverListenerManager.class); private volatile boolean isShutdown = false; private final ConfigurationService configService; private final ExecutionService executionService; private final FailoverService failoverService; private final String executionPath; private final Set<String> runningAndFailoverPath; public FailoverListenerManager(final JobScheduler jobScheduler) { super(jobScheduler); configService = jobScheduler.getConfigService(); executionService = jobScheduler.getExecutionService(); failoverService = jobScheduler.getFailoverService(); executionPath = JobNodePath.getNodeFullPath(jobName, ExecutionNode.ROOT); runningAndFailoverPath = new HashSet<>(); } @Override public void start() { zkCacheManager.addTreeCacheListener(new ExecutionPathListener(), executionPath, 1); } @Override public void shutdown() { super.shutdown(); isShutdown = true; zkCacheManager.closeTreeCache(executionPath, 1); closeRunningAndFailoverNodeCaches(); } private void closeRunningAndFailoverNodeCaches() { Iterator<String> iterator = runningAndFailoverPath.iterator(); while(iterator.hasNext()) { String next = iterator.next(); zkCacheManager.closeNodeCache(next); } } private synchronized void failover(final Integer item) { if(jobScheduler == null || jobScheduler.getJob() == null) { return; } if(!jobScheduler.getJob().isFailoverSupported() || !configService.isFailover() || executionService.isCompleted(item)) { return; } failoverService.createCrashedFailoverFlag(item); if (!executionService.hasRunningItems(jobScheduler.getShardingService().getLocalHostShardingItems())) { failoverService.failoverIfNecessary(); } } class ExecutionPathListener extends AbstractJobListener { @Override protected void dataChanged(CuratorFramework client, TreeCacheEvent event, String path) { try { if (isShutdown) return; if(executionPath.equals(path)) { return; } int item = getItem(path); String runningPath = JobNodePath.getNodeFullPath(jobName, ExecutionNode.getRunningNode(item)); String failoverPath = JobNodePath.getNodeFullPath(jobName, FailoverNode.getExecutionFailoverNode(item)); switch (event.getType()) { case NODE_ADDED: zkCacheManager.addNodeCacheListener(new RunningPathListener(item), runningPath); runningAndFailoverPath.add(runningPath); zkCacheManager.addNodeCacheListener(new FailoverPathJobListener(item), failoverPath); runningAndFailoverPath.add(failoverPath); break; case NODE_REMOVED: zkCacheManager.closeNodeCache(runningPath); runningAndFailoverPath.remove(runningPath); zkCacheManager.closeNodeCache(failoverPath); runningAndFailoverPath.remove(failoverPath); break; default: } } catch (Exception e) { log.error(e.getMessage(), e); } } private int getItem(String path) { return Integer.parseInt(path.substring(path.lastIndexOf('/') + 1)); } } class RunningPathListener implements NodeCacheListener { private int item; public RunningPathListener(int item) { this.item = item; } @Override public void nodeChanged() throws Exception { zkCacheManager.getExecutorService().execute(new Runnable() { @Override public void run() { try { if (isShutdown) return; if (!executionService.isRunning(item)) { failover(item); } } catch (Throwable t) { log.error(t.getMessage(), t); } } }); } } class FailoverPathJobListener implements NodeCacheListener { private int item; public FailoverPathJobListener(int item) { this.item = item; } @Override public void nodeChanged() throws Exception { zkCacheManager.getExecutorService().execute(new Runnable() { @Override public void run() { try { if (isShutdown) return; if (!executionService.isFailover(item)) { failover(item); } } catch (Throwable t) { log.error(t.getMessage(), t); } } }); } } }