1. 程式人生 > 其它 >skynet啟動時讀取配置檔案

skynet啟動時讀取配置檔案

新入門skynet系列視訊b站網址 https://www.bilibili.com/video/BV19d4y1678X

skynet啟動時讀取配置檔案

行11 skynet啟動時,我們啟動了一個lua虛擬機器,這個虛擬機器是在整個skynet生命週期中存在的。我們把他叫做skynet的環境虛擬機器

行13 之後啟動了一個臨時虛擬機器,把配置資訊裡面的配置選項都讀取出來,然後儲存到skynet的環境虛擬機器中。之後關閉這個臨時虛擬機器

void
skynet_env_init() {
	E = skynet_malloc(sizeof(*E));
	SPIN_INIT(E)
	E->L = luaL_newstate();//啟動一個環境虛擬機器
}

int
main(int argc, char *argv[]) {
    //...
    skynet_env_init();
    //...
    struct lua_State *L = luaL_newstate();//啟動一個臨時虛擬機器
	luaL_openlibs(L);	// link lua lib

	int err =  luaL_loadbufferx(L, load_config, strlen(load_config), "=[skynet config]", "t");
	assert(err == LUA_OK);
	lua_pushstring(L, config_file);

	err = lua_pcall(L, 1, 1, 0);//執行配置檔案
	if (err) {
		fprintf(stderr,"%s\n",lua_tostring(L,-1));
		lua_close(L);
		return 1;
	}
	_init_env(L);
    
}

我們下面看配置項都是怎麼讀取出來的。

行16 是 load_config表示的 字串載入,然後編譯成一個匿名函式

行18 是給這個匿名函式提供一個引數 引數就是我們skynet啟動時提供的 配置檔案的路徑+檔名 比如 ./example/config

行20 是執行這個匿名函式 把配置項都存入一個表result中

行26 最終把這個result表中的鍵值對都儲存到 環境虛擬機器

我們先看 load_config 表示的lua程式碼

	local result = {} --收集配置項 即鍵值對
	
	local function getenv(name) return assert(os.getenv(name), [[os.getenv() failed: ]] .. name) end
	
	local sep = package.config:sub(1,1) --sep是 /
	local current_path = [[.]]..sep
	
	local function include(filename)
		local last_path = current_path
		local path, name = filename:match([[(.*]]..sep..[[)(.*)$]])--這裡表示字串的形式是 [[字串]]
		if path then 
			if path:sub(1,1) == sep then	--絕對路徑 即path是 /xxx/yyy/zzz/
				current_path = path
			else
				current_path = current_path .. path --相對路徑 即path是 aaa/bbb/
			end
		else
			name = filename
		end
		local f = assert(io.open(current_path .. name)) --開啟配置檔案
		local code = assert(f:read [[*a]]) --讀取
		code = string.gsub(code, [[%$([%w_%d]+)]], getenv)--把系統的環境變數替換成對應的值
		f:close() --注意下面的程式碼 每次都把鍵值對放到result中儲存
		assert(load(code,[[@]]..filename,[[t]],result))() --把配置檔案編譯成匿名函式並執行 注意環境 
		current_path = last_path
	
	end

	setmetatable(result, { __index = { include = include } })
	local config_name = ...--這裡就是我們啟動skynet時的配置檔案 比如 ./example/config
	include(config_name) --開始處理配置檔案
	setmetatable(result, nil)
	
	return result --最終返回配置項的集合

行5->行23 是我們主要討論的讀取配置檔案的函式 local function include(filename) --dosomething end

我們給一個配置檔案的樣子

include(/one/two/config1) 	--這裡是絕對路徑
include(three/config2) 		--這裡是config2是相對於當前配置檔案的位置 也就是 ./three/config2
key1 = "xxxxx"
key2 = "yyyyy"

最終讀取到全域性虛擬機器中的函式是 _init_env(L);這個函式實際上就是把result裡面的鍵值對遍歷出來。

static void
_init_env(lua_State *L) {//L是臨時的lua虛擬機器
	lua_pushnil(L);  /* first key */ //此時-2的位置起始就是result表
	while (lua_next(L, -2) != 0) {//此時已經把result表中的一對鍵值對讀取出來,
		int keyt = lua_type(L, -2);//key放在-2的位置
		if (keyt != LUA_TSTRING) {
			fprintf(stderr, "Invalid config table\n");
			exit(1);
		}
		const char * key = lua_tostring(L,-2);//key放在-2的位置
		if (lua_type(L,-1) == LUA_TBOOLEAN) {//如果value是lua的布林型別
			int b = lua_toboolean(L,-1);
			skynet_setenv(key,b ? "true" : "false" );
		} else {
			const char * value = lua_tostring(L,-1);//value放在-1的位置
			if (value == NULL) {
				fprintf(stderr, "Invalid config table key = %s\n", key);
				exit(1);
			}
			skynet_setenv(key,value);
		}
		lua_pop(L,1);
	}
	lua_pop(L,1);
}

我們看skynet_setenv(key,value);就是最終把鍵值對儲存到環境虛擬機器

void 
skynet_setenv(const char *key, const char *value) {
	SPIN_LOCK(E)
	
	lua_State *L = E->L;
	lua_getglobal(L, key);
	assert(lua_isnil(L, -1));//注意 重複設定鍵值對是會報錯的
	lua_pop(L,1);//下面兩行設定鍵值對
	lua_pushstring(L,value);
	lua_setglobal(L,key);

	SPIN_UNLOCK(E)
}