##### 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.7142857142857144;2.714
PortalSettings$HealthCheckTask |
77% | 24/31
66% | 8/12
| 2.7142857142857144;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 |  |
     }