Coverage Report - com.ctrip.framework.apollo.portal.PortalSettings
 
Classes in this File Line Coverage Branch Coverage Complexity
PortalSettings
62%
17/27
50%
5/10
2.714
PortalSettings$HealthCheckTask
77%
24/31
66%
8/12
2.714
 
 1  
 package com.ctrip.framework.apollo.portal;
 2  
 
 3  
 
 4  
 import java.util.ArrayList;
 5  
 import java.util.Arrays;
 6  
 import java.util.HashMap;
 7  
 import java.util.LinkedList;
 8  
 import java.util.List;
 9  
 import java.util.Map;
 10  
 import java.util.concurrent.ConcurrentHashMap;
 11  
 import java.util.concurrent.Executors;
 12  
 import java.util.concurrent.ScheduledExecutorService;
 13  
 import java.util.concurrent.TimeUnit;
 14  
 
 15  
 import javax.annotation.PostConstruct;
 16  
 
 17  
 import org.slf4j.Logger;
 18  
 import org.slf4j.LoggerFactory;
 19  
 import org.springframework.beans.factory.annotation.Autowired;
 20  
 import org.springframework.beans.factory.annotation.Value;
 21  
 import org.springframework.boot.actuate.health.Health;
 22  
 import org.springframework.context.ApplicationContext;
 23  
 import org.springframework.stereotype.Component;
 24  
 
 25  
 import com.ctrip.framework.apollo.core.enums.Env;
 26  
 import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
 27  
 import com.ctrip.framework.apollo.portal.entity.po.ServerConfig;
 28  
 import com.ctrip.framework.apollo.portal.repository.ServerConfigRepository;
 29  
 
 30  
 @Component
 31  45
 public class PortalSettings {
 32  
 
 33  1
   private Logger logger = LoggerFactory.getLogger(PortalSettings.class);
 34  
 
 35  
   private static final int HEALTH_CHECK_INTERVAL = 5000;
 36  
 
 37  
   @Value("#{'${apollo.portal.envs}'.split(',')}")
 38  
   private List<String> allStrEnvs;
 39  
 
 40  
   @Autowired
 41  
   ApplicationContext applicationContext;
 42  
 
 43  
   @Autowired
 44  
   private ServerConfigRepository serverConfigRepository;
 45  
 
 46  1
   private List<Env> allEnvs = new ArrayList<Env>();
 47  
 
 48  
   private List<Env> activeEnvs;
 49  
 
 50  
   //mark env up or down
 51  1
   private Map<Env, Boolean> envStatusMark = new ConcurrentHashMap<>();
 52  
 
 53  
   private ScheduledExecutorService healthCheckService;
 54  
 
 55  
   @PostConstruct
 56  
   private void postConstruct() {
 57  
     //初始化portal支持操作的环境集合,线上的portal可能支持所有的环境操作,而线下环境则支持一部分.
 58  
     // 每个环境的portal支持哪些环境配置在数据库里
 59  1
     ServerConfig serverConfig = serverConfigRepository.findByKey("apollo.portal.envs");
 60  1
     if (serverConfig != null){
 61  0
       String[] configedEnvs = serverConfig.getValue().split(",");
 62  0
       allStrEnvs = Arrays.asList(configedEnvs);
 63  
     }
 64  
 
 65  1
     for (String e : allStrEnvs) {
 66  3
       allEnvs.add(Env.valueOf(e.toUpperCase()));
 67  3
     }
 68  
 
 69  
 
 70  1
     for (Env env : allEnvs) {
 71  3
       envStatusMark.put(env, true);
 72  3
     }
 73  
 
 74  1
     healthCheckService = Executors.newScheduledThreadPool(1);
 75  
 
 76  1
     healthCheckService
 77  1
         .scheduleWithFixedDelay(new HealthCheckTask(applicationContext), 1000, HEALTH_CHECK_INTERVAL,
 78  
                                 TimeUnit.MILLISECONDS);
 79  
 
 80  1
   }
 81  
 
 82  
   public List<Env> getActiveEnvs() {
 83  0
     List<Env> activeEnvs = new LinkedList<>();
 84  0
     for (Env env : allEnvs) {
 85  0
       if (envStatusMark.get(env)) {
 86  0
         activeEnvs.add(env);
 87  
       }
 88  0
     }
 89  0
     this.activeEnvs = activeEnvs;
 90  0
     return activeEnvs;
 91  
   }
 92  
 
 93  
   public Env getFirstAliveEnv() {
 94  0
     return activeEnvs.get(0);
 95  
   }
 96  
 
 97  
 
 98  1
   class HealthCheckTask implements Runnable {
 99  
 
 100  
     private static final int ENV_DIED_THREADHOLD = 2;
 101  
 
 102  1
     private Map<Env, Long> healthCheckFailCnt = new HashMap<>();
 103  
 
 104  
     private AdminServiceAPI.HealthAPI healthAPI;
 105  
 
 106  1
     public HealthCheckTask(ApplicationContext context) {
 107  1
       healthAPI = context.getBean(AdminServiceAPI.HealthAPI.class);
 108  1
       for (Env env : allEnvs) {
 109  3
         healthCheckFailCnt.put(env, 0l);
 110  3
       }
 111  1
     }
 112  
 
 113  
     public void run() {
 114  
 
 115  4
       for (Env env : allEnvs) {
 116  
         try {
 117  12
           if (isUp(env)) {
 118  
             //revive
 119  0
             if (!envStatusMark.get(env)) {
 120  0
               envStatusMark.put(env, true);
 121  0
               healthCheckFailCnt.put(env, 0l);
 122  0
               logger.info("env up again [env:{}]", env);
 123  
             }
 124  
           } else {
 125  
             //maybe meta server up but admin server down
 126  0
             handleEnvDown(env);
 127  
           }
 128  
 
 129  12
         } catch (Exception e) {
 130  
           //maybe meta server down
 131  12
           logger.warn("health check fail. [env:{}]", env, e.getMessage());
 132  12
           handleEnvDown(env);
 133  0
         }
 134  12
       }
 135  
 
 136  4
     }
 137  
 
 138  
     private boolean isUp(Env env) {
 139  12
       Health health = healthAPI.health(env);
 140  0
       return "UP".equals(health.getStatus().getCode());
 141  
     }
 142  
 
 143  
     private void handleEnvDown(Env env) {
 144  12
       long failCnt = healthCheckFailCnt.get(env);
 145  12
       healthCheckFailCnt.put(env, ++failCnt);
 146  
 
 147  12
       if (!envStatusMark.get(env)) {
 148  6
         logger.warn("[env:{}] down yet.", env);
 149  
       } else {
 150  6
         if (failCnt >= ENV_DIED_THREADHOLD) {
 151  3
           envStatusMark.put(env, false);
 152  3
           logger.error("env turn to down [env:{}]", env);
 153  
         } else {
 154  3
           logger.warn("env health check fail first time. [env:{}]", env);
 155  
         }
 156  
       }
 157  
 
 158  12
     }
 159  
 
 160  
   }
 161  
 }