(1). 概述

在这一小节,主要剖析:ConfigService,它是由:NacosFactory构建出来的.

(2). NacosFactory

public class NacosFactory {

    public static ConfigService createConfigService(Properties properties) throws NacosException {
		// ***********************************************************
		// 委托给了:ConfigFactory
		// ***********************************************************
        return ConfigFactory.createConfigService(properties);
    }
}

(3). ConfigFactory

public class ConfigFactory {

    public static ConfigService createConfigService(Properties properties) throws NacosException {
        try {
			// ************************************************************
			// 通过反射,加载并且new
			// ************************************************************
            Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService");
            Constructor constructor = driverImplClass.getConstructor(Properties.class);
            ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties);
            return vendorImpl;
        } catch (Throwable e) {
            throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
        }
    }
}

(4). ConfigService

public class NacosConfigService implements ConfigService {
	private static final long POST_TIMEOUT = 3000L;
	private static final String EMPTY = "";
	
	/**
	 * http agent
	 */
	private HttpAgent agent;
	/**
	 * longpolling
	 */
	private ClientWorker worker;
	private String namespace;
	private String encode;
	
	// ******************************************************************************
	// 责任链管理者
	// ******************************************************************************
	private ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager();

	// ********************************************************
	// 1. 构建器初始化
	//    设置encode
	//    获得
	// ********************************************************
	public NacosConfigService(Properties properties) throws NacosException {
		// properties = {secretKey=, namespace=45a6112b-a866-4e92-a8d6-9e440bcdebbe, fileExtension=properties, username=, enableRemoteSyncConfig=false, configLongPollTimeout=, configRetryTime=, encode=, serverAddr=127.0.0.1:8848, maxRetry=, group=erp:test-provider, clusterName=, password=, accessKey=, endpoint=}
		String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE);
        if (StringUtils.isBlank(encodeTmp)) {
            encode = Constants.ENCODE;
        } else {
            encode = encodeTmp.trim();
        }
		// 获得namespace
        initNamespace(properties);
		
		// ********************************************************
		// 2. 最终请求Nacos会通过MetricsHttpAgent去请求的
		// ********************************************************
        agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
        agent.start();
		
		// 3. 创建:ClientWorker(会委派给:MetricsHttpAgent)
        worker = new ClientWorker(agent, configFilterChainManager, properties);
    }
	
	
	// ***********************************************
	// 4. 获取配置信息
	// ***********************************************
	private String getConfigInner(
	     // 45a6112b-a866-4e92-a8d6-9e440bcdebbe
	     String tenant, 
		 // test-provider
		 String dataId, 
		 // erp:test-provider
		 String group, 
		 // 3000
		 long timeoutMs) throws NacosException {
		
		// 如果group为null,则,group=DEFAULT_GROUP
		group = null2defaultGroup(group);
		// 检查参数
		ParamUtils.checkKeyParam(dataId, group);
		
		// 返回结果
		ConfigResponse cr = new ConfigResponse();
		cr.setDataId(dataId);
		cr.setTenant(tenant);
		cr.setGroup(group);

		// 优先使用本地配置
		String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
		if (content != null) {
			LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", agent.getName(),
				dataId, group, tenant, ContentUtils.truncateContent(content));
			cr.setContent(content);
			configFilterChainManager.doFilter(null, cr);
			content = cr.getContent();
			return content;
		}

		try {
			// ******************************************************************
			// 5. 委托给:ClientWorker
			// ******************************************************************
			String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
			// 并获得数组的第0个元素,赋值给:ConfigResponse
			cr.setContent(ct[0]);
			
			// 调用责任链,对:ConfigResponse进行处理
			configFilterChainManager.doFilter(null, cr);
			
			// 返回被责任链处理过之后的内容
			content = cr.getContent();
			return content;
		} catch (NacosException ioe) {
			if (NacosException.NO_RIGHT == ioe.getErrCode()) {
				throw ioe;
			}
			LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",
				agent.getName(), dataId, group, tenant, ioe.toString());
		}
		
		// 当抛出的异常不是NacosException时,获取本地的快照.
		LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", agent.getName(),
			dataId, group, tenant, ContentUtils.truncateContent(content));
		content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);
		cr.setContent(content);
		configFilterChainManager.doFilter(null, cr);
		content = cr.getContent();
		return content;
	} // end getConfigInner
}	

(5). ClientWorker

public class ClientWorker {
	
	public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout)
        throws NacosException {
		// 创建返回结果	
        String[] ct = new String[2];
		// 又做了一次group检查
        if (StringUtils.isBlank(group)) {
            group = Constants.DEFAULT_GROUP;
        }
		

        HttpResult result = null;
        try {
			// 构建请求参数
            List<String> params = null;
            if (StringUtils.isBlank(tenant)) {
                params = new ArrayList<String>(Arrays.asList("dataId", dataId, "group", group));
            } else {
                params = new ArrayList<String>(Arrays.asList("dataId", dataId, "group", group, "tenant", tenant));
            }
			
			// **************************************************************
			// 委托给:ServerHttpAgent,最终还是会委托给:HttpSimpleClient,这是Nacos自己基于HttpURLConnection写的一个HTTP工具
			// **************************************************************
            result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);
        } catch (IOException e) {  
			// 异常处理
            String message = String.format(
                "[%s] [sub-server] get server config exception, dataId=%s, group=%s, tenant=%s", agent.getName(),
                dataId, group, tenant);
            LOGGER.error(message, e);
            throw new NacosException(NacosException.SERVER_ERROR, e);
        }

        switch (result.code) {
            case HttpURLConnection.HTTP_OK:  // 成功的情况下,把结果放在内存快照里.
                LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content);
				// ct[0] 为返回的结果内容
                ct[0] = result.content;
                // ct[1] 为Config-Type(properties)
				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);
            }
        }
    }
}

(6). 启动时发起的HTTP请求

# 1. get
http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=test-provider.properties&group=erp%3Atest-provider&tenant=45a6112b-a866-4e92-a8d6-9e440bcdebbe
	useLocalCache=true
	config-type=properties

# 2. post
http://127.0.0.1:8848/nacos/v1/cs/configs/listener

(7). 总结

好像没什么要总结的,都比较简单.