package com.dianping.pigeon.remoting.invoker.route.region; import com.dianping.pigeon.config.ConfigChangeListener; import com.dianping.pigeon.config.ConfigManager; import com.dianping.pigeon.config.ConfigManagerLoader; import com.dianping.pigeon.log.Logger; import com.dianping.pigeon.log.LoggerLoader; import com.dianping.pigeon.monitor.Monitor; import com.dianping.pigeon.monitor.MonitorLoader; import com.dianping.pigeon.remoting.common.domain.InvocationRequest; import com.dianping.pigeon.remoting.common.util.Constants; import com.dianping.pigeon.remoting.invoker.Client; import com.dianping.pigeon.remoting.invoker.ClientManager; import com.dianping.pigeon.remoting.invoker.config.InvokerConfig; import com.dianping.pigeon.remoting.invoker.exception.RouteException; import com.dianping.pigeon.util.ClassUtils; import com.dianping.pigeon.util.ServiceUtils; import com.google.common.collect.ImmutableMap; import org.apache.commons.lang.StringUtils; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Created by chenchongze on 16/4/14. */ public enum RegionPolicyManager { INSTANCE; RegionPolicyManager() {} private static volatile boolean isInitialized = false; public void init() { if (!isInitialized) { synchronized (RegionPolicyManager.class) { if (!isInitialized) { register(AutoSwitchRegionPolicy.NAME, null, AutoSwitchRegionPolicy.INSTANCE); register(WeightBasedRegionPolicy.NAME, null, WeightBasedRegionPolicy.INSTANCE); register(ForceRegionPolicy.NAME, null, ForceRegionPolicy.INSTANCE); if(configManager.getBooleanValue(KEY_ENABLEREGIONPOLICY, DEFAULT_ENABLEREGIONPOLICY)) { initRegionsConfig(); } else { logger.info("Region policy is disabled!"); } configManager.registerConfigChangeListener(new InnerConfigChangeListener()); isInitialized = true; } } } } private final Monitor monitor = MonitorLoader.getMonitor(); private final Logger logger = LoggerLoader.getLogger(this.getClass()); private final ConfigManager configManager = ConfigManagerLoader.getConfigManager(); // 自动切换region的开关 public final String KEY_ENABLEREGIONPOLICY = "pigeon.regions.route.enable"; public final boolean DEFAULT_ENABLEREGIONPOLICY = false; public final String KEY_REGIONINFO = "pigeon.regions"; public final String KEY_REGION_PREFER_BASE = "pigeon.regions.prefer."; private volatile boolean isEnabled = false; // example: 10.66 --> region1 private final Map<String, Region> patternRegionMappings = new HashMap<String, Region>(); private volatile List<Region> regionArray = new ArrayList<>(); private volatile Region localRegion; // regionName --> region : shanghai --> regionObj private volatile Map<String, Region> regionMap = new HashMap<>(); public final String DEFAULT_REGIONPOLICY = configManager.getStringValue( Constants.KEY_REGIONPOLICY, AutoSwitchRegionPolicy.NAME); private Map<String, RegionPolicy> regionPolicyMap = new ConcurrentHashMap<String, RegionPolicy>(); private void checkClientsNotNull(List<Client> clientList, InvokerConfig<?> invokerConfig) { if(clientList == null) { throw new RouteException("no available clientList in region policy for service[" + invokerConfig + "], env:" + ConfigManagerLoader.getConfigManager().getEnv()); } } public List<Client> getPreferRegionClients(List<Client> clientList, InvokerConfig<?> invokerConfig, InvocationRequest request) { RegionPolicy regionPolicy = getRegionPolicy(invokerConfig); if(regionPolicy == null) { regionPolicy = AutoSwitchRegionPolicy.INSTANCE; } clientList = regionPolicy.getPreferRegionClients(clientList, request); checkClientsNotNull(clientList, invokerConfig); Region regionSample = clientList.get(0).getRegion(); if (regionSample != null) { monitor.logEvent("PigeonCall.region", request.getServiceName() + "#" + regionSample.getName(), ""); } return clientList; } public RegionPolicy getRegionPolicy(InvokerConfig<?> invokerConfig) { String serviceId = ServiceUtils.getServiceId(invokerConfig.getUrl(), invokerConfig.getSuffix()); RegionPolicy regionPolicy = regionPolicyMap.get(serviceId); if (regionPolicy != null) { return regionPolicy; } regionPolicy = regionPolicyMap.get(invokerConfig.getRegionPolicy()); if (regionPolicy != null) { return regionPolicy; } if (DEFAULT_REGIONPOLICY != null) { regionPolicy = regionPolicyMap.get(DEFAULT_REGIONPOLICY); if (regionPolicy != null) { regionPolicyMap.put(invokerConfig.getRegionPolicy(), regionPolicy); return regionPolicy; } else { logger.warn("the regionPolicy[" + DEFAULT_REGIONPOLICY + "] is invalid, only support " + regionPolicyMap.keySet() + "."); } } return null; } /** * 注册RegionPolicy * @param serviceName * @param suffix * @param regionPolicy */ @SuppressWarnings("unchecked") public void register(String serviceName, String suffix, Object regionPolicy) { String serviceId = ServiceUtils.getServiceId(serviceName, suffix); RegionPolicy regionPolicyObj = null; if(regionPolicy instanceof RegionPolicy) { regionPolicyObj = (RegionPolicy) regionPolicy; } else if (regionPolicy instanceof String && StringUtils.isNotBlank((String) regionPolicy)) { if (!regionPolicyMap.containsKey(regionPolicy)) { try { Class<? extends RegionPolicy> regionPolicyClass = (Class<? extends RegionPolicy>) ClassUtils .loadClass((String) regionPolicy); regionPolicyObj = regionPolicyClass.newInstance(); } catch (Throwable e) { throw new IllegalArgumentException("failed to register regionPolicy[service=" + serviceId + ",class=" + regionPolicy + "]", e); } } else { regionPolicyObj = regionPolicyMap.get(regionPolicy); } } else if (regionPolicy instanceof Class) { try { Class<? extends RegionPolicy> regionPolicyClass = (Class<? extends RegionPolicy>) regionPolicy; regionPolicyObj = regionPolicyClass.newInstance(); } catch (Throwable e) { throw new IllegalArgumentException("failed to register regionPolicy[service=" + serviceId + ",class=" + regionPolicy + "]", e); } } if (regionPolicyObj != null) { regionPolicyMap.put(serviceId, regionPolicyObj); } } private class InnerConfigChangeListener implements ConfigChangeListener { @Override public void onKeyUpdated(String key, String value) { if (key.endsWith(KEY_ENABLEREGIONPOLICY)) { if(Boolean.valueOf(value)) { // region路由开,重新读取配置 // 清空allClient region信息 initRegionsConfig(); } else { // region路由关 isEnabled = false; logger.info("Region policy is disabled!"); } } else if(isEnabled && key.endsWith(KEY_REGIONINFO)) { initRegionsConfig(value); } else if(isEnabled && localRegion != null && key.endsWith(KEY_REGION_PREFER_BASE + localRegion.getName())) { initRegionsConfig(configManager.getStringValue(KEY_REGIONINFO), value); } } @Override public void onKeyAdded(String key, String value) { } @Override public void onKeyRemoved(String key) { } } private void clearRegion() { ConcurrentHashMap<String, Client> allClients = ClientManager.getInstance().getClusterListener().getAllClients(); if(!allClients.isEmpty()) { for(String address : allClients.keySet()) { Client client = allClients.get(address); client.clearRegion(); } } } public boolean isEnableRegionPolicy() { return configManager.getBooleanValue(KEY_ENABLEREGIONPOLICY, DEFAULT_ENABLEREGIONPOLICY) && isEnabled; } private void initRegionsConfig() { initRegionsConfig(configManager.getStringValue(KEY_REGIONINFO)); } private void initRegionsConfig(String pigeonRegionsConfig) { initRegionsConfig(pigeonRegionsConfig, null); } private synchronized void initRegionsConfig(String pigeonRegionsConfig, String regionsPreferConfig) { try { String[] regionConfigs = pigeonRegionsConfig.split(";"); int regionCount = regionConfigs.length; if(regionCount <= 0) { logger.error("Error! Please check regions config!"); return ; } Set<String> regionSet = new HashSet<String>(); Map<String, String> patternRegionNameMappings = new HashMap<String, String>(); for (String regionConfig : regionConfigs) { String[] regionPatternMapping = regionConfig.split(":"); String regionName = regionPatternMapping[0]; String[] patterns = regionPatternMapping[1].split(","); regionSet.add(regionName); for (String pattern : patterns) { patternRegionNameMappings.put(pattern, regionName); } } //初始化local region String localRegionPattern = getPattern(configManager.getLocalIp()); if(patternRegionNameMappings.containsKey(localRegionPattern)) { String localRegionName = patternRegionNameMappings.get(localRegionPattern); // 权重处理 if (StringUtils.isBlank(regionsPreferConfig)) { regionsPreferConfig = configManager.getStringValue(KEY_REGION_PREFER_BASE + localRegionName); } List<Region> regions = initRegionsWithPriority(regionsPreferConfig); Map<String, Region> _regionMap = new HashMap<>(); if(regionSet.size() == regions.size()) { for(Region region : regions) { if(!regionSet.contains(region.getName())) { logger.error("Error! Regions prefer not match regions config: " + region.getName()); return; } _regionMap.put(region.getName(), region); } //(re)init regionArray = Collections.unmodifiableList(regions);// 下面的步骤都基于regionArray initPatterRegionMappings(patternRegionNameMappings);// 初始化pattern region映射 localRegion = getRegionByName(localRegionName); regionMap = ImmutableMap.copyOf(_regionMap); clearRegion(); isEnabled = true; logger.info("Region route policy switch on! Local region is: " + regionArray.get(0)); } else { logger.error("Error! Regions prefer counts not match regions config!"); } } else { logger.error("Error! Can't init local region: " + configManager.getLocalIp()); } } catch (Throwable t) { logger.error("Error! Init region policy failed!", t); } } private String getPattern(String host) { int firstDotIndex = host.indexOf("."); if (firstDotIndex != -1) { int secondDotIndex = host.indexOf(".", firstDotIndex + 1); if (secondDotIndex != -1) { return host.substring(0, secondDotIndex); } } return ""; } private void initPatterRegionMappings(Map<String, String> patternRegionNameMappings) { patternRegionMappings.clear(); for(Map.Entry<String, String> entry : patternRegionNameMappings.entrySet()) { patternRegionMappings.put(entry.getKey(), getRegionByName(entry.getValue())); } } private List<Region> initRegionsWithPriority(String regionsPreferConfig) { if(StringUtils.isNotBlank(regionsPreferConfig)) { String[] regionNameAndWeights = regionsPreferConfig.split(","); List<Region> regions = new ArrayList<Region>(regionNameAndWeights.length); for(int i = 0; i < regionNameAndWeights.length; ++i) { String[] _regionNameAndWeight = regionNameAndWeights[i].split(":"); String regionName = _regionNameAndWeight[0]; int regionWeight = Integer.parseInt(_regionNameAndWeight[1]); regions.add(new Region(regionName, i, regionWeight)); } return regions; } return new ArrayList<Region>(); } private Region getRegionByName(String regionName) { for (Region region : regionArray) { if (region.getName().equalsIgnoreCase(regionName)) { return region; } } return null; } public Region getRegion(String host) { String pattern = getPattern(host); if(patternRegionMappings.containsKey(pattern)) { return patternRegionMappings.get(pattern); } else { return null; } } public List<Region> getRegionArray() { return regionArray; } public Region getLocalRegion() { return localRegion; } public Map<String, Region> getRegionMap() { return regionMap; } }