Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
lepdou
committed
May 18, 2016
1 parent
fae4343
commit 56c36b7
Showing
9 changed files
with
161 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
144 changes: 137 additions & 7 deletions
144
apollo-portal/src/main/java/com/ctrip/apollo/portal/PortalSettings.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,165 @@ | ||
package com.ctrip.apollo.portal; | ||
|
||
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 java.util.concurrent.locks.Lock; | ||
import java.util.concurrent.locks.ReentrantLock; | ||
|
||
import javax.annotation.PostConstruct; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.boot.actuate.health.Health; | ||
import org.springframework.context.ApplicationContext; | ||
import org.springframework.stereotype.Component; | ||
|
||
import com.ctrip.apollo.core.enums.Env; | ||
import com.ctrip.apollo.portal.api.AdminServiceAPI; | ||
|
||
@Component | ||
public class PortalSettings { | ||
|
||
private Logger logger = LoggerFactory.getLogger(PortalSettings.class); | ||
|
||
private static final int HEALTH_CHECK_INTERVAL = 5000; | ||
|
||
@Value("#{'${apollo.portal.env}'.split(',')}") | ||
private List<String> env; | ||
private List<String> allStrEnvs; | ||
|
||
@Autowired | ||
ApplicationContext applicationContext; | ||
|
||
private List<Env> allEnvs = new ArrayList<Env>(); | ||
|
||
private volatile boolean updatedFromLastHealthCheck = true; | ||
|
||
private List<Env> envs = new ArrayList<Env>(); | ||
//for cache | ||
private List<Env> activeEnvs = new LinkedList<>(); | ||
|
||
//mark env up or down | ||
private Map<Env, Boolean> envStatusMark = new ConcurrentHashMap<>(); | ||
|
||
private ScheduledExecutorService healthCheckService; | ||
|
||
private Lock lock = new ReentrantLock(); | ||
|
||
@PostConstruct | ||
private void postConstruct() { | ||
for (String e : env) { | ||
envs.add(Env.valueOf(e.toUpperCase())); | ||
//init origin config envs | ||
for (String e : allStrEnvs) { | ||
allEnvs.add(Env.valueOf(e.toUpperCase())); | ||
} | ||
|
||
for (Env env : allEnvs) { | ||
envStatusMark.put(env, true); | ||
} | ||
|
||
healthCheckService = Executors.newScheduledThreadPool(1); | ||
|
||
healthCheckService | ||
.scheduleWithFixedDelay(new HealthCheckTask(applicationContext), 1000, HEALTH_CHECK_INTERVAL, | ||
TimeUnit.MILLISECONDS); | ||
|
||
} | ||
|
||
public List<Env> getEnvs() { | ||
public List<Env> getActiveEnvs() { | ||
if (updatedFromLastHealthCheck) { | ||
lock.lock(); | ||
//maybe refresh many times but not create a bad impression. | ||
activeEnvs = refreshActiveEnvs(); | ||
lock.unlock(); | ||
} | ||
return activeEnvs; | ||
} | ||
|
||
private List<Env> refreshActiveEnvs() { | ||
List<Env> envs = new LinkedList<>(); | ||
for (Env env : allEnvs) { | ||
if (envStatusMark.get(env)) { | ||
envs.add(env); | ||
} | ||
} | ||
logger.info("refresh active envs"); | ||
return envs; | ||
} | ||
|
||
public Env getFirstEnv(){ | ||
return envs.get(0); | ||
public Env getFirstAliveEnv() { | ||
return activeEnvs.get(0); | ||
} | ||
|
||
|
||
class HealthCheckTask implements Runnable { | ||
|
||
private static final int ENV_DIED_THREADHOLD = 2; | ||
|
||
private Map<Env, Long> healthCheckFailCnt = new HashMap<>(); | ||
|
||
private AdminServiceAPI.HealthAPI healthAPI; | ||
|
||
public HealthCheckTask(ApplicationContext context) { | ||
healthAPI = context.getBean(AdminServiceAPI.HealthAPI.class); | ||
for (Env env : allEnvs) { | ||
healthCheckFailCnt.put(env, 0l); | ||
} | ||
} | ||
|
||
public void run() { | ||
logger.info("admin server health check start..."); | ||
boolean hasUpdateStatus = false; | ||
|
||
for (Env env : allEnvs) { | ||
try { | ||
if (isUp(env)) { | ||
//revive | ||
if (!envStatusMark.get(env)) { | ||
envStatusMark.put(env, true); | ||
healthCheckFailCnt.put(env, 0l); | ||
hasUpdateStatus = true; | ||
logger.info("env up again [env:{}]", env); | ||
} | ||
} else { | ||
//maybe meta server up but admin server down | ||
handleEnvDown(env); | ||
hasUpdateStatus = true; | ||
} | ||
|
||
} catch (Exception e) { | ||
//maybe meta server down | ||
logger.warn("health check fail. [env:{}]", env, e.getMessage()); | ||
handleEnvDown(env); | ||
hasUpdateStatus = true; | ||
} | ||
} | ||
|
||
if (!hasUpdateStatus) { | ||
logger.info("admin server health check OK"); | ||
} | ||
updatedFromLastHealthCheck = hasUpdateStatus; | ||
} | ||
|
||
private boolean isUp(Env env) { | ||
Health health = healthAPI.health(env); | ||
return "UP".equals(health.getStatus().getCode()); | ||
} | ||
|
||
private void handleEnvDown(Env env) { | ||
long failCnt = healthCheckFailCnt.get(env); | ||
healthCheckFailCnt.put(env, ++failCnt); | ||
|
||
if (failCnt >= ENV_DIED_THREADHOLD) { | ||
envStatusMark.put(env, false); | ||
logger.error("env down [env:{}]", env); | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters