### 變量[](http://tengine.taobao.org/book/chapter_07.html#id2 "永久鏈接至標題")
[](https://github.com/taobao/nginx-book/issues/new?title=%E6%A8%A1%E5%9D%97%E5%BC%80%E5%8F%91%E9%AB%98%E7%BA%A7%E7%AF%87(30%25)%C2%B6+%E7%BB%BC%E8%BF%B0%C2%B6&body=current%20content%3A%0A%20...%0Aadvice%3A%0A%20...%0Areason%3A%0A%20...%0A "點擊提交Issue,反饋你的意見...")
### 綜述[](http://tengine.taobao.org/book/chapter_07.html#id3 "永久鏈接至標題")
在Nginx中同一個請求需要在模塊之間數據的傳遞或者說在配置文件里面使用模塊動態的數據一般來說都是使用變量,比如在HTTP模塊中導出了host/remote_addr等變量,這樣我們就可以在配置文件中以及在其他的模塊使用這個變量。在Nginx中,有兩種定義變量的方式,一種是在配置文件中,使用set指令,一種就是上面我們提到的在模塊中定義變量,然后導出.
在Nginx中所有的變量都是與HTTP相關的(也就是說賦值都是在請求階段),并且基本上是同時保存在兩個數據結構中,一個就是hash表(可選),另一個是數組. 比如一些特殊的變量,比如arg_xxx/cookie_xxx等,這些變量的名字是不確定的(因此不能內置),而且他們還是只讀的(不能交由用戶修改),如果每個都要放到hash表中的話(不知道用戶會取多少個),會很占空間的,因此這些變量就沒有hash,只有索引.這里要注意,用戶不能定義這樣的變量,這樣的變量只存在于Nginx內部.
對應的變量結構體是這樣子(每一個變量都是一個ngx_http_variable_s結構體)的:
[](http:// "點擊提交Issue,反饋你的意見...")
struct ngx_http_variable_s {
ngx_str_t name; /* must be first to build the hash */
ngx_http_set_variable_pt set_handler;
ngx_http_get_variable_pt get_handler;
uintptr_t data;
ngx_uint_t flags;
ngx_uint_t index;
};
其中name表示對應的變量名字,set/get_handler表示對應的設置以及讀取回調,而data是傳遞給回調的參數,flags表示變量的屬性,index提供了一個索引(數組的腳標),從而可以迅速定位到對應的變量。set/get_handler只有在真正讀取設置變量的時候才會被調用.
這里要注意flag屬性,flag屬性就是由下面的幾個屬性組合而成:
#define NGX_HTTP_VAR_CHANGEABLE ? 1
#define NGX_HTTP_VAR_NOCACHEABLE ?2
#define NGX_HTTP_VAR_INDEXED ? ? ?4
#define NGX_HTTP_VAR_NOHASH ? ? ? 8
1. NGX_HTTP_VAR_CHANGEABLE表示這個變量是可變的.Nginx有很多內置變量是不可變的,比如arg_xxx這類變量,如果你使用set指令來修改,那么Nginx就會報錯.
1. NGX_HTTP_VAR_NOCACHEABLE表示這個變量每次都要去取值,而不是直接返回上次cache的值(配合對應的接口).
1. NGX_HTTP_VAR_INDEXED表示這個變量是用索引讀取的.
1. NGX_HTTP_VAR_NOHASH表示這個變量不需要被hash.
而變量在Nginx中的初始化流程是這樣的:
1. 首先當解析HTTP之前會調用ngx_http_variables_add_core_vars(pre_config)來將HTTP core模塊導出的變量(http_host/remote_addr...)添加進全局的hash key鏈中.
1. 解析完HTTP模塊之后,會調用ngx_http_variables_init_vars來初始化所有的變量(不僅包括HTTP core模塊的變量,也包括其他的HTTP模塊導出的變量,以及配置文件中使用set命令設置的變量),這里的初始化包括初始化hash表,以及初始化數組索引.
1. 當每次請求到來時會給每個請求創建一個變量數組(數組的個數就是上面第二步所保存的變量個數)。然后只有取變量值的時候,才會將變量保存在對應的變量數組位置。
[](http:// "點擊提交Issue,反饋你的意見...")
### 創建變量[](http://tengine.taobao.org/book/chapter_07.html#id4 "永久鏈接至標題")
在Nginx中,創建變量有兩種方式,分別是在配置文件中使用set指令,和在模塊中調用對應的接口,在配置文件中創建變量比較簡單,因此我們主要來看如何在模塊中創建自己的變量。
在Nginx中提供了下面的接口,可以供模塊調用來創建變量。
[](http:// "點擊提交Issue,反饋你的意見...")
ngx_http_variable_t *ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags);
這個函數所做的工作就是將變量 “name”添加進全局的hash key表中,然后初始化一些域,不過這里要注意,對應的變量的get/set回調,需要當這個函數返回之后,顯式的設置,比如在split_clients模塊中的例子:
[](http:// "點擊提交Issue,反饋你的意見...")
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
if (var == NULL) {
return NGX_CONF_ERROR;
}
//設置回調
var->get_handler = ngx_http_split_clients_variable;
var->data = (uintptr_t) ctx;
而對應的回調函數原型是這樣的:
[](http:// "點擊提交Issue,反饋你的意見...")
typedef void (*ngx_http_set_variable_pt) (ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
typedef ngx_int_t (*ngx_http_get_variable_pt) (ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
回調函數比較簡單,第一個參數是當前請求,第二個是需要設置或者獲取的變量值,第三個是初始化時的回調指針,這里我們著重來看一下ngx_http_variable_value_t,下面就是這個結構體的原型:
[](http:// "點擊提交Issue,反饋你的意見...")
typedef struct {
unsigned len:28;
unsigned valid:1;
unsigned no_cacheable:1;
unsigned not_found:1;
unsigned escape:1;
u_char *data;
} ngx_variable_value_t;
這里主要是data域,當我們在get_handle中設置變量值的時候,只需要將對應的值放入到data中就可以了,這里data需要在get_handle中分配內存,比如下面的例子(ngx_http_fastcgi_script_name_variable),就是fastcgi_script_name變量的get_handler代碼片段:
[](http:// "點擊提交Issue,反饋你的意見...")
v->len = f->script_name.len + flcf->index.len;
v->data = ngx_pnalloc(r->pool, v->len);
if (v->data == NULL) {
return NGX_ERROR;
}
p = ngx_copy(v->data, f->script_name.data, f->script_name.len);
ngx_memcpy(p, flcf->index.data, flcf->index.len);
[](http:// "點擊提交Issue,反饋你的意見...")
### 使用變量[](http://tengine.taobao.org/book/chapter_07.html#id5 "永久鏈接至標題")
Nginx的內部變量指的就是Nginx的官方模塊中所導出的變量,在Nginx中,大部分常用的變量都是CORE HTTP模塊導出的。而在Nginx中,不僅可以在模塊代碼中使用變量,而且還可以在配置文件中使用。
假設我們需要在配置文件中使用http模塊的host變量,那么只需要這樣在變量名前加一個$符號就可以了($host).而如果需要在模塊中使用host變量,那么就比較麻煩,Nginx提供了下面幾個接口來取得變量:
[](http:// "點擊提交Issue,反饋你的意見...")
ngx_http_variable_value_t *ngx_http_get_indexed_variable(ngx_http_request_t *r,
ngx_uint_t index);
ngx_http_variable_value_t *ngx_http_get_flushed_variable(ngx_http_request_t *r,
ngx_uint_t index);
ngx_http_variable_value_t *ngx_http_get_variable(ngx_http_request_t *r,
ngx_str_t *name, ngx_uint_t key);
他們的區別是這樣子的,ngx_http_get_indexed_variable和ngx_http_get_flushed_variable都是用來取得有索引的變量,不過他們的區別是后一個會處理 NGX_HTTP_VAR_NOCACHEABLE這個標記,也就是說如果你想要cache你的變量值,那么你的變量屬性就不能設置NGX_HTTP_VAR_NOCACHEABLE,并且通過ngx_http_get_flushed_variable來獲取變量值.而ngx_http_get_variable和上面的區別就是它能夠得到沒有索引的變量值.
通過上面我們知道可以通過索引來得到變量值,可是這個索引該如何取得呢,Nginx也提供了對應的接口:
[](http:// "點擊提交Issue,反饋你的意見...")
ngx_int_t ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);
通過這個接口,就可以取得對應變量名的索引值。
接下來來看對應的例子,比如在http_log模塊中,如果在log_format中配置了對應的變量,那么它會調用ngx_http_get_variable_index來保存索引:
[](http:// "點擊提交Issue,反饋你的意見...")
static ngx_int_t
ngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,
ngx_str_t *value)
{
ngx_int_t index;
//得到變量的索引
index = ngx_http_get_variable_index(cf, value);
if (index == NGX_ERROR) {
return NGX_ERROR;
}
op->len = 0;
op->getlen = ngx_http_log_variable_getlen;
op->run = ngx_http_log_variable;
//保存索引值
op->data = index;
return NGX_OK;
}
然后http_log模塊會使用ngx_http_get_indexed_variable來得到對應的變量值,這里要注意,就是使用這個接口的時候,判斷返回值,不僅要判斷是否為空,也需要判斷value->not_found,這是因為只有第一次調用才會返回空,后續返回就不是空,因此需要判斷value->not_found:
[](http:// "點擊提交Issue,反饋你的意見...")
static u_char *
ngx_http_log_variable(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
{
ngx_http_variable_value_t *value;
//獲取變量值
value = ngx_http_get_indexed_variable(r, op->data);
if (value == NULL || value->not_found) {
*buf = '-';
return buf + 1;
}
if (value->escape == 0) {
return ngx_cpymem(buf, value->data, value->len);
} else {
return (u_char *) ngx_http_log_escape(buf, value->data, value->len);
}
}
- 上篇:nginx模塊開發篇
- nginx平臺初探
- 初探nginx架構
- nginx基礎概念
- connection
- request
- keepalive
- pipe
- lingering_close
- 基本數據結構
- ngx_str_t
- ngx_pool_t
- ngx_array_t
- ngx_hash_t
- ngx_hash_wildcard_t
- ngx_hash_combined_t
- ngx_hash_keys_arrays_t
- ngx_chain_t
- ngx_buf_t
- ngx_list_t
- ngx_queue_t
- nginx的配置系統
- 指令參數
- 指令上下文
- nginx的模塊化體系結構
- 模塊的分類
- nginx的請求處理
- handler模塊
- handler模塊簡介
- 模塊的基本結構
- 模塊配置結構
- 模塊配置指令
- 模塊上下文結構
- 模塊的定義
- handler模塊的基本結構
- handler模塊的掛載
- handler的編寫步驟
- 示例: hello handler 模塊
- handler模塊的編譯和使用
- 更多handler模塊示例分析
- http access module
- http static module
- http log module
- 過濾模塊
- 過濾模塊簡介
- 過濾模塊的分析
- upstream模塊
- upstream模塊
- upstream模塊接口
- memcached模塊分析
- 本節回顧
- 負載均衡模塊
- 配置
- 指令
- 鉤子
- 初始化配置
- 初始化請求
- peer.get和peer.free回調函數
- 本節回顧
- 其他模塊
- core模塊
- event模塊
- 模塊開發高級篇
- 變量
- 下篇:nginx原理解析篇
- nginx架構詳解
- nginx的源碼目錄結構
- nginx的configure原理
- 模塊編譯順序
- nginx基礎設施
- 內存池
- nginx的啟動階段
- 概述
- 共有流程
- 配置解析
- nginx的請求處理階段
- 接收請求流程
- http請求格式簡介
- 請求頭讀取
- 解析請求行
- 解析請求頭
- 請求體讀取
- 讀取請求體
- 丟棄請求體
- 多階段處理請求
- 多階段執行鏈
- POST_READ階段
- SERVER_REWRITE階段
- FIND_CONFIG階段
- REWRITE階段
- POST_REWRITE階段
- PREACCESS階段
- ACCESS階段
- POST_ACCESS階段
- TRY_FILES階段
- CONTENT階段
- LOG階段
- Nginx filter
- header filter分析
- body filter分析
- ngx_http_copy_filter_module分析
- ngx_http_write_filter_module分析
- subrequest原理解析
- https請求處理解析
- 附錄A 編碼風格
- 附錄B 常用API
- 附錄C 模塊編譯,調試與測試