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方法比較長,先畫一個流程圖吧:
- 首先擷取讀取鎖,如果沒有得到鎖則傳回失敗。lockResult>0時執行讀取操作
- 然後是判斷beta、tag、autoTag來确定是否帶對應的參數進行資料擷取
- 再根據PropertyUtil.isDirectRead()方法确定是通過mysql讀取還是本地檔案
- 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的輸出流。 零拷貝傳輸。
總結
服務端如何處理擷取配置的請求基本分析完了。
- 讀鎖的擷取
- beta、tag、autoTag的判斷來确認具體讀什麼配置
- PropertyUtil.isDirectRead()判斷是讀mysql還是讀檔案
- 使用jdk的零拷貝傳輸直接将檔案輸入流轉response輸出流