package com.ctrip.platform.dal.dao.markdown; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ctrip.platform.dal.dao.DalClientFactory; import com.ctrip.platform.dal.dao.Version; import com.ctrip.platform.dal.dao.client.DalConnection; import com.ctrip.platform.dal.dao.status.DalStatusManager; import com.ctrip.platform.dal.dao.status.DataSourceStatus; import com.ctrip.platform.dal.dao.status.MarkdownStatus; public class MarkdownManager { private static Logger logger = LoggerFactory.getLogger(MarkdownManager.class); private static final int durations = 1000; private static AtomicReference<ScheduledExecutorService> managerRef = new AtomicReference<>(); private static AtomicReference<List<ErrorDetector>> detectorsRef = new AtomicReference<>(); private static ConcurrentLinkedQueue<ErrorContext> exqueue = new ConcurrentLinkedQueue<ErrorContext>(); public static void init() { if(managerRef.get() !=null) return; synchronized (MarkdownManager.class) { if(managerRef.get() !=null) return; ArrayList<ErrorDetector> detectors = new ArrayList<ErrorDetector>(); // We currently only have Timeout case detectors.add(new TimeoutDetector()); detectorsRef.set(detectors); ScheduledExecutorService manager = Executors.newSingleThreadScheduledExecutor(); manager.scheduleAtFixedRate(new CollectExceptionTask(), durations, durations, TimeUnit.MICROSECONDS); managerRef.set(manager); } } public static void shutdown(){ if(managerRef.get() ==null) return; synchronized (MarkdownManager.class) { if(managerRef.get() ==null) return; managerRef.get().shutdownNow(); managerRef.set(null); logger.info("Markdown Manager has been destoryed"); } } public static void autoMarkdown(MarkDownInfo info) { DalStatusManager.getDataSourceStatus(info.getDbKey()).setAutoMarkdown(true); DalClientFactory.getDalLogger().info(String.format("Database %s has been marked down automatically", info.getDbKey())); DalClientFactory.getDalLogger().markdown(info); } public static void autoMarkup(MarkupInfo info) { DalStatusManager.getDataSourceStatus(info.getDbKey()).setAutoMarkdown(false); DalClientFactory.getDalLogger().info(String.format("Database %s has been marked up automatically", info.getDbKey())); DalClientFactory.getDalLogger().markup(info); } public static boolean isMarkdown(String key) { MarkdownStatus mcb = DalStatusManager.getMarkdownStatus(); if (mcb.isAppMarkdown()) return true; DataSourceStatus item = DalStatusManager.getDataSourceStatus(key); // Manual markdown can only be markup manually. if (item.isManualMarkdown()) return true; if(!mcb.isEnableAutoMarkdown()) return false; if (!item.isAutoMarkdown()) return false; // Timeout is not reached if ((System.currentTimeMillis() - item.getAutoMarkdownTime().getTime()) <= mcb.getAutoMarkupDelay() * 1000) return true; autoMarkup(new MarkupInfo(key, Version.getVersion(), 0)); return false; } /** * Clear all auto markdown */ public static void resetAutoMarkdowns() { for(String dbName: DalClientFactory.getDalConfigure().getDataSourceNames()) DalStatusManager.getDataSourceStatus(dbName).setAutoMarkdown(false); } public static void detect(DalConnection conn, long start, Throwable e) { if (conn == null || conn.getMeta() == null || !(e instanceof SQLException)) return; ErrorContext ctx = new ErrorContext( conn.getMeta().getDataBaseKeyName(), conn.getMeta().getDatabaseCategory(), System.currentTimeMillis() - start, (SQLException) e); exqueue.add(ctx); } private static class CollectExceptionTask implements Runnable { @Override public void run() { try { ErrorContext ctx = exqueue.poll(); while (ctx != null) { if(DalStatusManager.getMarkdownStatus().isEnableAutoMarkdown()) { if (!isMarkdown(ctx.getName())) { for (ErrorDetector mk : detectorsRef.get()) { mk.detect(ctx); } } } ctx = exqueue.poll(); } } catch (Throwable e) { e.printStackTrace(); } } } }