2023-09-15
原文作者:王伟王胖胖 原文地址: https://blog.csdn.net/wangwei19871103/article/details/105739179

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

这个应该是最终的了,首先会对一个urlMD5,对这个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里,进行过滤,返回结果内容:

202309152315367391.png

解析

根据后缀名选择解析器,解析器用链表连起来的,从头开始,哪个可以可以解析后缀就用哪个解析出内容,封装成LinkedHashMap返回,具体就不看啦。

202309152315374162.png
解析器:

202309152315379423.png
单链表的结构:

202309152315384874.png
至此就完成了locate方法,结果被封装成CompositePropertySource返回了,因为这里有尝试3次,所以有三个结果,只有最后一次是有内容的。

202309152315392505.png

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。

阅读全文