天天看點

Nacos源碼分析十四、服務端處理擷取配置請求

Nacos用戶端的學習已經基本告一段落。從本篇開始我們把關注點放在服務端。

我們知道nacos服務端主要功能就是提供配置中心的功能和命名服務。接下來我們還是按這兩部分讨論。

首先是配置中心。我們先看一下服務端如何處理用戶端發起的擷取配置的請求,相應接口是在com.alibaba.nacos.config.server.controller.ConfigController中定義,我們看一下getConfig方法:

@GetMapping
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
public void getConfig(HttpServletRequest request, HttpServletResponse response,
        @RequestParam("dataId") String dataId, @RequestParam("group") String group,
        @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,
        @RequestParam(value = "tag", required = false) String tag)
        throws IOException, ServletException, NacosException {
    // check tenant
    ParamUtils.checkTenant(tenant);
    tenant = processTenant(tenant);
    // check params
    ParamUtils.checkParam(dataId, group, "datumId", "content");
    ParamUtils.checkParam(tag);
    
    final String clientIp = RequestUtil.getRemoteIp(request);
    inner.doGetConfig(request, response, dataId, group, tenant, tag, clientIp);
}
           

先是參數檢查,然後擷取用戶端IP,最後調用ConfigServletInner的doGetConfig方法。

doGetConfig方法比較長,先畫一個流程圖吧:

Nacos源碼分析十四、服務端處理擷取配置請求
  1. 首先擷取讀取鎖,如果沒有得到鎖則傳回失敗。lockResult>0時執行讀取操作
  2. 然後是判斷beta、tag、autoTag來确定是否帶對應的參數進行資料擷取
  3. 再根據PropertyUtil.isDirectRead()方法确定是通過mysql讀取還是本地檔案
  4. response寫對應的内容,最終傳回。

我們看一下PropertyUtil.isDirectRead()判斷:

public static boolean isDirectRead() {
    return ApplicationUtils.getStandaloneMode() && isEmbeddedStorage();
}
           

是否是單機模式,并且開啟mysql存儲,預設的embeddedStorage值和是否是單機模式值一緻:

private static boolean embeddedStorage = ApplicationUtils.getStandaloneMode();
           

persistService.findConfigInfo4Beta是beta的資料庫查詢:

@Override
public ConfigInfo4Beta findConfigInfo4Beta(final String dataId, final String group, final String tenant) {
    String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
    final String sql = "SELECT ID,data_id,group_id,tenant_id,app_name,content,beta_ips FROM config_info_beta WHERE data_id=? AND group_id=? AND tenant_id=?";
    
    return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_INFO4BETA_ROW_MAPPER);
    
}
           

findConfigInfo 不帶tag的:

@Override
public ConfigInfo findConfigInfo(final String dataId, final String group, final String tenant) {
    final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
    final String sql = "SELECT ID,data_id,group_id,tenant_id,app_name,content,md5,type FROM config_info "
            + " WHERE data_id=? AND group_id=? AND tenant_id=?";
    final Object[] args = new Object[] {dataId, group, tenantTmp};
    return databaseOperate.queryOne(sql, args, CONFIG_INFO_ROW_MAPPER);
    
}
           

findConfigInfo4Tag 帶tag的:

@Override
public ConfigInfo4Tag findConfigInfo4Tag(final String dataId, final String group, final String tenant,
        final String tag) {
    String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
    String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim();
    
    final String sql = "SELECT ID,data_id,group_id,tenant_id,tag_id,app_name,content FROM config_info_tag WHERE data_id=? AND group_id=? AND tenant_id=? AND tag_id=?";
    
    return databaseOperate
            .queryOne(sql, new Object[] {dataId, group, tenantTmp, tagTmp}, CONFIG_INFO4TAG_ROW_MAPPER);
}
           

然後我們看一下DiskUtil的擷取file的方法,以帶tag的為例:

public static File targetTagFile(String dataId, String group, String tenant, String tag) {
    File file = null;
    if (StringUtils.isBlank(tenant)) {
        file = new File(ApplicationUtils.getNacosHome(), TAG_DIR);
    } else {
        file = new File(ApplicationUtils.getNacosHome(), TENANT_TAG_DIR);
        file = new File(file, tenant);
    }
    file = new File(file, group);
    file = new File(file, dataId);
    file = new File(file, tag);
    return file;
}
           

就是分目錄往下new File

最後我們看一下response寫檔案:

fis.getChannel()
        .transferTo(0L, fis.getChannel().size(), Channels.newChannel(response.getOutputStream()));
           

這個是檔案的輸入流轉response的輸出流。 零拷貝傳輸。

總結

服務端如何處理擷取配置的請求基本分析完了。

  1. 讀鎖的擷取
  2. beta、tag、autoTag的判斷來确認具體讀什麼配置
  3. PropertyUtil.isDirectRead()判斷是讀mysql還是讀檔案
  4. 使用jdk的零拷貝傳輸直接将檔案輸入流轉response輸出流