Commit 1cc43d08 by Yiming Liu

Integrate env & service discovery in client

parent bde0a061
......@@ -3,10 +3,12 @@ spring:
name: apollo-adminservice
server:
port: ${port:8080}
port: ${port:23646}
logging:
level:
org.springframework.cloud: 'DEBUG'
file: /opt/logs/apollo-adminservice.log
ctrip:
appid: 100003172
......@@ -39,13 +39,6 @@
<artifactId>guava</artifactId>
</dependency>
<!-- end of util -->
<!-- log -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- end of log -->
<!-- test -->
<dependency>
<groupId>commons-logging</groupId>
......
......@@ -4,7 +4,8 @@ package com.ctrip.apollo.client.constants;
* @author Jason Song(song_s@ctrip.com)
*/
public class Constants {
public static final String APP_ID = "appId";
public static final String APP_ID = "app.id";
public static final String VERSION = "version";
public static final String DEFAULT_VERSION_NAME = "latest-release";
public static final String ENV = "env";
}
package com.ctrip.apollo.client.env;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ctrip.apollo.Apollo;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.client.constants.Constants;
import com.ctrip.apollo.core.MetaDomainConsts;
import com.ctrip.apollo.core.utils.StringUtils;
public class ClientEnvironment {
private static final Logger logger = LoggerFactory.getLogger(ClientEnvironment.class);
private final static String DEFAULT_FILE = "/apollo.properties";
private AtomicReference<Env> env = new AtomicReference<Env>();
private static ClientEnvironment instance = new ClientEnvironment();
private ClientEnvironment() {
}
public static ClientEnvironment getInstance() {
return instance;
}
public Env getEnv() {
if (env.get() == null) {
Env resultEnv = Apollo.getEnv();
Properties apolloProperties = null;
try {
apolloProperties = readConfigFile(DEFAULT_FILE, null);
} catch (IOException e) {
throw new IllegalArgumentException("Could not read Apollo properties");
}
if (apolloProperties != null) {
String strEnv = apolloProperties.getProperty(Constants.ENV);
if (!StringUtils.isBlank(strEnv)) {
resultEnv = Env.valueOf(strEnv.trim().toUpperCase());
}
}
env.compareAndSet(null, resultEnv);
}
if (env.get() == null) {
throw new IllegalArgumentException("Apollo env is not set");
}
return env.get();
}
public String getMetaServerDomainName() {
return MetaDomainConsts.getDomain(getEnv());
}
@SuppressWarnings("unchecked")
private Properties readConfigFile(String configPath, Properties defaults) throws IOException {
InputStream in = this.getClass().getResourceAsStream(configPath);
logger.info("Reading config from resource {}", configPath);
if (in == null) {
// load outside resource under current user path
Path path = new File(System.getProperty("user.dir") + configPath).toPath();
if (Files.isReadable(path)) {
in = new FileInputStream(path.toFile());
logger.info("Reading config from file {} ", path);
}
}
Properties props = new Properties();
if (defaults != null) {
props.putAll(defaults);
}
if (in != null) {
props.load(in);
}
StringBuilder sb = new StringBuilder();
for (Enumeration<String> e = (Enumeration<String>) props.propertyNames(); e
.hasMoreElements();) {
String key = e.nextElement();
String val = (String) props.getProperty(key);
sb.append(key).append('=').append(val).append('\n');
}
logger.info("Reading properties: \n" + sb.toString());
return props;
}
}
......@@ -2,6 +2,8 @@ package com.ctrip.apollo.client.loader;
import com.ctrip.apollo.client.loader.impl.InMemoryConfigLoader;
import com.ctrip.apollo.client.loader.impl.LocalFileConfigLoader;
import com.ctrip.apollo.Apollo.Env;
import com.ctrip.apollo.client.env.ClientEnvironment;
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoader;
import com.ctrip.apollo.client.util.ConfigUtil;
import org.springframework.web.client.RestTemplate;
......@@ -10,14 +12,13 @@ import org.springframework.web.client.RestTemplate;
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigLoaderFactory {
private static ConfigLoaderFactory configLoaderFactory = new ConfigLoaderFactory();
private static ConfigLoaderFactory configLoaderFactory = new ConfigLoaderFactory();
private ConfigLoaderFactory() {
}
private ConfigLoaderFactory() {}
public static ConfigLoaderFactory getInstance() {
return configLoaderFactory;
}
public static ConfigLoaderFactory getInstance() {
return configLoaderFactory;
}
public ConfigLoader getLocalFileConfigLoader() {
ConfigLoader configLoader = new LocalFileConfigLoader();
......@@ -31,12 +32,17 @@ public class ConfigLoaderFactory {
}
public ConfigLoader getRemoteConfigLoader() {
ConfigLoader remoteConfigLoader = new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance());
ConfigLoader remoteConfigLoader = new RemoteConfigLoader(new RestTemplate(), ConfigUtil.getInstance(), new ConfigServiceLocator());
// remoteConfigLoader.setFallBackLoader(getInMemoryConfigLoader());
return remoteConfigLoader;
}
public ConfigLoaderManager getConfigLoaderManager() {
ClientEnvironment env = ClientEnvironment.getInstance();
if (env.getEnv().equals(Env.LOCAL)) {
return new ConfigLoaderManager(getLocalFileConfigLoader(), ConfigUtil.getInstance());
} else {
return new ConfigLoaderManager(getRemoteConfigLoader(), ConfigUtil.getInstance());
}
}
}
package com.ctrip.apollo.client.loader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;
import com.ctrip.apollo.client.env.ClientEnvironment;
import com.ctrip.apollo.core.serivce.ApolloService;
public class ConfigServiceLocator {
private static final Logger logger = LoggerFactory.getLogger(ConfigServiceLocator.class);
private RestTemplate restTemplate = new RestTemplate();
private List<ApolloService> serviceCaches = new ArrayList<>();
public List<ApolloService> getConfigServices() {
ClientEnvironment env = ClientEnvironment.getInstance();
String domainName = env.getMetaServerDomainName();
String url = domainName + "/services/config";
try {
ApolloService[] services = restTemplate.getForObject(new URI(url), ApolloService[].class);
if (services != null && services.length > 0) {
serviceCaches.clear();
for (ApolloService service : services) {
serviceCaches.add(service);
}
}
} catch (Exception e) {
logger.warn(e.getMessage());
}
return serviceCaches;
}
}
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.serivce.ApolloService;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -13,6 +15,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map;
/**
......@@ -24,10 +27,12 @@ public class RemoteConfigLoader extends AbstractConfigLoader {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class);
private final RestTemplate restTemplate;
private final ConfigUtil configUtil;
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil) {
private final ConfigServiceLocator serviceLocator;
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil, ConfigServiceLocator locator) {
this.restTemplate = restTemplate;
this.configUtil = configUtil;
this.serviceLocator = locator;
}
ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri, String cluster, ApolloRegistry apolloRegistry, ApolloConfig previousConfig) {
......@@ -78,9 +83,17 @@ public class RemoteConfigLoader extends AbstractConfigLoader {
@Override
protected ApolloConfig doLoadApolloConfig(ApolloRegistry apolloRegistry, ApolloConfig previous) {
ApolloConfig result = this.getRemoteConfig(restTemplate,
configUtil.getConfigServerUrl(), configUtil.getCluster(),
getConfigServiceUrl(), configUtil.getCluster(),
apolloRegistry, previous);
//When remote server return 304, we need to return the previous result
return result == null ? previous : result;
}
}
private String getConfigServiceUrl() {
List<ApolloService> services = serviceLocator.getConfigServices();
if(services.size()==0){
throw new RuntimeException("No available config service");
}
return services.get(0).getHomepageUrl();
}
}
\ No newline at end of file
......@@ -23,7 +23,7 @@ public class ConfigUtil {
public static final String APOLLO_PROPERTY = "apollo.properties";
private static ConfigUtil configUtil = new ConfigUtil();
private ApplicationContext applicationContext;
private ConfigUtil() {
}
......@@ -31,11 +31,6 @@ public class ConfigUtil {
return configUtil;
}
public String getConfigServerUrl() {
// TODO return the meta server url based on different environments
return "http://localhost";
}
public String getCluster() {
// TODO return the actual cluster
return "default";
......
appId=101
app.id=101
version=1.0
env=local
\ No newline at end of file
package com.ctrip.apollo.client.loader.impl;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import com.ctrip.apollo.client.loader.ConfigServiceLocator;
import com.ctrip.apollo.client.model.ApolloRegistry;
import com.ctrip.apollo.client.util.ConfigUtil;
import com.ctrip.apollo.core.dto.ApolloConfig;
import com.ctrip.apollo.core.serivce.ApolloService;
/**
* @author Jason Song(song_s@ctrip.com)
......@@ -35,12 +41,14 @@ public class RemoteConfigLoaderTest {
private RestTemplate restTemplate;
private ConfigUtil configUtil;
@Mock
private ConfigServiceLocator serviceLocater;
@Mock
private ResponseEntity<ApolloConfig> someResponse;
@Before
public void setUp() {
configUtil = spy(ConfigUtil.getInstance());
remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, configUtil));
remoteConfigLoader = spy(new RemoteConfigLoader(restTemplate, configUtil, serviceLocater));
}
@Test
......@@ -52,7 +60,12 @@ public class RemoteConfigLoaderTest {
ApolloRegistry apolloRegistry = assembleSomeApolloRegistry(someAppId, "someVersion");
ApolloConfig previousConfig = null;
when(configUtil.getConfigServerUrl()).thenReturn(someServerUrl);
ApolloService someService = new ApolloService();
someService.setHomepageUrl(someServerUrl);
List<ApolloService> someServices = new ArrayList<>();
someServices.add(someService);
when(serviceLocater.getConfigServices()).thenReturn(someServices);
when(configUtil.getCluster()).thenReturn(someCluster);
doReturn(apolloConfig).when(remoteConfigLoader)
.getRemoteConfig(restTemplate, someServerUrl, someCluster, apolloRegistry, previousConfig);
......@@ -115,7 +128,6 @@ public class RemoteConfigLoaderTest {
ApolloConfig result = remoteConfigLoader.getRemoteConfig(restTemplate, someServerUrl, someClusterName, apolloRegistry, previousConfig);
assertNull(result);
}
private ApolloRegistry assembleSomeApolloRegistry(long someAppId, String someVersion) {
......
package com.ctrip.apollo.metaservice.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ctrip.apollo.core.serivce.ApolloService;
import com.ctrip.apollo.metaservice.service.DiscoveryService;
import com.netflix.appinfo.InstanceInfo;
......@@ -18,17 +20,44 @@ public class ServiceController {
@RequestMapping("/meta")
public List<InstanceInfo> getMetaService() {
return discoveryService.getMetaServiceInstances();
public List<ApolloService> getMetaService() {
List<InstanceInfo> instances = discoveryService.getMetaServiceInstances();
List<ApolloService> result = new ArrayList<ApolloService>();
for (InstanceInfo instance : instances) {
ApolloService service = new ApolloService();
service.setAppName(instance.getAppName());
service.setInstanceId(instance.getInstanceId());
service.setHomepageUrl(instance.getHomePageUrl());
result.add(service);
}
return result;
}
@RequestMapping("/config")
public List<InstanceInfo> getConfigService() {
return discoveryService.getConfigServiceInstances();
public List<ApolloService> getConfigService() {
List<InstanceInfo> instances = discoveryService.getConfigServiceInstances();
List<ApolloService> result = new ArrayList<ApolloService>();
for (InstanceInfo instance : instances) {
ApolloService service = new ApolloService();
service.setAppName(instance.getAppName());
service.setInstanceId(instance.getInstanceId());
service.setHomepageUrl(instance.getHomePageUrl());
result.add(service);
}
return result;
}
@RequestMapping("/admin")
public List<InstanceInfo> getAdminService(){
return discoveryService.getAdminServiceInstances();
public List<ApolloService> getAdminService() {
List<InstanceInfo> instances = discoveryService.getAdminServiceInstances();
List<ApolloService> result = new ArrayList<ApolloService>();
for (InstanceInfo instance : instances) {
ApolloService service = new ApolloService();
service.setAppName(instance.getAppName());
service.setInstanceId(instance.getInstanceId());
service.setHomepageUrl(instance.getHomePageUrl());
result.add(service);
}
return result;
}
}
......@@ -6,7 +6,7 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ctrip.apollo.core.ServiceIdConsts;
import com.ctrip.apollo.core.ServiceNameConsts;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Application;
......@@ -18,17 +18,17 @@ public class DiscoveryService {
private EurekaClient eurekaClient;
public List<InstanceInfo> getConfigServiceInstances() {
Application application = eurekaClient.getApplication(ServiceIdConsts.APOLLO_CONFIGSERVICE);
Application application = eurekaClient.getApplication(ServiceNameConsts.APOLLO_CONFIGSERVICE);
return application != null ? application.getInstances() : new ArrayList<>();
}
public List<InstanceInfo> getMetaServiceInstances() {
Application application = eurekaClient.getApplication(ServiceIdConsts.APOLLO_METASERVICE);
Application application = eurekaClient.getApplication(ServiceNameConsts.APOLLO_METASERVICE);
return application != null ? application.getInstances() : new ArrayList<>();
}
public List<InstanceInfo> getAdminServiceInstances(){
Application application = eurekaClient.getApplication(ServiceIdConsts.APOLLO_ADMINSERVICE);
Application application = eurekaClient.getApplication(ServiceNameConsts.APOLLO_ADMINSERVICE);
return application != null ? application.getInstances() : new ArrayList<>();
}
}
......@@ -3,10 +3,12 @@ spring:
name: apollo-configservice
server:
port: ${port:80}
port: ${port:2756}
logging:
level:
org.springframework.cloud: 'DEBUG'
file: /opt/logs/apollo-configservice.log
ctrip:
appid: 100003171
\ No newline at end of file
......@@ -29,5 +29,11 @@
<artifactId>guava</artifactId>
</dependency>
<!-- end of util -->
<!-- log -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- end of log -->
</dependencies>
</project>
package com.ctrip.apollo;
public class Apollo {
public final static String VERSION = "java-0.0.1-SNAPSHOT";
private static Env m_env;
public enum Env {
LOCAL, DEV, FWS, FAT, UAT, LPT, PROD, TOOLS
}
public static void initialize(Env env) {
m_env = env;
}
public static Env getEnv() {
return m_env;
}
}
package com.ctrip.apollo.core;
import java.util.HashMap;
import java.util.Map;
import com.ctrip.apollo.Apollo.Env;
public class MetaDomainConsts {
public static final String LOCAL = "http://localhost";
public static final String DEV = "http://ws.meta.apollo.fx.dev.nt.ctripcorp.com";
public static final String FAT = "http://ws.meta.apollo.fx.fat.nt.ctripcorp.com";;
public static final String FWS = "http://ws.meta.apollo.fx.fws.nt.ctripcorp.com";
public static final String UAT = "http://ws.meta.apollo.fx.uat.nt.ctripcorp.com";
public static final String LPT = "http://ws.meta.apollo.fx.lpt.nt.ctripcorp.com";
public static final String TOOLS = "http://ws.meta.apollo.fx.tools.ctripcorp.com";
public static final String PRD = "http://ws.meta.apollo.fx.ctripcorp.com";
private static Map<Env, String> domains = new HashMap<>();
static {
domains.put(Env.LOCAL, LOCAL);
domains.put(Env.DEV, DEV);
domains.put(Env.FAT, FAT);
domains.put(Env.FWS, FWS);
domains.put(Env.UAT, UAT);
domains.put(Env.LPT, LPT);
domains.put(Env.TOOLS, TOOLS);
domains.put(Env.PROD, PRD);
}
public static String getDomain(Env env) {
return domains.get(env);
}
}
package com.ctrip.apollo.core;
public class ServiceIdConsts {
public class ServiceNameConsts {
public static final String APOLLO_METASERVICE = "apollo-metaservice";
......
package com.ctrip.apollo.core.serivce;
public class ApolloService {
private String appName;
private String instanceId;
private String homepageUrl;
public String getAppName() {
return appName;
}
public String getHomepageUrl() {
return homepageUrl;
}
public String getInstanceId() {
return instanceId;
}
public void setAppName(String appName) {
this.appName = appName;
}
public void setHomepageUrl(String homepageUrl) {
this.homepageUrl = homepageUrl;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
}
package com.ctrip.apollo.core.utils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApolloThreadFactory implements ThreadFactory {
private static Logger log = LoggerFactory.getLogger(ApolloThreadFactory.class);
private final AtomicLong m_threadNumber = new AtomicLong(1);
private final String m_namePrefix;
private final boolean m_daemon;
private final static ThreadGroup m_threadGroup = new ThreadGroup("Apollo");
public static ThreadGroup getThreadGroup() {
return m_threadGroup;
}
public static ThreadFactory create(String namePrefix, boolean daemon) {
return new ApolloThreadFactory(namePrefix, daemon);
}
public static boolean waitAllShutdown(int timeoutInMillis) {
ThreadGroup group = getThreadGroup();
Thread[] activeThreads = new Thread[group.activeCount()];
group.enumerate(activeThreads);
Set<Thread> alives = new HashSet<Thread>(Arrays.asList(activeThreads));
Set<Thread> dies = new HashSet<Thread>();
log.info("Current ACTIVE thread count is: {}", alives.size());
long expire = System.currentTimeMillis() + timeoutInMillis;
while (System.currentTimeMillis() < expire) {
classify(alives, dies, new ClassifyStandard<Thread>() {
@Override
public boolean satisfy(Thread t) {
return !t.isAlive() || t.isInterrupted() || t.isDaemon();
}
});
if (alives.size() > 0) {
log.info("Alive apollo threads: {}", alives);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
// ignore
}
} else {
log.info("All apollo threads are shutdown.");
return true;
}
}
log.warn("Some apollo threads are still alive but expire time has reached, alive threads: {}", alives);
return false;
}
private static interface ClassifyStandard<T> {
boolean satisfy(T t);
}
private static <T> void classify(Set<T> src, Set<T> des, ClassifyStandard<T> standard) {
Set<T> s = new HashSet<>();
for (T t : src) {
if (standard.satisfy(t)) {
s.add(t);
}
}
src.removeAll(s);
des.addAll(s);
}
private ApolloThreadFactory(String namePrefix, boolean daemon) {
m_namePrefix = namePrefix;
m_daemon = daemon;
}
public Thread newThread(Runnable r) {
Thread t = new Thread(m_threadGroup, r,//
m_threadGroup.getName() + "-" + m_namePrefix + "-" + m_threadNumber.getAndIncrement());
t.setDaemon(m_daemon);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
package com.ctrip.apollo.core.utils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
public class DNSUtil {
public static List<String> resolve(String domainName) throws UnknownHostException {
List<String> result = new ArrayList<String>();
InetAddress[] addresses = InetAddress.getAllByName(domainName);
if (addresses != null) {
for (InetAddress addr : addresses) {
result.add(addr.getHostAddress());
}
}
return result;
}
}
......@@ -9,3 +9,5 @@ spring:
username: sa
password: sa
ctrip:
appid: 100003173
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment