MetricsHttpAgent的httpGet
封装了一层计时,内部还是HttpAgent
去请求。
@Override
public HttpResult httpGet(String path, List<String> headers, List<String> paramValues, String encoding, long readTimeoutMs) throws IOException {
Histogram.Timer timer = MetricsMonitor.getConfigRequestMonitor("GET", path, "NA");
HttpResult result = null;
try {
result = httpAgent.httpGet(path, headers, paramValues, encoding, readTimeoutMs);
} catch (IOException e) {
throw e;
} finally {
timer.observeDuration();
timer.close();
}
return result;
}
ServerHttpAgent的httpGet
设置一些参数信息,最后请求http://xxxxxx:xxxx/nacos/v1/cs/configs
,带上参数,使用HttpSimpleClient.httpGet
,然后根据返回设置当前的服务器地址,如果一个服务器请求失败,就尝试获取下一个服务器地址,继续请求。但是有尝试次数和超时限制。
@Override
public HttpResult httpGet(String path, List<String> headers, List<String> paramValues, String encoding,
long readTimeoutMs) throws IOException {
final long endTime = System.currentTimeMillis() + readTimeoutMs;//超时时间
final boolean isSSL = false;
injectSecurityInfo(paramValues);//安全信息,accessToken,tenant等
String currentServerAddr = serverListMgr.getCurrentServerAddr();//当前服务器地址
int maxRetry = this.maxRetry;//默认3次最大请求次数
do {
try {
List<String> newHeaders = getSpasHeaders(paramValues);
if (headers != null) {
newHeaders.addAll(headers);//头信息
}
HttpResult result = HttpSimpleClient.httpGet(
getUrl(currentServerAddr, path), newHeaders, paramValues, encoding,
readTimeoutMs, isSSL);
if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR
|| result.code == HttpURLConnection.HTTP_BAD_GATEWAY
|| result.code == HttpURLConnection.HTTP_UNAVAILABLE) {
LOGGER.error("[NACOS ConnectException] currentServerAddr: {}, httpCode: {}",
serverListMgr.getCurrentServerAddr(), result.code);
} else {
// 可用就更新
serverListMgr.updateCurrentServerAddr(currentServerAddr);
return result;
}
} catch (ConnectException ce) {
...
}
if (serverListMgr.getIterator().hasNext()) {
currentServerAddr = serverListMgr.getIterator().next();
} else {
maxRetry--;//次数限制
if (maxRetry < 0) {
throw new ConnectException("[NACOS HTTP-GET] The maximum number of tolerable server reconnection errors has been reached");
}
serverListMgr.refreshCurrentServerAddr();
}
} while (System.currentTimeMillis() <= endTime);//超时限制
LOGGER.error("no available server");
throw new ConnectException("no available server");
}
HttpSimpleClient的httpGet
这个应该是最终的了,首先会对一个url
做MD5
,对这个MD5
值做检查,有一个频率的限制,默认每秒5
次请求。剩下的就是用URL
去请求啦,然后读取流信息。
static public HttpResult httpGet(String url, List<String> headers, List<String> paramValues,
String encoding, long readTimeoutMs, boolean isSSL) throws IOException {
String encodedContent = encodingParams(paramValues, encoding);
url += (null == encodedContent) ? "" : ("?" + encodedContent);
if (Limiter.isLimit(MD5.getInstance().getMD5String(
new StringBuilder(url).append(encodedContent).toString()))) {
return new HttpResult(NacosException.CLIENT_OVER_THRESHOLD,
"More than client-side current limit threshold");
}
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(ParamUtil.getConnectTimeout() > 100 ? ParamUtil.getConnectTimeout() : 100);
conn.setReadTimeout((int) readTimeoutMs);
List<String> newHeaders = getHeaders(url, headers, paramValues);
setHeaders(conn, newHeaders, encoding);
conn.connect();
int respCode = conn.getResponseCode();
String resp = null;
if (HttpURLConnection.HTTP_OK == respCode) {
resp = IoUtils.toString(conn.getInputStream(), encoding);
} else {
resp = IoUtils.toString(conn.getErrorStream(), encoding);
}
return new HttpResult(respCode, conn.getHeaderFields(), resp);
} finally {
IoUtils.closeQuietly(conn);
}
}
状态码处理
最后根据状态码会有一些处理,比如保存内容,保存相应头的Config-Type
信息。
switch (result.code) {
case HttpURLConnection.HTTP_OK:
//保存快照
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content);
ct[0] = result.content;//取出内容
if (result.headers.containsKey(CONFIG_TYPE)) {
ct[1] = result.headers.get(CONFIG_TYPE).get(0);//取出类型
} else {
ct[1] = ConfigType.TEXT.getType();
}
return ct;
case HttpURLConnection.HTTP_NOT_FOUND://保存快照
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null);
return ct;
case HttpURLConnection.HTTP_CONFLICT: {
LOGGER.error(
"[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, "
+ "tenant={}", agent.getName(), dataId, group, tenant);
throw new NacosException(NacosException.CONFLICT,
"data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant);
}
case HttpURLConnection.HTTP_FORBIDDEN: {
LOGGER.error("[{}] [sub-server-error] no right, dataId={}, group={}, tenant={}", agent.getName(), dataId,
group, tenant);
throw new NacosException(result.code, result.content);
}
default: {
LOGGER.error("[{}] [sub-server-error] dataId={}, group={}, tenant={}, code={}", agent.getName(), dataId,
group, tenant, result.code);
throw new NacosException(result.code,
"http error, code=" + result.code + ",dataId=" + dataId + ",group=" + group + ",tenant=" + tenant);
}
结果处理
然后把结果写进响应ConfigResponse
里,进行过滤,返回结果内容:
解析
根据后缀名选择解析器,解析器用链表连起来的,从头开始,哪个可以可以解析后缀就用哪个解析出内容,封装成LinkedHashMap
返回,具体就不看啦。
解析器:
单链表的结构:
至此就完成了locate方法,结果被封装成CompositePropertySource
返回了,因为这里有尝试3
次,所以有三个结果,只有最后一次是有内容的。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。