package com.ctrip.framework.apollo.portal.component; import com.ctrip.framework.apollo.core.MetaDomainConsts; import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.component.config.PortalConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.health.Health; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.annotation.PostConstruct; @Component public class PortalSettings { private static final Logger logger = LoggerFactory.getLogger(PortalSettings.class); private static final int HEALTH_CHECK_INTERVAL = 10 * 1000; @Autowired ApplicationContext applicationContext; @Autowired private PortalConfig portalConfig; private List<Env> allEnvs = new ArrayList<>(); //mark env up or down private Map<Env, Boolean> envStatusMark = new ConcurrentHashMap<>(); @PostConstruct private void postConstruct() { allEnvs = portalConfig.portalSupportedEnvs(); for (Env env : allEnvs) { envStatusMark.put(env, true); } ScheduledExecutorService healthCheckService = Executors.newScheduledThreadPool(1, ApolloThreadFactory.create("EnvHealthChecker", false)); healthCheckService .scheduleWithFixedDelay(new HealthCheckTask(applicationContext), 1000, HEALTH_CHECK_INTERVAL, TimeUnit.MILLISECONDS); } public List<Env> getAllEnvs() { return allEnvs; } public List<Env> getActiveEnvs() { List<Env> activeEnvs = new LinkedList<>(); for (Env env : allEnvs) { if (envStatusMark.get(env)) { activeEnvs.add(env); } } return activeEnvs; } public boolean isEnvActive(Env env) { Boolean mark = envStatusMark.get(env); return mark == null ? false : mark; } private class HealthCheckTask implements Runnable { private static final int ENV_DOWN_THRESHOLD = 2; private Map<Env, Integer> healthCheckFailedCounter = new HashMap<>(); private AdminServiceAPI.HealthAPI healthAPI; public HealthCheckTask(ApplicationContext context) { healthAPI = context.getBean(AdminServiceAPI.HealthAPI.class); for (Env env : allEnvs) { healthCheckFailedCounter.put(env, 0); } } public void run() { for (Env env : allEnvs) { try { if (isUp(env)) { //revive if (!envStatusMark.get(env)) { envStatusMark.put(env, true); healthCheckFailedCounter.put(env, 0); logger.info("Env revived because env health check success. env: {}", env); } } else { logger.error("Env health check failed, maybe because of admin server down. env: {}, meta server address: {}", env, MetaDomainConsts.getDomain(env)); handleEnvDown(env); } } catch (Exception e) { logger.error("Env health check failed, maybe because of meta server down " + "or configure wrong meta server address. env: {}, meta server address: {}", env, MetaDomainConsts.getDomain(env), e); handleEnvDown(env); } } } private boolean isUp(Env env) { Health health = healthAPI.health(env); return "UP".equals(health.getStatus().getCode()); } private void handleEnvDown(Env env) { int failedTimes = healthCheckFailedCounter.get(env); healthCheckFailedCounter.put(env, ++failedTimes); if (!envStatusMark.get(env)) { logger.error("Env is down. env: {}, failed times: {}, meta server address: {}", env, failedTimes, MetaDomainConsts.getDomain(env)); } else { if (failedTimes >= ENV_DOWN_THRESHOLD) { envStatusMark.put(env, false); logger.error("Env is down because health check failed for {} times, " + "which equals to down threshold. env: {}, meta server address: {}", ENV_DOWN_THRESHOLD, env, MetaDomainConsts.getDomain(env)); } else { logger.error( "Env health check failed for {} times which less than down threshold. down threshold:{}, env: {}, meta server address: {}", failedTimes, ENV_DOWN_THRESHOLD, env, MetaDomainConsts.getDomain(env)); } } } } }