content: 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", true));
  
      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));
          }
        }
  
      }
  
    }
  }
  