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.registry.RegistryManager; import com.dianping.pigeon.remoting.common.domain.InvocationRequest; import com.dianping.pigeon.remoting.invoker.Client; import org.apache.commons.lang.StringUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by chenchongze on 17/5/15. */ public class ForceRegionPolicy implements RegionPolicy { public static final ForceRegionPolicy INSTANCE = new ForceRegionPolicy(); public static final String NAME = "force"; private final Logger logger = LoggerLoader.getLogger(this.getClass()); private final RegionPolicyManager regionPolicyManager = RegionPolicyManager.INSTANCE; private final RegistryManager registryManager = RegistryManager.getInstance(); private final ConfigManager configManager = ConfigManagerLoader.getConfigManager(); private final Monitor monitor = MonitorLoader.getMonitor(); private static final String KEY_REGION_THRESHOLD_RATIO = "pigeon.regions.switchratio"; private volatile float regionSwitchRatio = configManager.getFloatValue(KEY_REGION_THRESHOLD_RATIO, 0.5f); private static final String KEY_REGION_FORCE_CONFIG = "pigeon.regions.force.config"; private volatile String[] forceRegPrefer = new String[0]; private ForceRegionPolicy() { String forceRegionConf = configManager.getStringValue(KEY_REGION_FORCE_CONFIG, "shanghai,beijing"); initForceRegionConfig(forceRegionConf); configManager.registerConfigChangeListener(new InnerConfigChangeListener()); } private void initForceRegionConfig(String forceRegionConfig) { if (StringUtils.isNotBlank(forceRegionConfig)) { forceRegPrefer = forceRegionConfig.split(","); } } @Override public List<Client> getPreferRegionClients(List<Client> clientList, InvocationRequest request) { List<Region> forceRegions = getForceRegionList(); if (forceRegions != null) { return getRegionActiveClients(clientList, request, forceRegions); } return clientList; } private List<Region> getForceRegionList() { Map<String, Region> regionMap = regionPolicyManager.getRegionMap(); if (forceRegPrefer.length != regionMap.size()) { logger.debug("Force region config size not match regions config, please check!"); return null; } List<Region> _regionArr = new ArrayList<>(); for (String reg : forceRegPrefer) { Region _region = regionMap.get(reg); if (_region == null) { logger.debug("Force region config not match regions config: " + reg); return null; } _regionArr.add(_region); } return _regionArr; } private List<Client> getRegionActiveClients(List<Client> clientList, InvocationRequest request, List<Region> regionArrays) { int sizeBefore = clientList.size(); Map<Region, InnerRegionStat> regionStats = new HashMap<Region, InnerRegionStat>(); for (Region region : regionArrays) { // 缓存每个region的统计信息 regionStats.put(region, new InnerRegionStat()); } for (Client client : clientList) { // 分发client的region统计信息 try { InnerRegionStat regionStat = regionStats.get(client.getRegion()); if (regionStat != null) { regionStat.addTotal(); if (client.isActive() && registryManager.getServiceWeightFromCache(client.getAddress()) > 0) { regionStat.addActive(); regionStat.addClient(client); } } } catch (Throwable t) { logger.error(t); } } for (int i = 0; i < regionArrays.size(); ++i) {// 优先级大小按数组大小排列 Region region = regionArrays.get(i); try { InnerRegionStat regionStat = regionStats.get(region); int total = regionStat.getTotal(); int active = regionStat.getActive(); List<Client> regionClientList = regionStat.getClientList(); float least = regionSwitchRatio * total; if (total > 0 && active > 0 && active >= least) { if (logger.isDebugEnabled()) { logger.debug("b: " + sizeBefore + ", a:" + regionClientList.size()); } return regionClientList; } else { if (logger.isDebugEnabled()) { logger.debug(request.getServiceName() + " skipped region " + region.getName() + ", available clients less than " + least); } monitor.logEvent("PigeonCall.forceRegionUnavailable", request.getServiceName() + "#" + region.getName(), ""); } } catch (Throwable t) { logger.error(t); } finally { // maybe release work } } return clientList; } private class InnerRegionStat { private int active = 0; private int total = 0; private List<Client> clientList = new ArrayList<Client>(); public List<Client> getClientList() { return clientList; } public void addClient(Client client) { clientList.add(client); } public int getActive() { return active; } public void addActive() { ++active; } public int getTotal() { return total; } public void addTotal() { ++total; } } private class InnerConfigChangeListener implements ConfigChangeListener { @Override public void onKeyUpdated(String key, String value) { try { if (key.endsWith(KEY_REGION_THRESHOLD_RATIO)) { regionSwitchRatio = Float.valueOf(value); logger.info("set [" + key + "] value: " + value); } else if (key.endsWith(KEY_REGION_FORCE_CONFIG)) { initForceRegionConfig(value); } } catch (Throwable t) { logger.warn("set " + key + " failed!", t); } } @Override public void onKeyAdded(String key, String value) { } @Override public void onKeyRemoved(String key) { } } }