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
次,所以有三个结果,只有最后一次是有内容的。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。