##### Coverage Report - com.ctrip.apollo.portal.PortalSettings

Classes in this File | Line Coverage | Branch Coverage | Complexity
PortalSettings |
65% | 15/23
50% | 4/8
| 2.5714285714285716;2.571
PortalSettings$HealthCheckTask |
74% | 23/31
58% | 7/12
| 2.5714285714285716;2.571

1 |  |
     package com.ctrip.apollo.portal;
2 |  |
3 |  |
     import java.util.ArrayList;
4 |  |
     import java.util.HashMap;
5 |  |
     import java.util.LinkedList;
6 |  |
     import java.util.List;
7 |  |
     import java.util.Map;
8 |  |
     import java.util.concurrent.ConcurrentHashMap;
9 |  |
     import java.util.concurrent.Executors;
10 |  |
     import java.util.concurrent.ScheduledExecutorService;
11 |  |
     import java.util.concurrent.TimeUnit;
12 |  |
13 |  |
     import javax.annotation.PostConstruct;
14 |  |
15 |  |
     import org.slf4j.Logger;
16 |  |
     import org.slf4j.LoggerFactory;
17 |  |
     import org.springframework.beans.factory.annotation.Autowired;
18 |  |
     import org.springframework.beans.factory.annotation.Value;
19 |  |
     import org.springframework.boot.actuate.health.Health;
20 |  |
     import org.springframework.context.ApplicationContext;
21 |  |
     import org.springframework.stereotype.Component;
22 |  |
23 |  |
     import com.ctrip.apollo.core.enums.Env;
24 |  |
     import com.ctrip.apollo.portal.api.AdminServiceAPI;
25 |  |
26 |  |
     @Component
27 | 11 |
     public class PortalSettings {
28 |  |
29 | 1 |
       private Logger logger = LoggerFactory.getLogger(PortalSettings.class);
30 |  |
31 |  |
       private static final int HEALTH_CHECK_INTERVAL = 5000;
32 |  |
33 |  |
       @Value("#{'${apollo.portal.env}'.split(',')}")
34 |  |
       private List<String> allStrEnvs;
35 |  |
36 |  |
       @Autowired
37 |  |
       ApplicationContext applicationContext;
38 |  |
39 | 1 |
       private List<Env> allEnvs = new ArrayList<Env>();
40 |  |
41 |  |
       private List<Env> activeEnvs;
42 |  |
43 |  |
       //mark env up or down
44 | 1 |
       private Map<Env, Boolean> envStatusMark = new ConcurrentHashMap<>();
45 |  |
46 |  |
       private ScheduledExecutorService healthCheckService;
47 |  |
48 |  |
       @PostConstruct
49 |  |
       private void postConstruct() {
50 |  |
         //init origin config envs
51 | 1 |
         for (String e : allStrEnvs) {
52 | 1 |
           allEnvs.add(Env.valueOf(e.toUpperCase()));
53 | 1 |
         }
54 |  |
55 | 1 |
         for (Env env : allEnvs) {
56 | 1 |
           envStatusMark.put(env, true);
57 | 1 |
         }
58 |  |
59 | 1 |
         healthCheckService = Executors.newScheduledThreadPool(1);
60 |  |
61 | 1 |
         healthCheckService
62 | 1 |
             .scheduleWithFixedDelay(new HealthCheckTask(applicationContext), 1000, HEALTH_CHECK_INTERVAL,
63 |  |
                                     TimeUnit.MILLISECONDS);
64 |  |
65 | 1 |
       }
66 |  |
67 |  |
       public List<Env> getActiveEnvs() {
68 | 0 |
         List<Env> activeEnvs = new LinkedList<>();
69 | 0 |
         for (Env env : allEnvs) {
70 | 0 |
           if (envStatusMark.get(env)) {
71 | 0 |
             activeEnvs.add(env);
72 |  |
           }
73 | 0 |
         }
74 | 0 |
         this.activeEnvs = activeEnvs;
75 | 0 |
         return activeEnvs;
76 |  |
       }
77 |  |
78 |  |
       public Env getFirstAliveEnv() {
79 | 0 |
         return activeEnvs.get(0);
80 |  |
       }
81 |  |
82 |  |
83 | 1 |
       class HealthCheckTask implements Runnable {
84 |  |
85 |  |
         private static final int ENV_DIED_THREADHOLD = 2;
86 |  |
87 | 1 |
         private Map<Env, Long> healthCheckFailCnt = new HashMap<>();
88 |  |
89 |  |
         private AdminServiceAPI.HealthAPI healthAPI;
90 |  |
91 | 1 |
         public HealthCheckTask(ApplicationContext context) {
92 | 1 |
           healthAPI = context.getBean(AdminServiceAPI.HealthAPI.class);
93 | 1 |
           for (Env env : allEnvs) {
94 | 1 |
             healthCheckFailCnt.put(env, 0l);
95 | 1 |
           }
96 | 1 |
         }
97 |  |
98 |  |
         public void run() {
99 |  |
100 | 2 |
           for (Env env : allEnvs) {
101 |  |
             try {
102 | 2 |
               if (isUp(env)) {
103 |  |
                 //revive
104 | 0 |
                 if (!envStatusMark.get(env)) {
105 | 0 |
                   envStatusMark.put(env, true);
106 | 0 |
                   healthCheckFailCnt.put(env, 0l);
107 | 0 |
                   logger.info("env up again [env:{}]", env);
108 |  |
                 }
109 |  |
               } else {
110 |  |
                 //maybe meta server up but admin server down
111 | 0 |
                 handleEnvDown(env);
112 |  |
               }
113 |  |
114 | 2 |
             } catch (Exception e) {
115 |  |
               //maybe meta server down
116 | 2 |
               logger.warn("health check fail. [env:{}]", env, e.getMessage());
117 | 2 |
               handleEnvDown(env);
118 | 0 |
             }
119 | 2 |
           }
120 |  |
121 | 2 |
         }
122 |  |
123 |  |
         private boolean isUp(Env env) {
124 | 2 |
           Health health = healthAPI.health(env);
125 | 0 |
           return "UP".equals(health.getStatus().getCode());
126 |  |
         }
127 |  |
128 |  |
         private void handleEnvDown(Env env) {
129 | 2 |
           long failCnt = healthCheckFailCnt.get(env);
130 | 2 |
           healthCheckFailCnt.put(env, ++failCnt);
131 |  |
132 | 2 |
           if (!envStatusMark.get(env)) {
133 | 0 |
             logger.warn("[env:{}] down yet.", env);
134 |  |
           } else {
135 | 2 |
             if (failCnt >= ENV_DIED_THREADHOLD) {
136 | 1 |
               envStatusMark.put(env, false);
137 | 1 |
               logger.error("env turn to down [env:{}]", env);
138 |  |
             } else {
139 | 1 |
               logger.warn("env health check fail first time. [env:{}]", env);
140 |  |
             }
141 |  |
           }
142 |  |
143 | 2 |
         }
144 |  |
145 |  |
       }
146 |  |
     }