Commit 7ca4247f authored by Jason Song's avatar Jason Song

refactor portal meta server address logic a little bit

parent a81d7287
......@@ -2,12 +2,15 @@ package com.ctrip.framework.apollo.portal.environment;
import com.ctrip.framework.apollo.portal.component.config.PortalConfig;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* load meta server addressed from database.
* PortalDB.ServerConfig
*/
class DatabasePortalMetaServerProvider implements PortalMetaServerProvider {
private static final Logger logger = LoggerFactory.getLogger(DatabasePortalMetaServerProvider.class);
/**
* read config from database
......@@ -16,13 +19,9 @@ class DatabasePortalMetaServerProvider implements PortalMetaServerProvider {
private volatile Map<Env, String> addresses;
public DatabasePortalMetaServerProvider(
final PortalConfig portalConfig
) {
DatabasePortalMetaServerProvider(final PortalConfig portalConfig) {
this.portalConfig = portalConfig;
// will cause NullPointException if portalConfig is null
Map<String, String> map = portalConfig.getMetaServers();
addresses = Env.conversionKey(map);
reload();
}
@Override
......@@ -37,8 +36,9 @@ class DatabasePortalMetaServerProvider implements PortalMetaServerProvider {
@Override
public void reload() {
Map<String, String> map = portalConfig.getMetaServers();
addresses = Env.conversionKey(map);
Map<String, String> map = portalConfig.getMetaServers();
addresses = Env.transformToEnvMap(map);
logger.info("Loaded meta server addresses from portal config: {}", addresses);
}
}
package com.ctrip.framework.apollo.portal.environment;
import static com.ctrip.framework.apollo.portal.environment.Env.conversionKey;
import static com.ctrip.framework.apollo.portal.environment.Env.transformToEnvMap;
import com.ctrip.framework.apollo.core.utils.ResourceUtils;
import com.ctrip.framework.apollo.portal.util.KeyValueUtils;
......@@ -31,17 +31,10 @@ class DefaultPortalMetaServerProvider implements PortalMetaServerProvider {
*/
private static final String APOLLO_ENV_PROPERTIES_FILE_PATH = "apollo-env.properties";
private static final DefaultPortalMetaServerProvider INSTANCE = new DefaultPortalMetaServerProvider();
public static DefaultPortalMetaServerProvider getInstance() {
return INSTANCE;
}
private volatile Map<Env, String> domains;
private DefaultPortalMetaServerProvider() {
domains = initializeDomains();
DefaultPortalMetaServerProvider() {
reload();
}
@Override
......@@ -58,12 +51,13 @@ class DefaultPortalMetaServerProvider implements PortalMetaServerProvider {
@Override
public void reload() {
domains = initializeDomains();
logger.info("Loaded meta server addresses from system property, os environment and properties file: {}", domains);
}
/**
* load all environment's meta address dynamically when this class loaded by JVM
*/
private static Map<Env, String> initializeDomains() {
private Map<Env, String> initializeDomains() {
// add to domain
Map<Env, String> map = new ConcurrentHashMap<>();
......@@ -73,34 +67,33 @@ class DefaultPortalMetaServerProvider implements PortalMetaServerProvider {
map.putAll(getDomainsFromSystemProperty());
// log all
logger.info("Loaded meta server addresses: {}", map);
return map;
}
static Map<Env, String> getDomainsFromSystemProperty() {
private Map<Env, String> getDomainsFromSystemProperty() {
// find key-value from System Property which key ends with "_meta" (case insensitive)
Map<String, String> metaServerAddressesFromSystemProperty = KeyValueUtils.filterWithKeyIgnoreCaseEndsWith(System.getProperties(), "_meta");
// remove key's suffix "_meta" (case insensitive)
metaServerAddressesFromSystemProperty = KeyValueUtils.removeKeySuffix(metaServerAddressesFromSystemProperty, "_meta".length());
return conversionKey(metaServerAddressesFromSystemProperty);
return transformToEnvMap(metaServerAddressesFromSystemProperty);
}
static Map<Env, String> getDomainsFromOSEnvironment() {
private Map<Env, String> getDomainsFromOSEnvironment() {
// find key-value from OS environment variable which key ends with "_meta" (case insensitive)
Map<String, String> metaServerAddressesFromOSEnvironment = KeyValueUtils.filterWithKeyIgnoreCaseEndsWith(System.getenv(), "_meta");
// remove key's suffix "_meta" (case insensitive)
metaServerAddressesFromOSEnvironment = KeyValueUtils.removeKeySuffix(metaServerAddressesFromOSEnvironment, "_meta".length());
return conversionKey(metaServerAddressesFromOSEnvironment);
return transformToEnvMap(metaServerAddressesFromOSEnvironment);
}
static Map<Env, String> getDomainsFromPropertiesFile() {
private Map<Env, String> getDomainsFromPropertiesFile() {
// find key-value from properties file which key ends with ".meta" (case insensitive)
Properties properties = new Properties();
properties = ResourceUtils.readConfigFile(APOLLO_ENV_PROPERTIES_FILE_PATH, properties);
Map<String, String> metaServerAddressesFromPropertiesFile = KeyValueUtils.filterWithKeyIgnoreCaseEndsWith(properties, ".meta");
// remove key's suffix ".meta" (case insensitive)
metaServerAddressesFromPropertiesFile = KeyValueUtils.removeKeySuffix(metaServerAddressesFromPropertiesFile, ".meta".length());
return conversionKey(metaServerAddressesFromPropertiesFile);
return transformToEnvMap(metaServerAddressesFromPropertiesFile);
}
}
......@@ -198,7 +198,7 @@ public class Env {
* @param metaServerAddresses key is environment, value is environment's meta server address
* @return relationship between {@link Env} and meta server address
*/
public static Map<Env, String> conversionKey(Map<String, String> metaServerAddresses) {
static Map<Env, String> transformToEnvMap(Map<String, String> metaServerAddresses) {
// add to domain
Map<Env, String> map = new ConcurrentHashMap<>();
for(Map.Entry<String, String> entry : metaServerAddresses.entrySet()) {
......
......@@ -32,35 +32,34 @@ import org.springframework.stereotype.Service;
@Service
public class PortalMetaDomainService {
public static final String DEFAULT_META_URL = "http://apollo.meta";
private static final Logger logger = LoggerFactory.getLogger(PortalMetaDomainService.class);
private static final long REFRESH_INTERVAL_IN_SECOND = 60;// 1 min
static final String DEFAULT_META_URL = "http://apollo.meta";
// env -> meta server address cache
private static final Map<Env, String> metaServerAddressCache = Maps.newConcurrentMap();
private final Map<Env, String> metaServerAddressCache = Maps.newConcurrentMap();
/**
* initialize meta server provider without cache.
* Multiple {@link PortalMetaServerProvider}
*/
private final List<PortalMetaServerProvider> portalMetaServerProviders = new ArrayList<>();
private static final long REFRESH_INTERVAL_IN_SECOND = 60;// 1 min
private static final Logger logger = LoggerFactory.getLogger(PortalMetaDomainService.class);
// env -> meta server address cache
// comma separated meta server address -> selected single meta server address cache
private static final Map<String, String> selectedMetaServerAddressCache = Maps.newConcurrentMap();
private static final AtomicBoolean periodicRefreshStarted = new AtomicBoolean(false);
private final Map<String, String> selectedMetaServerAddressCache = Maps.newConcurrentMap();
private final AtomicBoolean periodicRefreshStarted = new AtomicBoolean(false);
public PortalMetaDomainService(final PortalConfig portalConfig) {
PortalMetaDomainService(final PortalConfig portalConfig) {
// high priority with data in database
portalMetaServerProviders.add(new DatabasePortalMetaServerProvider(portalConfig));
// System properties, OS environment, configuration file
portalMetaServerProviders.add(DefaultPortalMetaServerProvider.getInstance());
portalMetaServerProviders.add(new DefaultPortalMetaServerProvider());
}
/**
* Return one meta server address. If multiple meta server addresses are configured, will select one.
*/
synchronized public String getDomain(Env env) {
public String getDomain(Env env) {
String metaServerAddress = getMetaServerAddress(env);
// if there is more than one address, need to select one
if (metaServerAddress.contains(",")) {
......@@ -72,13 +71,12 @@ public class PortalMetaDomainService {
/**
* Return meta server address. If multiple meta server addresses are configured, will return the comma separated string.
*/
synchronized public String getMetaServerAddress(Env env) {
public String getMetaServerAddress(Env env) {
// in cache?
if (!metaServerAddressCache.containsKey(env)) {
// put it to cache
metaServerAddressCache.put(
env,
getMetaServerAddressCacheValue(portalMetaServerProviders, env)
metaServerAddressCache
.put(env, getMetaServerAddressCacheValue(portalMetaServerProviders, env)
);
}
......@@ -94,7 +92,7 @@ public class PortalMetaDomainService {
* @param env environment
* @return meta server address
*/
private static String getMetaServerAddressCacheValue(
private String getMetaServerAddressCacheValue(
Collection<PortalMetaServerProvider> providers, Env env) {
// null value
......@@ -139,7 +137,7 @@ public class PortalMetaDomainService {
* In production environment, we still suggest using one single domain like http://config.xxx.com(backed by software
* load balancers like nginx) instead of multiple ip addresses
*/
private static String selectMetaServerAddress(String metaServerAddresses) {
private String selectMetaServerAddress(String metaServerAddresses) {
String metaAddressSelected = selectedMetaServerAddressCache.get(metaServerAddresses);
if (metaAddressSelected == null) {
// initialize
......@@ -153,7 +151,7 @@ public class PortalMetaDomainService {
return metaAddressSelected;
}
private static void updateMetaServerAddresses(String metaServerAddresses) {
private void updateMetaServerAddresses(String metaServerAddresses) {
logger.debug("Selecting meta server address for: {}", metaServerAddresses);
Transaction transaction = Tracer.newTransaction("Apollo.MetaService", "refreshMetaServerAddress");
......@@ -197,7 +195,7 @@ public class PortalMetaDomainService {
}
}
private static void schedulePeriodicRefresh() {
private void schedulePeriodicRefresh() {
ScheduledExecutorService scheduledExecutorService =
Executors.newScheduledThreadPool(1, ApolloThreadFactory.create("MetaServiceLocator", true));
......
package com.ctrip.framework.apollo.portal.environment;
import com.ctrip.framework.apollo.tracer.spi.MessageProducer;
import com.ctrip.framework.apollo.tracer.spi.Transaction;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.junit.After;
import org.junit.Before;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.ServerSocket;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public abstract class BaseIntegrationTest {
protected static final int PORT = findFreePort();
private Server server;
......@@ -40,15 +32,6 @@ public abstract class BaseIntegrationTest {
}
@Before
public void setUp() throws Exception {
MessageProducer someProducer = mock(MessageProducer.class);
Transaction someTransaction = mock(Transaction.class);
when(someProducer.newTransaction(anyString(), anyString())).thenReturn(someTransaction);
}
@After
public void tearDown() throws Exception {
if (server != null && server.isStarted()) {
......@@ -56,7 +39,7 @@ public abstract class BaseIntegrationTest {
}
}
protected ContextHandler mockServerHandler(final int statusCode, final String response) {
ContextHandler mockServerHandler(final int statusCode, final String response) {
ContextHandler context = new ContextHandler("/");
context.setHandler(new AbstractHandler() {
......@@ -82,7 +65,7 @@ public abstract class BaseIntegrationTest {
* @return a free port number on localhost
* @throws IllegalStateException if unable to find a free port
*/
protected static int findFreePort() {
static int findFreePort() {
ServerSocket socket = null;
try {
socket = new ServerSocket(0);
......
......@@ -8,6 +8,7 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
......@@ -16,24 +17,41 @@ import org.mockito.junit.MockitoJUnitRunner;
public class DatabasePortalMetaServerProviderTest {
private DatabasePortalMetaServerProvider databasePortalMetaServerProvider;
@Mock
private PortalConfig portalConfig;
private Map<String, String> metaServiceMap;
@Before
public void init() {
public void setUp() {
MockitoAnnotations.initMocks(this);
// mock it
PortalConfig portalConfig = Mockito.mock(PortalConfig.class);
final Map<String, String> map = new HashMap<>();
map.put("nothing", "http://unknown.com");
map.put("dev", "http://server.com:8080");
Mockito.when(portalConfig.getMetaServers()).thenReturn(map);
metaServiceMap = new HashMap<>();
metaServiceMap.put("nothing", "http://unknown.com");
metaServiceMap.put("dev", "http://server.com:8080");
Mockito.when(portalConfig.getMetaServers()).thenReturn(metaServiceMap);
// use mocked object to construct
databasePortalMetaServerProvider = new DatabasePortalMetaServerProvider(portalConfig);
}
@Test
public void getMetaServerAddress() {
public void testGetMetaServerAddress() {
String address = databasePortalMetaServerProvider.getMetaServerAddress(Env.DEV);
assertEquals("http://server.com:8080", address);
String newMetaServerAddress = "http://another-server.com:8080";
metaServiceMap.put("dev", newMetaServerAddress);
databasePortalMetaServerProvider.reload();
assertEquals(newMetaServerAddress, databasePortalMetaServerProvider.getMetaServerAddress(Env.DEV));
}
@Test
public void testExists() {
assertTrue(databasePortalMetaServerProvider.exists(Env.DEV));
assertFalse(databasePortalMetaServerProvider.exists(Env.PRO));
assertTrue(databasePortalMetaServerProvider.exists(Env.addEnvironment("nothing")));
}
}
\ No newline at end of file
package com.ctrip.framework.apollo.portal.environment;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.ctrip.framework.apollo.portal.AbstractUnitTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class DefaultPortalMetaServerProviderTest extends AbstractUnitTest {
private final DefaultPortalMetaServerProvider defaultPortalMetaServerProvider = DefaultPortalMetaServerProvider.getInstance();
private DefaultPortalMetaServerProvider defaultPortalMetaServerProvider;
@Before
public void setUp() throws Exception {
defaultPortalMetaServerProvider = new DefaultPortalMetaServerProvider();
}
@After
public void tearDown() throws Exception {
System.clearProperty("dev_meta");
System.clearProperty("fat_meta");
defaultPortalMetaServerProvider.reload();
}
@Test
......@@ -42,10 +48,12 @@ public class DefaultPortalMetaServerProviderTest extends AbstractUnitTest {
String randomAddress = "randomAddress";
String randomEnvironment = "randomEnvironment";
System.setProperty(randomEnvironment + "_meta", randomAddress);
assertFalse(defaultPortalMetaServerProvider.exists(Env.addEnvironment(randomEnvironment)));
// reload above added
defaultPortalMetaServerProvider.reload();
assertEquals(randomAddress,
defaultPortalMetaServerProvider.getMetaServerAddress(Env.valueOf(randomEnvironment)));
assertTrue(defaultPortalMetaServerProvider.exists(Env.addEnvironment(randomEnvironment)));
// clear the property
System.clearProperty(randomEnvironment + "_meta");
......
......@@ -9,20 +9,21 @@ import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class PortalMetaDomainServiceTest extends BaseIntegrationTest {
@Mock
private PortalMetaDomainService portalMetaDomainService;
@Mock
private PortalConfig portalConfig;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
// mock it
PortalConfig portalConfig = Mockito.mock(PortalConfig.class);
final Map<String, String> map = new HashMap<>();
map.put("nothing", "http://unknown.com");
Mockito.when(portalConfig.getMetaServers()).thenReturn(map);
......
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