/** * Project: doris.config.client-1.0-SNAPSHOT File Created at 2011-4-26 $Id: ConfigManagerImpl.java 97082 2011-06-27 * 07:15:00Z mian.hem $ Copyright 1999-2100 Alibaba.com Corporation Limited. All rights reserved. This software is the * confidential and proprietary information of Alibaba Company. ("Confidential Information"). You shall not disclose * such Confidential Information and shall use it only in accordance with the terms of the license agreement you entered * into with Alibaba.com. */ package com.alibaba.doris.common.config; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.alibaba.doris.common.adminservice.AdminServiceFactory; import com.alibaba.doris.common.adminservice.CommonConfigService; import com.alibaba.doris.common.adminservice.connenctor.AdminConnector; import com.alibaba.doris.common.util.PropertiesLoadUtil; /** * 配置管理客户端, 定时(默认是3000ms) 通过http 调用方式访问配置管理服务器取得最新配置 并推送给注册在 <code>ConfigManagerImpl</code>中的<code>ConfigListener</code> * 。 如果配置管理客户端没有取得最新配置,就不会推送给<code>ConfigListener</code>。 * * @see ConfigListener * @see ConfigConnector * @author mianhe */ public class ConfigManagerImpl implements ConfigManager { private static final Log logger = LogFactory.getLog(ConfigManagerImpl.class); // configuration location private String location; private long interval = 3000; // 客户端是否处于停止状态 private volatile boolean stopped = false; // 是否初始化成功 private volatile boolean initialized = false; private final Object lock = new Object(); private volatile List<ConfigListener> configListeners = new ArrayList<ConfigListener>(); private Properties properties = null; // 初始化的时候生成的独立线程,此线程用来定时去取最新配置 private Thread fetchThread; private CommonConfigService commonConfigService = AdminServiceFactory.getCommonConfigService(); private volatile Map<String, String> configurationCache = new ConcurrentHashMap<String, String>(); private ClientConfiguration clientConfiguration; /** * 初始化配置客户端。 初始化之前必须设置正确的<code>ConfigConnector</code>, 否则初始化失败。初始化后会有独立新线程定时发送请求去取最新的配置。 * * @throws ConfigException 初始化之前必须设置正确的 <code>ConfigConnector</code>, 否则初始化抛异常而失败。 */ public void initConfig() throws ConfigException { if (initialized) { logger.warn("ConfigManagerImpl is already initialized!"); return; } Properties defaultProperties = loadDefaultConfig(); // properties为空,则通过location加载配置,否则将properties中所有的值转换为String if (properties == null) { this.properties = PropertiesLoadUtil.loadProperties(location); } else { Properties tempProperties = new Properties(); Enumeration<Object> keysEnum = properties.keys(); while (keysEnum.hasMoreElements()) { Object key = keysEnum.nextElement(); if (key instanceof String) { tempProperties.setProperty((String)key, properties.get(key).toString()); } else { throw new ConfigException(String.format( "The key[%s]'type of client properties file must be 'String',not support the type:%s", key, key.getClass())); } } this.properties = tempProperties; } if( defaultProperties != null) { defaultProperties.putAll( properties ); properties = defaultProperties; } loadClientConfiguration(properties); // initialize the connector AdminConnector adminConnector = AdminConnector.getInstance(); adminConnector.init(properties);// this init must be execute when client initialized if (!adminConnector.isConnected()) { String mainAdminUrl = properties.getProperty("doris.config.adminserver.main.url"); String backupAdminUrl = properties.getProperty("doris.config.adminserver.backup.url"); throw new ConfigException("admin server is not available,main admin:" + mainAdminUrl + ",backup admin:" + backupAdminUrl); } String autoFetchProp = properties.getProperty("doris.admin.config.autofetch.enable", "true"); boolean autoFetchEnable = Boolean.parseBoolean(autoFetchProp); if (autoFetchEnable) { // the interval to dectect configuration changes. String intervalConfig = properties.getProperty("doris.config.fetch.interval"); if (StringUtils.isEmpty(intervalConfig)) { throw new IllegalArgumentException("confg 'doris.config.fetch.interval' is not valid."); } this.interval = Long.parseLong(intervalConfig.trim()); // start fetch thread. fetchThread = new Thread(new ConfigFetchTask(), "doris-config-fetcher"); fetchThread.setDaemon(true); fetchThread.start(); } // initialize completed. initialized = true; } public Properties loadDefaultConfig() { return null; } private void loadClientConfiguration(Properties property) { clientConfiguration = new ClientConfiguration(); // To get the time out value; String timeout = property.getProperty("doris.config.client.operation.timeout"); if (StringUtils.isNotBlank(timeout)) { clientConfiguration.setTimeoutOfOperation(NumberUtils.toLong(timeout)); } } public ClientConfiguration getClientConfiguration() { return this.clientConfiguration; } /** * 关闭配置管理客户端。 * * @throws ConfigException */ public void close() throws ConfigException { if (logger.isInfoEnabled()) { logger.info("Closing config fetcher..."); } stopped = true; initialized = false; fetchThread.interrupt(); } private class ConfigFetchTask implements Runnable { public void run() { try { while (!stopped) { try { Thread.sleep(interval); } catch (Exception e) { logger.warn("Exception when the key fetch task sleep: " + e.toString()); } if (stopped) { return; } try { fetch(null, false); } catch (Exception e) { logger.error("Exception when fetch doris config: ", e); } } } finally { logger.warn("the key fetch thread exit!"); } } } /** * 取得所有监听器所关心的配置。 * * @param b */ private void fetch(Map<String, String> map, boolean forceRefresh) { synchronized (lock) { Map<String, String> configurations = null; if (map == null) { if (this.configListeners == null || this.configListeners.isEmpty()) { return; } Map<String, Long> actionVersions = new HashMap<String, Long>(); for (ConfigListener confLstnr : configListeners) { if (forceRefresh) { actionVersions.put(confLstnr.getConfigListenerName(), null); } else { actionVersions.put(confLstnr.getConfigListenerName(), confLstnr.getConfigVersion()); } } if (forceRefresh) { List<String> actions = new ArrayList<String>(actionVersions.keySet()); configurations = commonConfigService.getConfig(actions); } else { configurations = commonConfigService.getConfig(actionVersions); } } else { configurations = map; } for (ConfigListener confLstnr : configListeners) { String lsnerName = confLstnr.getConfigListenerName(); String conf = configurations.get(lsnerName); if (StringUtils.isNotEmpty(conf) && !"null".equalsIgnoreCase(conf)) { configurationCache.put(lsnerName, conf); confLstnr.onConfigChange(conf); } } } } public void refreshConfig() { fetch(null, true); } public void refreshConfig(Map<String, String> configurations) { fetch(configurations, false); } /** * 注册配置管理实例,这些实例在配置变更的时候将会被触发。 * * @param configManager 用来接受配置变更的Doris配置管理实例 * @throws IllegalStateException 没有被初始化会抛异常 */ public void addConfigListener(ConfigListener configListener) { if (this.initialized) { this.configListeners.add(configListener); fetch(null, true); } else { throw new IllegalStateException("the configuration manager is not initialized yet."); } } /** * 解注册配置管理实例,这些实例在配置变更的时候将不会被触发。 * * @param configManager 用来接受配置变更的Doris配置管理实例 * @throws IllegalStateException 没有被初始化会抛异常 */ public void removeConfigListener(ConfigListener configManager) { if (this.initialized) { this.configListeners.remove(configManager); } else { throw new IllegalStateException("the configuration manager is not initialized yet."); } } /** * set config properties. * @since 0.1.4 */ public void setConfigProperties(Properties properties){ this.properties = properties; } /** * set config location, local or remote. */ public void setConfigLocation(String location) { this.location = location; } /** * get config properties. */ public Properties getProperties() { return properties; } public String getConfig(String actionName) { return getConfig(actionName, null); } public String getConfig(String actionName, Long version) { Map<String, Long> paras = new HashMap<String, Long>(); paras.put(actionName, version); Map<String, String> rsltMap = commonConfigService.getConfig(paras); return rsltMap.get(actionName); } public String getCachedConfig(String actionName) { return configurationCache.get(actionName); } }