nginx的腳本的語法和shell是很像的,我大體看了一下以爲挺有意思的,就想寫寫記錄一下。我沒看過shell腳本的引擎,不知道nginx腳本引擎和shell腳本引擎像不像,可是我以爲nginx的腳本引擎有點像C和彙編。html
ngx_http_script_engine_t這個結構就表明了一段腳本,ip指向的是編譯好的腳本,sp指向的是一塊內存用來存儲腳本運行的時候產生的一些中間值。ip/sp從名字看就已經很像彙編了instruction pointer/stack pointer指令寄存器和棧寄存器呀,固然是我瞎猜的,有時間的話能夠查一下官方文檔。代碼段裏的各個指令長度不必定相同。nginx
再來講說編譯過程,編譯過程是在nginx_http_script_engine_t創建以前執行的,我先畫出了整個圖是爲了更好理解。舉個set指令編譯的的例子,好比你在腳本里有這樣的代碼set $foo helloworld,腳本編譯的步驟以下:shell
第一步:首先在cmcf->variables_keys和cmcf->variables裏增長一個變量foo,這個變量是可寫的。我以前寫的nginx的變量系統裏只說了變量的讀取方法,差異不大。函數
第二步:把ngx_http_script_value_code_t指令放到代碼段裏(code字段是一個回調函數,賦值成ngx_http_script_value_code),把ngx_http_script_var_code_t指令放到代碼段裏(code字段是一個回調函數,賦值成ngx_http_script_set_var_code)。url
第三步:http請求來的時候會在rewrite階段按順序執行ip指向的這一段代碼,也就是執行ngx_http_script_value_code和ngx_http_script_set_var_code函數。.net
咱們看一下這兩個函數作了什麼debug
void ngx_http_script_value_code(ngx_http_script_engine_t *e) { ngx_http_script_value_code_t *code; code = (ngx_http_script_value_code_t *) e->ip; e->ip += sizeof(ngx_http_script_value_code_t); e->sp->len = code->text_len; e->sp->data = (u_char *) code->text_data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, "http script value: \"%v\"", e->sp); e->sp++; }
void ngx_http_script_set_var_code(ngx_http_script_engine_t *e) { ngx_http_request_t *r; ngx_http_script_var_code_t *code; code = (ngx_http_script_var_code_t *) e->ip; e->ip += sizeof(ngx_http_script_var_code_t); r = e->request; e->sp--; r->variables[code->index].len = e->sp->len; r->variables[code->index].valid = 1; r->variables[code->index].no_cacheable = 0; r->variables[code->index].not_found = 0; r->variables[code->index].data = e->sp->data; }
第一條指令把「helloworld」這個字符串放到了sp裏,第二條指令把值從sp裏取出來存到了變量系統的foo變量裏,任務完成,看起來很簡單。code
set指令還能夠這樣用set $foo $x$y,這就是所謂的變量插值,過程和上面這個相似,只不過第一條指令是先從變量系統裏取出$x和$y的值,再放入sp裏。htm
其餘指令和set指令的執行過程相似,把我看到的也寫一下吧blog
if指令:一樣舉個最簡單的例子if( $host = "www.foo.com" ),編譯的時候依次把ngx_http_script_var_code/ngx_http_script_value_code_t/ngx_http_script_equal_code/ngx_http_script_if_code四條指令放到代碼段裏。腳本運行的時候這幾條指令的工做分別是ngx_http_script_var_code把變量host的值取出來放到sp裏。ngx_http_script_value_code_t把字符串「www.foo.com」放到sp裏。ngx_http_script_equal_code比較sp裏存的兩個值是否相等並把兩個值清除掉,相等就在sp裏寫入「1」,不相等就寫入「0」(比較完之後這兩個值就沒用了,清除掉這兩個值而且寫入結果很像C裏函數調用的過程)。ngx_http_script_if_code檢查sp裏的值是否是「0」,不是「0」說明條件爲真繼續執行以後的腳本,是「0」說明條件爲假就會跳過這一段代碼執行ngx_http_script_if_code_t結構裏next偏移以後的代碼。全部的代碼都是在一個代碼段裏,不會由於有if把代碼作嵌套,只不過會用next跳來跳去。
有一點須要注意若是if在location裏if體裏能夠作一些location的配置,好比root之類的。當NGX_HTTP_REWRITE_PHASE階段執行腳本的時候會把新的loc_conf賦值給r->loc_conf,這個必定要注意是NGX_HTTP_REWRITE_PHASE階段而不是NGX_HTTP_FIND_CONFIG_PHASE階段,設置loc_conf通常狀況是在NGX_HTTP_FIND_CONFIG_PHASE階段,可是此次不是。
void ngx_http_script_if_code(ngx_http_script_engine_t *e) { ngx_http_script_if_code_t *code; code = (ngx_http_script_if_code_t *) e->ip; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, "http script if"); e->sp--; if (e->sp->len && (e->sp->len != 1 || e->sp->data[0] != '0')) { if (code->loc_conf) { e->request->loc_conf = code->loc_conf; ngx_http_update_location_config(e->request); } e->ip += sizeof(ngx_http_script_if_code_t); return; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, "http script if: false"); e->ip += code->next; }
return指令:這個是比較簡單的,腳本執行到這個指令就直接返回了,參數了能夠帶數據,例如return 200 helloworld,此外還能夠重定向return 302 http://www.nginx.org。
break指令:粗暴的結束目前的腳本,可是有一點要注意,若是break指令在location裏面,他並不會影響location其餘字段的設置,由於他們在不一樣的階段執行。好比說設置以下的配置文件
location / { root html; break; index index.html; }
這一點都不會影響你的index指令,他們不在同一階段,index是在NGX_HTTP_FIND_CONFIG_PHASE階段,break是在NGX_HTTP_REWRITE_PHASE階段,就像if指令裏說的那樣。
rewrite指令:這個略顯麻煩,可是道理是同樣的,休息,明天接着寫。