Java tutorial
/* * (C) 2007-2012 Alibaba Group Holding Limited. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * Authors: * leiwen <chrisredfield1985@126.com> , boyan <killme2008@gmail.com> */ package com.starit.diamond.client.impl; import static com.starit.diamond.common.Constants.LINE_SEPARATOR; import static com.starit.diamond.common.Constants.WORD_SEPARATOR; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URLDecoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.GZIPInputStream; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HostConfiguration; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpMethodBase; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.params.HttpConnectionManagerParams; import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.starit.diamond.client.DiamondConfigure; import com.starit.diamond.client.DiamondSubscriber; import com.starit.diamond.client.SubscriberListener; import com.starit.diamond.client.jmx.DiamondClientUtil; import com.starit.diamond.client.processor.LocalConfigInfoProcessor; import com.starit.diamond.client.processor.ServerAddressProcessor; import com.starit.diamond.client.processor.SnapshotConfigInfoProcessor; import com.starit.diamond.common.Constants; import com.starit.diamond.configinfo.CacheData; import com.starit.diamond.configinfo.ConfigureInfomation; import com.starit.diamond.md5.MD5; import com.starit.diamond.mockserver.MockServer; import com.starit.diamond.utils.AppNameUtils; import com.starit.diamond.utils.SimpleCache; /** * ?DiamondSubscriber * * @author aoqiong * */ class DefaultDiamondSubscriber implements DiamondSubscriber { // private static final String DATA_DIR = "data"; // ?? private static final String SNAPSHOT_DIR = "snapshot"; private static final Log log = LogFactory.getLog(DefaultDiamondSubscriber.class); private static final int SC_OK = 200; private static final int SC_NOT_MODIFIED = 304; private static final int SC_NOT_FOUND = 404; private static final int SC_SERVICE_UNAVAILABLE = 503; private final Logger dataLog = LoggerFactory.getLogger(DefaultDiamondSubscriber.class); private final ConcurrentHashMap<String/* DataID */, ConcurrentHashMap<String/* Group */, CacheData>> cache = new ConcurrentHashMap<String, ConcurrentHashMap<String, CacheData>>(); private volatile SubscriberListener subscriberListener = null; private volatile DiamondConfigure diamondConfigure; private ScheduledExecutorService scheduledExecutor = null; private final LocalConfigInfoProcessor localConfigInfoProcessor = new LocalConfigInfoProcessor(); private SnapshotConfigInfoProcessor snapshotConfigInfoProcessor; private final SimpleCache<String> contentCache = new SimpleCache<String>(); private ServerAddressProcessor serverAddressProcessor = null; private final AtomicInteger domainNamePos = new AtomicInteger(0); private volatile boolean isRun = false; private HttpClient httpClient = null; private String appName = null; private volatile boolean bFirstCheck = true; // private String clusterType; public static class Builder { private final Map<String/* dataId */, Set<String>/* group */> dataIdGroupPairs = new HashMap<String, Set<String>>(); private final SubscriberListener subscriberListener; private DiamondConfigure diamondConfigure; private String appName; private String type; public Builder(SubscriberListener subscriberListener) { this(subscriberListener, Constants.DEFAULT_DIAMOND_CLUSTER); } public Builder(SubscriberListener subscriberListener, String type) { this.subscriberListener = subscriberListener; this.type = type; this.diamondConfigure = new DiamondConfigure(type); } public Builder addDataId(String dataId, String group) { if (null == group) { group = Constants.DEFAULT_GROUP; } synchronized (this.dataIdGroupPairs) { Set<String> groups = this.dataIdGroupPairs.get(dataId); if (null == groups) { Set<String> groupSet = new HashSet<String>(); groupSet.add(group); this.dataIdGroupPairs.put(dataId, groupSet); } } return this; } public Builder addDataId(String dataId) { return addDataId(dataId, null); } public Builder setAppName(String appName) { this.appName = appName; return this; } public Builder setDiamondConfigure(DiamondConfigure diamondConfigure) { this.diamondConfigure = diamondConfigure; return this; } public DiamondSubscriber build() { return new DefaultDiamondSubscriber(this); } } private DefaultDiamondSubscriber(Builder builder) { this.subscriberListener = builder.subscriberListener; this.diamondConfigure = builder.diamondConfigure; this.appName = builder.appName; if (this.appName == null) { this.appName = getAppName(); } this.clusterType = builder.type; if (null != builder.dataIdGroupPairs) { for (Map.Entry<String, Set<String>> dataIdGroupsPair : builder.dataIdGroupPairs.entrySet()) { for (String group : dataIdGroupsPair.getValue()) { this.addDataId(dataIdGroupsPair.getKey(), group); } } } } public DefaultDiamondSubscriber(SubscriberListener subscriberListener) { this(subscriberListener, Constants.DEFAULT_DIAMOND_CLUSTER); } public DefaultDiamondSubscriber(SubscriberListener subscriberListener, String clusterType) { this.subscriberListener = subscriberListener; this.appName = getAppName(); this.clusterType = clusterType; this.diamondConfigure = new DiamondConfigure(clusterType); } private void recordDataIdChange(String dataId, String group) { DiamondClientUtil.addPopCount(clusterType, dataId, group); } /** * ?DiamondSubscriber<br> * 1.?DataId??<br> * 2.??DataId??<br> */ public synchronized void start() { if (isRun) { return; } if (null == scheduledExecutor || scheduledExecutor.isTerminated()) { scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); } localConfigInfoProcessor.start(this.diamondConfigure.getFilePath() + "/" + DATA_DIR); serverAddressProcessor = new ServerAddressProcessor(this.diamondConfigure, this.scheduledExecutor); serverAddressProcessor.setClusterType(clusterType); serverAddressProcessor.start(); this.snapshotConfigInfoProcessor = new SnapshotConfigInfoProcessor( this.diamondConfigure.getFilePath() + "/" + SNAPSHOT_DIR); // domainNamePos randomDomainNamePos(); initHttpClient(); // ? isRun = true; if (log.isInfoEnabled()) { log.info("???" + this.diamondConfigure.getDomainNameList()); } if (MockServer.isTestMode()) { bFirstCheck = false; } else { // this.diamondConfigure.setPollingIntervalTime(Constants.POLLING_INTERVAL_TIME); } // rotateCheckConfigInfo(); addShutdownHook(); } private void randomDomainNamePos() { // ??? Random rand = new Random(); List<String> domainList = this.diamondConfigure.getDomainNameList(); if (!domainList.isEmpty()) { this.domainNamePos.set(rand.nextInt(domainList.size())); } } private void addShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { // ? close(); } }); } protected void initHttpClient() { if (MockServer.isTestMode()) { return; } HostConfiguration hostConfiguration = new HostConfiguration(); hostConfiguration.setHost(diamondConfigure.getDomainNameList().get(this.domainNamePos.get()), diamondConfigure.getPort()); MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); connectionManager.closeIdleConnections(diamondConfigure.getPollingIntervalTime() * 4000); HttpConnectionManagerParams params = new HttpConnectionManagerParams(); params.setStaleCheckingEnabled(diamondConfigure.isConnectionStaleCheckingEnabled()); params.setMaxConnectionsPerHost(hostConfiguration, diamondConfigure.getMaxHostConnections()); params.setMaxTotalConnections(diamondConfigure.getMaxTotalConnections()); params.setConnectionTimeout(diamondConfigure.getConnectionTimeout()); // 1, // boyan@taobao.com params.setSoTimeout(60 * 1000); connectionManager.setParams(params); httpClient = new HttpClient(connectionManager); httpClient.setHostConfiguration(hostConfiguration); } /** * * * @param pos */ void setDomainNamesPos(int pos) { this.domainNamePos.set(pos); } /** * ???????DiamondServer??? */ private void rotateCheckConfigInfo() { scheduledExecutor.schedule(new Runnable() { public void run() { if (!isRun) { log.warn("DiamondSubscriber???"); return; } try { checkLocalConfigInfo(); checkDiamondServerConfigInfo(); checkSnapshot(); } catch (Exception e) { e.printStackTrace(); log.error("?", e); } finally { rotateCheckConfigInfo(); } } }, bFirstCheck ? 60 : diamondConfigure.getPollingIntervalTime(), TimeUnit.SECONDS); bFirstCheck = false; } void getConfigInfoAndPopToClient(String dataId, String group) { ConcurrentHashMap<String, CacheData> cacheDatas = cache.get(dataId); if (null == cacheDatas) { log.info("[pushit client] cacheDatas == null"); return; } CacheData cacheData = cacheDatas.get(group); if (null == cacheData) { log.info("[pushit client] cacheData == null"); return; } log.info("[pushit client] diamond-client?diamond-server?"); receiveConfigInfo(cacheData); } /** * ?DiamondServerdataId??? * * @param dataId */ private void receiveConfigInfo(final CacheData cacheData) { scheduledExecutor.execute(new Runnable() { public void run() { if (!isRun) { log.warn("DiamondSubscriber???"); return; } try { String configInfo = getConfigureInfomation(cacheData.getDataId(), cacheData.getGroup(), diamondConfigure.getReceiveWaitTime(), true); if (null == configInfo) { return; } if (null == subscriberListener) { log.warn("null == subscriberListener"); return; } popConfigInfo(cacheData, configInfo, RotateType.SERVER); } catch (Exception e) { log.error("?Diamond????", e); } } }); } private void checkSnapshot() { for (Entry<String, ConcurrentHashMap<String, CacheData>> cacheDatasEntry : cache.entrySet()) { ConcurrentHashMap<String, CacheData> cacheDatas = cacheDatasEntry.getValue(); if (null == cacheDatas) { continue; } for (Entry<String, CacheData> cacheDataEntry : cacheDatas.entrySet()) { final CacheData cacheData = cacheDataEntry.getValue(); // ??diamond server???,snapshot if (!cacheData.isUseLocalConfigInfo() && cacheData.getFetchCount() == 0) { String configInfo = getSnapshotConfiginfomation(cacheData.getDataId(), cacheData.getGroup()); if (configInfo != null) { popConfigInfo(cacheData, configInfo, RotateType.SNAPSHOT); } } } } } private void checkDiamondServerConfigInfo() { Set<String> updateDataIdGroupPairs = checkUpdateDataIds(diamondConfigure.getReceiveWaitTime()); if (null == updateDataIdGroupPairs || updateDataIdGroupPairs.size() == 0) { log.debug("DataID"); return; } // ???DataID?? for (String freshDataIdGroupPair : updateDataIdGroupPairs) { int middleIndex = freshDataIdGroupPair.indexOf(WORD_SEPARATOR); if (middleIndex == -1) continue; String freshDataId = freshDataIdGroupPair.substring(0, middleIndex); String freshGroup = freshDataIdGroupPair.substring(middleIndex + 1); ConcurrentHashMap<String, CacheData> cacheDatas = cache.get(freshDataId); if (null == cacheDatas) { continue; } CacheData cacheData = cacheDatas.get(freshGroup); if (null == cacheData) { continue; } receiveConfigInfo(cacheData); } } private void checkLocalConfigInfo() { for (Entry<String/* dataId */, ConcurrentHashMap<String/* group */, CacheData>> cacheDatasEntry : cache .entrySet()) { ConcurrentHashMap<String, CacheData> cacheDatas = cacheDatasEntry.getValue(); if (null == cacheDatas) { continue; } for (Entry<String, CacheData> cacheDataEntry : cacheDatas.entrySet()) { final CacheData cacheData = cacheDataEntry.getValue(); try { String configInfo = getLocalConfigureInfomation(cacheData); if (null != configInfo) { if (log.isInfoEnabled()) { log.info("???, dataId:" + cacheData.getDataId() + ", group:" + cacheData.getGroup()); } popConfigInfo(cacheData, configInfo, RotateType.LOCAL); continue; } if (cacheData.isUseLocalConfigInfo()) { continue; } } catch (Exception e) { log.error("????", e); } } } } /** * ?? * */ void popConfigInfo(final CacheData cacheData, final String configInfo, final RotateType rotateType) { final ConfigureInfomation configureInfomation = new ConfigureInfomation(); configureInfomation.setConfigureInfomation(configInfo); final String dataId = cacheData.getDataId(); final String group = cacheData.getGroup(); configureInfomation.setDataId(dataId); configureInfomation.setGroup(group); cacheData.incrementFetchCountAndGet(); if (null != this.subscriberListener.getExecutor()) { this.subscriberListener.getExecutor().execute(new Runnable() { public void run() { try { if (subscriberListener instanceof DefaultSubscriberListener) { ((DefaultSubscriberListener) subscriberListener).setRotateType(rotateType); } subscriberListener.receiveConfigInfo(configureInfomation); saveSnapshot(dataId, group, configInfo); } catch (Throwable t) { log.error("???group" + group + ", dataId" + dataId, t); } } }); } else { try { if (subscriberListener instanceof DefaultSubscriberListener) { ((DefaultSubscriberListener) subscriberListener).setRotateType(rotateType); } subscriberListener.receiveConfigInfo(configureInfomation); saveSnapshot(dataId, group, configInfo); } catch (Throwable t) { log.error("???group" + group + ", dataId" + dataId, t); } } } public synchronized void close() { if (!isRun) { return; } log.warn("DiamondSubscriber"); localConfigInfoProcessor.stop(); serverAddressProcessor.stop(); serverAddressProcessor = null; isRun = false; scheduledExecutor.shutdownNow(); scheduledExecutor = null; cache.clear(); DiamondClientUtil.close(); log.warn("DiamondSubscriber?"); } /** * * @param waitTime * ?(?HTTP) * @param timeout * ?(?HTTP) * @return HTTP */ long getOnceTimeOut(long waitTime, long timeout) { long onceTimeOut = this.diamondConfigure.getOnceTimeout(); long remainTime = timeout - waitTime; if (onceTimeOut > remainTime) { onceTimeOut = remainTime; } return onceTimeOut; } public String getLocalConfigureInfomation(CacheData cacheData) throws IOException { if (!isRun) { throw new RuntimeException( "DiamondSubscriber????ConfigureInfomation"); } return localConfigInfoProcessor.getLocalConfigureInfomation(cacheData, false); } public String getConfigureInfomation(String dataId, long timeout) { return getConfigureInfomation(dataId, null, timeout); } public String getConfigureInfomation(String dataId, String group, long timeout) { // ??? // flowControl(); if (null == group) { group = Constants.DEFAULT_GROUP; } CacheData cacheData = getCacheData(dataId, group); // ? try { String localConfig = localConfigInfoProcessor.getLocalConfigureInfomation(cacheData, true); if (localConfig != null) { cacheData.incrementFetchCountAndGet(); saveSnapshot(dataId, group, localConfig); return localConfig; } } catch (IOException e) { log.error("??", e); } // ??? String result = getConfigureInfomation(dataId, group, timeout, false); if (result != null) { saveSnapshot(dataId, group, result); cacheData.incrementFetchCountAndGet(); } return result; } public boolean exists(String dataId, String group) { try { String result = this.getConfigureInfomation(dataId, group, 10000, false); if (result == null) { return false; } return true; } catch (Exception e) { log.error("??, dataId=" + dataId + ", group=" + group, e); return false; } } private void saveSnapshot(String dataId, String group, String config) { if (config != null) { try { this.snapshotConfigInfoProcessor.saveSnaptshot(dataId, group, config); } catch (IOException e) { log.error("?snapshot,dataId=" + dataId + ",group=" + group, e); } } } public String getAvailableConfigureInfomation(String dataId, String group, long timeout) { // ???? try { String result = getConfigureInfomation(dataId, group, timeout); if (result != null && result.length() > 0) { return result; } } catch (Throwable t) { // ignore } // ??dump if (MockServer.isTestMode()) { return null; } return getSnapshotConfiginfomation(dataId, group); } public String getAvailableConfigureInfomationFromSnapshot(String dataId, String group, long timeout) { String result = getSnapshotConfiginfomation(dataId, group); if (!StringUtils.isBlank(result)) { return result; } return getConfigureInfomation(dataId, group, timeout); } private String getSnapshotConfiginfomation(String dataId, String group) { if (group == null) { group = Constants.DEFAULT_GROUP; } try { CacheData cacheData = getCacheData(dataId, group); String config = this.snapshotConfigInfoProcessor.getConfigInfomation(dataId, group); if (config != null && cacheData != null) { cacheData.incrementFetchCountAndGet(); } return config; } catch (Exception e) { log.error("?snapshot dataId=" + dataId + ",group=" + group, e); return null; } } /** * * @param dataId * @param group * @param timeout * @param skipContentCache * ?cachegetcheck?get?cache * @return */ String getConfigureInfomation(String dataId, String group, long timeout, boolean skipContentCache) { start(); if (!isRun) { throw new RuntimeException("DiamondSubscriber????ConfigureInfomation"); } if (null == group) { group = Constants.DEFAULT_GROUP; } // =======================?======================= if (MockServer.isTestMode()) { return MockServer.getConfigInfo(dataId, group); } // ========================================================== /** * TTLcache */ if (!skipContentCache) { String key = makeCacheKey(dataId, group); String content = contentCache.get(key); if (content != null) { return content; } } long waitTime = 0; String uri = getUriString(dataId, group); if (log.isInfoEnabled()) { log.info(uri); } CacheData cacheData = getCacheData(dataId, group); // ? int retryTimes = this.getDiamondConfigure().getRetrieveDataRetryTimes(); log.info("????" + retryTimes); // ?? int tryCount = 0; while (0 == timeout || timeout > waitTime) { // ?1 tryCount++; if (tryCount > retryTimes + 1) { log.warn("??"); break; } log.info("???" + tryCount + "?, waitTime:" + waitTime); // long onceTimeOut = getOnceTimeOut(waitTime, timeout); waitTime += onceTimeOut; HttpMethod httpMethod = new GetMethod(uri); configureHttpMethod(skipContentCache, cacheData, onceTimeOut, httpMethod); try { int httpStatus = httpClient.executeMethod(httpMethod); switch (httpStatus) { case SC_OK: { String result = getSuccess(dataId, group, cacheData, httpMethod); // dataid? recordDataIdChange(dataId, group); return result; } case SC_NOT_MODIFIED: { String result = getNotModified(dataId, cacheData, httpMethod); return result; } case SC_NOT_FOUND: { log.warn("DataID:" + dataId + "??"); this.snapshotConfigInfoProcessor.removeSnapshot(dataId, group); return null; } case SC_SERVICE_UNAVAILABLE: { // ????? rotateToNextDomain(); } break; default: { log.warn("HTTP State: " + httpStatus + ":" + httpClient.getState()); rotateToNextDomain(); } } } catch (HttpException e) { log.error("???Http" + e); rotateToNextDomain(); } catch (IOException e) { log.error("???IO" + e); rotateToNextDomain(); } catch (Exception e) { log.error("", e); rotateToNextDomain(); } finally { httpMethod.releaseConnection(); } } throw new RuntimeException("?ConfigureInfomation, DataID" + dataId + ", Group" + group + "," + timeout); } private CacheData getCacheData(String dataId, String group) { CacheData cacheData = null; ConcurrentHashMap<String, CacheData> cacheDatas = this.cache.get(dataId); if (null != cacheDatas) { cacheData = cacheDatas.get(group); } if (null == cacheData) { cacheData = new CacheData(dataId, group); ConcurrentHashMap<String, CacheData> newCacheDatas = new ConcurrentHashMap<String, CacheData>(); ConcurrentHashMap<String, CacheData> oldCacheDatas = this.cache.putIfAbsent(dataId, newCacheDatas); if (null == oldCacheDatas) { oldCacheDatas = newCacheDatas; } if (null != oldCacheDatas.putIfAbsent(group, cacheData)) { cacheData = oldCacheDatas.get(group); } } return cacheData; } /** * RP_NO_CHANGE?<br> * 1.MD5?MD5?????<br> * 2.MD5?NULL<br> */ private String getNotModified(String dataId, CacheData cacheData, HttpMethod httpMethod) { Header md5Header = httpMethod.getResponseHeader(Constants.CONTENT_MD5); if (null == md5Header) { throw new RuntimeException("RP_NO_CHANGEMD5?"); } String md5 = md5Header.getValue(); if (!cacheData.getMd5().equals(md5)) { String lastMd5 = cacheData.getMd5(); cacheData.setMd5(Constants.NULL); cacheData.setLastModifiedHeader(Constants.NULL); throw new RuntimeException("MD5?,DataID:[" + dataId + "]MD5:[" + lastMd5 + "]MD5:[" + md5 + "]"); } cacheData.setMd5(md5); changeSpacingInterval(httpMethod); if (log.isInfoEnabled()) { log.info("DataId: " + dataId + ", configInfo?"); } return null; } /** * RP_OK?<br> * 1.??????<br> * 2.????MD5???????<br> * 3.?LastModified?MD5???????<br> */ private String getSuccess(String dataId, String group, CacheData cacheData, HttpMethod httpMethod) { String configInfo = Constants.NULL; configInfo = getContent(httpMethod); if (null == configInfo) { throw new RuntimeException("RP_OK???"); } Header md5Header = httpMethod.getResponseHeader(Constants.CONTENT_MD5); if (null == md5Header) { throw new RuntimeException("RP_OKMD5?, " + configInfo); } String md5 = md5Header.getValue(); if (!checkContent(configInfo, md5)) { throw new RuntimeException("??MD5?,DataID:[" + dataId + "]??:[" + configInfo + "]MD5:[" + md5 + "]"); } Header lastModifiedHeader = httpMethod.getResponseHeader(Constants.LAST_MODIFIED); if (null == lastModifiedHeader) { throw new RuntimeException("RP_OKlastModifiedHeader"); } String lastModified = lastModifiedHeader.getValue(); cacheData.setMd5(md5); cacheData.setLastModifiedHeader(lastModified); changeSpacingInterval(httpMethod); // cache String key = makeCacheKey(dataId, group); contentCache.put(key, configInfo); // ? StringBuilder buf = new StringBuilder(); buf.append("dataId=").append(dataId); buf.append(" ,group=").append(group); buf.append(" ,content=").append(configInfo); dataLog.info(buf.toString()); return configInfo; } private void configureHttpMethod(boolean skipContentCache, CacheData cacheData, long onceTimeOut, HttpMethod httpMethod) { if (skipContentCache && null != cacheData) { if (null != cacheData.getLastModifiedHeader() && Constants.NULL != cacheData.getLastModifiedHeader()) { httpMethod.addRequestHeader(Constants.IF_MODIFIED_SINCE, cacheData.getLastModifiedHeader()); } if (null != cacheData.getMd5() && Constants.NULL != cacheData.getMd5()) { httpMethod.addRequestHeader(Constants.CONTENT_MD5, cacheData.getMd5()); } } // appName if (null != this.appName) { httpMethod.addRequestHeader(Constants.APPNAME, this.appName); } httpMethod.addRequestHeader(Constants.CLIENT_VERSION_HEADER, Constants.CLIENT_VERSION); httpMethod.addRequestHeader(Constants.ACCEPT_ENCODING, "gzip,deflate"); // HttpMethod? HttpMethodParams params = new HttpMethodParams(); params.setSoTimeout((int) onceTimeOut); // /////////////////////// httpMethod.setParams(params); httpClient.getHostConfiguration().setHost( diamondConfigure.getDomainNameList().get(this.domainNamePos.get()), diamondConfigure.getPort()); } private String makeCacheKey(String dataId, String group) { String key = dataId + "-" + group; return key; } /** * DiamondServer??DataID * * @param timeout * @return */ Set<String> checkUpdateDataIds(long timeout) { if (!isRun) { throw new RuntimeException( "DiamondSubscriber????DataID"); } // =======================?======================= if (MockServer.isTestMode()) { return testData(); } // ========================================================== long waitTime = 0; String probeUpdateString = getProbeUpdateString(); if (StringUtils.isBlank(probeUpdateString)) { return null; } while (0 == timeout || timeout > waitTime) { // long onceTimeOut = getOnceTimeOut(waitTime, timeout); waitTime += onceTimeOut; PostMethod postMethod = new PostMethod(Constants.CONFIG_HTTP_URI_FILE); postMethod.addParameter(Constants.PROBE_MODIFY_REQUEST, probeUpdateString); if (null != this.appName) { postMethod.addRequestHeader(Constants.APPNAME, this.appName); } postMethod.addRequestHeader(Constants.CLIENT_VERSION_HEADER, Constants.CLIENT_VERSION); // HttpMethod? HttpMethodParams params = new HttpMethodParams(); params.setSoTimeout((int) onceTimeOut); // /////////////////////// postMethod.setParams(params); try { httpClient.getHostConfiguration().setHost( diamondConfigure.getDomainNameList().get(this.domainNamePos.get()), this.diamondConfigure.getPort()); int httpStatus = httpClient.executeMethod(postMethod); switch (httpStatus) { case SC_OK: { Set<String> result = getUpdateDataIds(postMethod); return result; } case SC_SERVICE_UNAVAILABLE: { rotateToNextDomain(); } break; default: { log.warn("?DataIDHTTP State: " + httpStatus); rotateToNextDomain(); } } } catch (HttpException e) { log.error("???Http" + e); rotateToNextDomain(); } catch (IOException e) { log.error("???IO" + e); rotateToNextDomain(); } catch (Exception e) { log.error("", e); rotateToNextDomain(); } finally { postMethod.releaseConnection(); } } throw new RuntimeException("?DataID " + diamondConfigure.getDomainNameList().get(this.domainNamePos.get()) + ", " + timeout); } void sendAckToServer(String dataId, String group, String message, String listenerName, RotateType rotateType, long timeout) { long waitTime = 0; while (timeout == 0 || timeout > waitTime) { // long onceTimeOut = getOnceTimeOut(waitTime, timeout); waitTime += onceTimeOut; GetMethod ackMethod = new GetMethod(getAckUrl(dataId, group, message, listenerName, rotateType)); configureAckHttpMethod(ackMethod, onceTimeOut); try { int status = httpClient.executeMethod(ackMethod); switch (status) { case SC_OK: { log.info("?????"); return; } case SC_NOT_FOUND: { log.warn("client.ack?"); rotateToNextDomain(); break; } case SC_SERVICE_UNAVAILABLE: { log.warn("?diamond server??"); rotateToNextDomain(); break; } default: { log.warn("??HTTP??: " + httpClient.getState()); rotateToNextDomain(); } } } catch (HttpException e) { log.error("?????HTTP", e); rotateToNextDomain(); } catch (IOException e) { log.error("?????IO", e); rotateToNextDomain(); } catch (Exception e) { log.error("?????", e); rotateToNextDomain(); } finally { ackMethod.releaseConnection(); } } log.error("?????, :" + timeout + "ms"); } private String getAckUrl(String dataId, String group, String ackMessage, String listenerName, RotateType rotateType) { StringBuilder uriBuilder = new StringBuilder(); uriBuilder.append(Constants.HTTP_URI_ACK); uriBuilder.append("?"); uriBuilder.append("dataId=").append(dataId).append("&"); uriBuilder.append("group=").append(group).append("&"); uriBuilder.append("ack=").append(ackMessage).append("&"); uriBuilder.append("listener=").append(listenerName).append("&"); uriBuilder.append("type=").append(rotateType); return uriBuilder.toString(); } private void configureAckHttpMethod(HttpMethod httpMethod, long onceTimeOut) { // appName if (null != this.appName) { httpMethod.addRequestHeader(Constants.APPNAME, this.appName); } httpMethod.addRequestHeader(Constants.CLIENT_VERSION_HEADER, Constants.CLIENT_VERSION); // HttpMethod? HttpMethodParams params = new HttpMethodParams(); params.setSoTimeout((int) onceTimeOut); httpMethod.setParams(params); httpClient.getHostConfiguration().setHost( diamondConfigure.getDomainNameList().get(this.domainNamePos.get()), diamondConfigure.getPort()); } private Set<String> testData() { Set<String> dataIdList = new HashSet<String>(); for (String dataId : this.cache.keySet()) { ConcurrentHashMap<String, CacheData> cacheDatas = this.cache.get(dataId); for (String group : cacheDatas.keySet()) { if (null != MockServer.getUpdateConfigInfo(dataId, group)) { dataIdList.add(dataId + WORD_SEPARATOR + group); } } } return dataIdList; } /** * ?DataID * * @return */ // private Set<String> getLocalUpdateDataIds() { // Set<String> probeModifySet = new HashSet<String>(); // for (Entry<String, ConcurrentHashMap<String, CacheData>> cacheDatasEntry // : this.cache.entrySet()) { // ConcurrentHashMap<String, CacheData> cacheDatas = // cacheDatasEntry.getValue(); // if (null == cacheDatas) { // continue; // } // for (Entry<String, CacheData> cacheDataEntry : cacheDatas.entrySet()) { // if // (this.localConfigInfoProcessor.containsNewLocalConfigureInfomation(cacheDataEntry.getValue())) // { // probeModifySet.add(cacheDataEntry.getKey()); // } // } // } // return probeModifySet; // } /** * ?DataID * * @param localModifySet * @return */ private String getProbeUpdateString() { // ?checkDataID:Group:MD5 StringBuilder probeModifyBuilder = new StringBuilder(); for (Entry<String, ConcurrentHashMap<String, CacheData>> cacheDatasEntry : this.cache.entrySet()) { String dataId = cacheDatasEntry.getKey(); ConcurrentHashMap<String, CacheData> cacheDatas = cacheDatasEntry.getValue(); if (null == cacheDatas) { continue; } for (Entry<String, CacheData> cacheDataEntry : cacheDatas.entrySet()) { final CacheData data = cacheDataEntry.getValue(); // ???diamond server if (!data.isUseLocalConfigInfo()) { probeModifyBuilder.append(dataId).append(WORD_SEPARATOR); if (null != cacheDataEntry.getValue().getGroup() && Constants.NULL != cacheDataEntry.getValue().getGroup()) { probeModifyBuilder.append(cacheDataEntry.getValue().getGroup()).append(WORD_SEPARATOR); } else { probeModifyBuilder.append(WORD_SEPARATOR); } if (null != cacheDataEntry.getValue().getMd5() && Constants.NULL != cacheDataEntry.getValue().getMd5()) { probeModifyBuilder.append(cacheDataEntry.getValue().getMd5()).append(LINE_SEPARATOR); } else { probeModifyBuilder.append(LINE_SEPARATOR); } } } } String probeModifyString = probeModifyBuilder.toString(); return probeModifyString; } /** * ???DataIDConfigInfo??? * ?DataIDDomainPos? * * @param cacheData */ /* * void rotateToNextDomain(CacheData cacheData) { synchronized (cacheData) { * int domainNameCount = diamondConfigure.getDomainNameList().size(); int * index = cacheData.getDomainNamePos().incrementAndGet(); // ? if(index * < 0) { index = -index; } cacheData.getDomainNamePos().set(index % * domainNameCount); if (diamondConfigure.getDomainNameList().size() > 0) * log.warn("DataID: [" + cacheData.getDataId() + "]?DiamondServer??" + * diamondConfigure * .getDomainNameList().get(cacheData.getDomainNamePos().get())); } } */ /** * ?? */ synchronized void rotateToNextDomain() { int domainNameCount = diamondConfigure.getDomainNameList().size(); int index = domainNamePos.incrementAndGet(); if (index < 0) { index = -index; } if (domainNameCount == 0) { log.error("diamond??, ?"); return; } domainNamePos.set(index % domainNameCount); if (diamondConfigure.getDomainNameList().size() > 0) log.warn("?DiamondServer??" + diamondConfigure.getDomainNameList().get(domainNamePos.get())); } /** * ?UriString * * @param dataId * @param group * @return */ String getUriString(String dataId, String group) { StringBuilder uriBuilder = new StringBuilder(); uriBuilder.append(Constants.CONFIG_HTTP_URI_FILE); uriBuilder.append("?"); uriBuilder.append(Constants.DATAID).append("=").append(dataId); if (null != group) { uriBuilder.append("&"); uriBuilder.append(Constants.GROUP).append("=").append(group); } return uriBuilder.toString(); } /** * ? * * @param httpMethod */ void changeSpacingInterval(HttpMethod httpMethod) { Header[] spacingIntervalHeaders = httpMethod.getResponseHeaders(Constants.SPACING_INTERVAL); if (spacingIntervalHeaders.length >= 1) { try { diamondConfigure.setPollingIntervalTime(Integer.parseInt(spacingIntervalHeaders[0].getValue())); } catch (RuntimeException e) { log.error("", e); } } } /** * ?Response?? * * @param httpMethod * @return */ String getContent(HttpMethod httpMethod) { StringBuilder contentBuilder = new StringBuilder(); if (isZipContent(httpMethod)) { // ??? InputStream is = null; GZIPInputStream gzin = null; InputStreamReader isr = null; BufferedReader br = null; try { is = httpMethod.getResponseBodyAsStream(); gzin = new GZIPInputStream(is); isr = new InputStreamReader(gzin, ((HttpMethodBase) httpMethod).getResponseCharSet()); // ????? br = new BufferedReader(isr); char[] buffer = new char[4096]; int readlen = -1; while ((readlen = br.read(buffer, 0, 4096)) != -1) { contentBuilder.append(buffer, 0, readlen); } } catch (Exception e) { log.error("", e); } finally { try { br.close(); } catch (Exception e1) { // ignore } try { isr.close(); } catch (Exception e1) { // ignore } try { gzin.close(); } catch (Exception e1) { // ignore } try { is.close(); } catch (Exception e1) { // ignore } } } else { // ??? String content = null; try { content = httpMethod.getResponseBodyAsString(); } catch (Exception e) { log.error("???", e); } if (null == content) { return null; } contentBuilder.append(content); } return contentBuilder.toString(); } Set<String> getUpdateDataIdsInBody(HttpMethod httpMethod) { Set<String> modifiedDataIdSet = new HashSet<String>(); try { String modifiedDataIdsString = httpMethod.getResponseBodyAsString(); return convertStringToSet(modifiedDataIdsString); } catch (Exception e) { } return modifiedDataIdSet; } Set<String> getUpdateDataIds(HttpMethod httpMethod) { return getUpdateDataIdsInBody(httpMethod); } private Set<String> convertStringToSet(String modifiedDataIdsString) { if (null == modifiedDataIdsString || "".equals(modifiedDataIdsString)) { return null; } Set<String> modifiedDataIdSet = new HashSet<String>(); try { modifiedDataIdsString = URLDecoder.decode(modifiedDataIdsString, "UTF-8"); } catch (Exception e) { log.error("?modifiedDataIdsString", e); } if (log.isInfoEnabled() && modifiedDataIdsString != null) { if (modifiedDataIdsString.startsWith("OK")) { log.debug(":" + modifiedDataIdsString); } else { log.info("??:" + modifiedDataIdsString); } } final String[] modifiedDataIdStrings = modifiedDataIdsString.split(LINE_SEPARATOR); for (String modifiedDataIdString : modifiedDataIdStrings) { if (!"".equals(modifiedDataIdString)) { modifiedDataIdSet.add(modifiedDataIdString); } } return modifiedDataIdSet; } /** * ??MD5?? * * @param configInfo * @param md5 * @return */ boolean checkContent(String configInfo, String md5) { String realMd5 = MD5.getInstance().getMD5String(configInfo); return realMd5 == null ? md5 == null : realMd5.equals(md5); } /** * ? * * @param httpMethod * @return */ boolean isZipContent(HttpMethod httpMethod) { if (null != httpMethod.getResponseHeader(Constants.CONTENT_ENCODING)) { String acceptEncoding = httpMethod.getResponseHeader(Constants.CONTENT_ENCODING).getValue(); if (acceptEncoding.toLowerCase().indexOf("gzip") > -1) { return true; } } return false; } public void setSubscriberListener(SubscriberListener subscriberListener) { this.subscriberListener = subscriberListener; } public SubscriberListener getSubscriberListener() { return this.subscriberListener; } public void addDataId(String dataId, String group) { SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss"); log.info("diamond client start:" + formatter.format(new Date(System.currentTimeMillis()))); if (null == group) { group = Constants.DEFAULT_GROUP; } ConcurrentHashMap<String, CacheData> cacheDatas = this.cache.get(dataId); if (null == cacheDatas) { ConcurrentHashMap<String, CacheData> newCacheDatas = new ConcurrentHashMap<String, CacheData>(); ConcurrentHashMap<String, CacheData> oldCacheDatas = this.cache.putIfAbsent(dataId, newCacheDatas); if (null != oldCacheDatas) { cacheDatas = oldCacheDatas; } else { cacheDatas = newCacheDatas; } } CacheData cacheData = cacheDatas.get(group); if (null == cacheData) { cacheDatas.putIfAbsent(group, new CacheData(dataId, group)); if (log.isInfoEnabled()) { log.info("DataID[" + dataId + "]Group" + group); } this.start(); DiamondClientUtil.addDataId(this.clusterType, dataId + "-" + group); } } public void addDataId(String dataId) { addDataId(dataId, null); } public boolean containDataId(String dataId) { return containDataId(dataId, null); } public boolean containDataId(String dataId, String group) { if (null == group) { group = Constants.DEFAULT_GROUP; } ConcurrentHashMap<String, CacheData> cacheDatas = this.cache.get(dataId); if (null == cacheDatas) { return false; } return cacheDatas.containsKey(group); } public void clearAllDataIds() { this.cache.clear(); } public Set<String> getDataIds() { return new HashSet<String>(this.cache.keySet()); } public ConcurrentHashMap<String, ConcurrentHashMap<String, CacheData>> getCache() { return cache; } public void removeDataId(String dataId) { removeDataId(dataId, null); } public synchronized void removeDataId(String dataId, String group) { if (null == group) { group = Constants.DEFAULT_GROUP; } ConcurrentHashMap<String, CacheData> cacheDatas = this.cache.get(dataId); if (null == cacheDatas) { return; } cacheDatas.remove(group); log.warn("DataID[" + dataId + "]Group: " + group); if (cacheDatas.size() == 0) { this.cache.remove(dataId); log.warn("DataID[" + dataId + "]"); } } public DiamondConfigure getDiamondConfigure() { return this.diamondConfigure; } public void setDiamondConfigure(DiamondConfigure diamondConfigure) { if (!isRun) { this.diamondConfigure = diamondConfigure; } else { // ???? copyDiamondConfigure(diamondConfigure); } if (!diamondConfigure.getDomainNameList().isEmpty()) { DiamondClientUtil.setServerAddrs(this.clusterType, diamondConfigure.getDomainNameList()); } } private void copyDiamondConfigure(DiamondConfigure diamondConfigure) { // TODO ???? } private static String getAppName() { String appName = null; try { appName = AppNameUtils.getAppName(); } catch (Throwable t) { log.warn("Can not getAppName. AppName = null"); appName = null; } if (appName == null) { appName = "null"; } else { appName = appName.trim(); } return appName; } public void setAppName(String appName) { this.appName = appName; } }