nginx 世界的 location 是異常強(qiáng)大的,畢竟 nginx 的主要應(yīng)用場(chǎng)景是在負(fù)載均衡、API server,在不同 server、location 之間跳轉(zhuǎn)更是家常便飯。利用不同 location 的功能組合,我們可以完成內(nèi)部調(diào)用、流水線方式跳轉(zhuǎn)、外部重定向等幾大不同方式,下面將給大家介紹幾個(gè)主要應(yīng)用,就當(dāng)拋磚引玉。
例如對(duì)數(shù)據(jù)庫、內(nèi)部公共函數(shù)的統(tǒng)一接口,可以把它們放到統(tǒng)一的 location 中。通常情況下,為了保護(hù)這些內(nèi)部接口,都會(huì)把這些接口設(shè)置為 internal 。這么做的最主要好處就是可以讓這個(gè)內(nèi)部接口相對(duì)獨(dú)立,不受外界干擾。
示例代碼:
location = /sum {
# 只允許內(nèi)部調(diào)用
internal;
# 這里做了一個(gè)求和運(yùn)算只是一個(gè)例子,可以在這里完成一些數(shù)據(jù)庫、
# 緩存服務(wù)器的操作,達(dá)到基礎(chǔ)模塊和業(yè)務(wù)邏輯分離目的
content_by_lua_block {
local args = ngx.req.get_uri_args()
ngx.say(tonumber(args.a) + tonumber(args.b))
}
}
location = /app/test {
content_by_lua_block {
local res = ngx.location.capture(
"/sum", {args={a=3, b=8}}
)
ngx.say("status:", res.status, " response:", res.body)
}
}
緊接著,稍微擴(kuò)充一下,并行請(qǐng)求的效果,示例如下:
location = /sum {
internal;
content_by_lua_block {
ngx.sleep(0.1)
local args = ngx.req.get_uri_args()
ngx.print(tonumber(args.a) + tonumber(args.b))
}
}
location = /subduction {
internal;
content_by_lua_block {
ngx.sleep(0.1)
local args = ngx.req.get_uri_args()
ngx.print(tonumber(args.a) - tonumber(args.b))
}
}
location = /app/test_parallels {
content_by_lua_block {
local start_time = ngx.now()
local res1, res2 = ngx.location.capture_multi( {
{"/sum", {args={a=3, b=8}}},
{"/subduction", {args={a=3, b=8}}}
})
ngx.say("status:", res1.status, " response:", res1.body)
ngx.say("status:", res2.status, " response:", res2.body)
ngx.say("time used:", ngx.now() - start_time)
}
}
location = /app/test_queue {
content_by_lua_block {
local start_time = ngx.now()
local res1 = ngx.location.capture_multi( {
{"/sum", {args={a=3, b=8}}}
})
local res2 = ngx.location.capture_multi( {
{"/subduction", {args={a=3, b=8}}}
})
ngx.say("status:", res1.status, " response:", res1.body)
ngx.say("status:", res2.status, " response:", res2.body)
ngx.say("time used:", ngx.now() - start_time)
}
}
測(cè)試結(jié)果:
? ~ curl 127.0.0.1/app/test_parallels
status:200 response:11
status:200 response:-5
time used:0.10099983215332
? ~ curl 127.0.0.1/app/test_queue
status:200 response:11
status:200 response:-5
time used:0.20199990272522
利用 ngx.location.capture_multi
函數(shù),直接完成了兩個(gè)子請(qǐng)求并行執(zhí)行。當(dāng)兩個(gè)請(qǐng)求沒有相互依賴,這種方法可以極大提高查詢效率。兩個(gè)無依賴請(qǐng)求,各自是 100ms,順序執(zhí)行需要 200ms,但通過并行執(zhí)行可以在 100ms 完成兩個(gè)請(qǐng)求。實(shí)際生產(chǎn)中查詢時(shí)間可能沒這么規(guī)整,但思想大同小異,這個(gè)特性是很有用的。
http://wiki.jikexueyuan.com/project/openresty/images/work_location_flow_1.png" alt="圖例" />
該方法,可以被廣泛應(yīng)用于廣告系統(tǒng)(1:N模型,一個(gè)請(qǐng)求,后端從N家供應(yīng)商中獲取條件最優(yōu)廣告)、高并發(fā)前端頁面展示(并行無依賴界面、降級(jí)開關(guān)等)。
現(xiàn)在的網(wǎng)絡(luò)請(qǐng)求,已經(jīng)變得越來越擁擠。各種不同 API 、下載請(qǐng)求混雜在一起,就要求不同廠商對(duì)下載的動(dòng)態(tài)調(diào)整有各種不同的定制策略,而這些策略在一天的不同時(shí)間段,規(guī)則可能還不一樣。這時(shí)候我們還可以效仿工廠的流水線模式,逐層過濾、處理。
示例代碼:
location ~ ^/static/([-_a-zA-Z0-9/]+).jpg {
set $image_name $1;
content_by_lua_block {
ngx.exec("/download_internal/images/"
.. ngx.var.image_name .. ".jpg");
};
}
location /download_internal {
internal;
# 這里還可以有其他統(tǒng)一的 download 下載設(shè)置,例如限速等
alias ../download;
}
注意,ngx.exec 方法與 ngx.redirect 是完全不同的,前者是個(gè)純粹的內(nèi)部跳轉(zhuǎn)并且沒有引入任何額外 HTTP 信號(hào)。 這里的兩個(gè) location 更像是流水線上工人之間的協(xié)作關(guān)系。第一環(huán)節(jié)的工人對(duì)完成自己處理部分后,直接交給第二環(huán)節(jié)處理人(實(shí)際上可以有更多環(huán)節(jié)),它們之間的數(shù)據(jù)流是定向的。
http://wiki.jikexueyuan.com/project/openresty/images/work_location_flow_2.png" alt="圖例" />
不知道大家什么時(shí)候開始注意的,百度的首頁已經(jīng)不再是 HTTP 協(xié)議,它已經(jīng)全面修改到了 HTTPS 協(xié)議上。但是對(duì)于大家的輸入習(xí)慣,估計(jì)還是在地址欄里面輸入 baidu.com
,回車后發(fā)現(xiàn)它會(huì)自動(dòng)跳轉(zhuǎn)到 https://www.baidu.com
,這時(shí)候就需要的外部重定向了。
location = /foo {
content_by_lua_block {
ngx.say([[I am foo]])
}
}
location = / {
rewrite_by_lua_block {
return ngx.redirect('/foo');
}
}
執(zhí)行測(cè)試,結(jié)果如下:
? ~ curl 127.0.0.1 -i
HTTP/1.1 302 Moved Temporarily
Server: openresty/1.9.3.2rc3
Date: Sun, 22 Nov 2015 11:04:03 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: /foo
<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>openresty/1.9.3.2rc3</center>
</body>
</html>
? ~ curl 127.0.0.1/foo -i
HTTP/1.1 200 OK
Server: openresty/1.9.3.2rc3
Date: Sun, 22 Nov 2015 10:43:51 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
I am foo
當(dāng)我們使用瀏覽器訪問頁面 http://127.0.0.1
就可以發(fā)現(xiàn)瀏覽器會(huì)自動(dòng)跳轉(zhuǎn)到 http://127.0.0.1/foo
。
與之前兩個(gè)應(yīng)用實(shí)例不同的,外部重定向是可以跨域名的。例如從 A 網(wǎng)站跳轉(zhuǎn)到 B 網(wǎng)站是絕對(duì)允許的。在 CDN 場(chǎng)景的大量下載應(yīng)用中,一般分為調(diào)度、存儲(chǔ)兩個(gè)重要環(huán)節(jié)。調(diào)度就是通過根據(jù)請(qǐng)求方 IP 、下載文件等信息尋找最近、最快節(jié)點(diǎn),應(yīng)答跳轉(zhuǎn)給請(qǐng)求方完成下載。