openresty开发系列29--openresty中发起http请求
openresty開發系列29--openresty中發起http請求
有些場景是需要nginx在進行請求轉發
用戶瀏覽器請求url訪問到nginx服務器,但此請求業務需要再次請求其他業務;
如用戶請求訂單服務獲取訂單詳情,可訂單詳情中需要返回商品信息,也就需要再請求商品服務獲取商品信息;
這樣就需要nginx需要有發起http請求的能力,而不是讓用戶瀏覽器再次請求商品信息
nginx服務發起http請求區分內部請求 和 外部請求
圖解
下面我們就介紹一下,openResty中如何發起http請求?
一)內部請求
1)capture請求方法
res = ngx.location.capture(uri,{
?? ?options?
});
options可以傳參數和設置請求方式
local res = ngx.location.capture("/product",{
?? ?method = ngx.HTTP_GET,?? #請求方式
?? ?args = {a=1,b=2},? #get方式傳參數
?? ?body = "c=3&d=4" #post方式傳參數
});
res.status --->保存子請求的響應狀態碼
res.header --->用一個標準 Lua 表儲子請求響應的所有頭信息。如果是"多值"響應頭,
?? ??? ??? --->這些值將使用 Lua (數組) 表順序存儲。
res.body?? --->保存子請求的響應體數據,它可能被截斷。
?? ??? ??? --->用戶需要檢測 res.truncated (截斷) 布爾值標記來判斷 res.body 是否包含截斷的數據。
?? ??? ??? --->這種數據截斷的原因只可能是因為子請求發生了不可恢復的錯誤,
?? ??? ??? --->例如遠端在發送響應體時過早中斷了連接,或子請求在接收遠端響應體時超時。
res.truncated --->是否截斷
-----------------------------------------
編輯nginx.conf配置文件,配置一下路由,定義有個兩個服務請求 商品服務請求和訂單服務請求
location /product {? #商品服務請求
?? ?echo "商品請求";
}
?????? ?
location /order {? #訂單服務請求
?? ?content_by_lua_block {
?? ??? ?local res = ngx.location.capture("/product");
?? ??? ?ngx.say(res.status)
?? ??? ?ngx.say(res.body)
?? ?}
}
ngx.location.capture 方法就是發起http的請求,但是它只能請求 內部服務,不能直接請求外部服務
輸出結果,http狀態為200,返回了 商品服務中的內容
------------------------------------------------
這邊有一種情況,這樣的定義,用戶用瀏覽器直接請求商品服務也照樣請求
可很多時候我們會要求商品請求 是不對外暴露的,也就是用戶無法直接訪問商品服務請求。
那我們只要在內部請求那邊加上一個關鍵字,internal 就可以了
location /product {? #商品服務請求
??? internal;
?? ?echo "商品請求";
}
這樣直接訪問就報404錯誤了
-----------------post 請求-----------------
location /product {? #商品服務請求
?? ?content_by_lua_block {
?? ??? ?ngx.req.read_body();
?? ??? ?local args = ngx.req.get_post_args()
?? ??? ?ngx.print(tonumber(args.a) + tonumber(args.b))
?? ?}
}
?? ??? ?
location /order {? #訂單服務請求
?? ?content_by_lua_block {
?? ??? ?local res = ngx.location.capture("/product",{
?? ??? ??? ?method = ngx.HTTP_POST, ?
?? ??? ??? ?args = {a=1,b=2}, ?
?? ??? ??? ?body = "a=3&b=4"
?? ??? ?});
?? ??? ?ngx.say(res.status)
?? ??? ?ngx.say(res.body)
?? ?}
}
2)capture_multi 并發請求
再以上基礎上面增加需求,要獲得用戶信息
正常邏輯是串行: order request ---> product request ---> user request ----> end
提高性能的方式,發送product請求的同時發起user請求:?? order request ---> product request
?? ??? ??? ??? ??? ??? ??? ??? ??? ---> user request????? ----> end
語法:res1,res2, ... = ngx.location.capture_multi({
?? ??? ??? ??? ??? ??? ??? ??? ?{uri, options?},
?? ??? ??? ??? ??? ??? ??? ??? ?{uri, options?},
?? ??? ??? ??? ??? ??? ??? ??? ?...
?? ??? ??? ??? ??? ??? ?})
-----并發調用
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_multi {
??? 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("/sum", {
??????????????????????? args={a=3, b=8}
??????????????????? })
??????? local res2 = ngx.location.capture("/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)
??? }
}
訪問:http://10.11.0.215/app/test_queue
返回:status:200 response:11 status:200 response:-5 time used:0.22399997711182
訪問:http://10.11.0.215/app/test_multi
返回:status:200 response:11 status:200 response:-5 time used:0.10199999809265
從處理的時間長度看multi并發比queue隊列要快一倍左右
二)外部請求
如何發起外部請求呢?
因為ngx.location.capture不能直接發起外部請求,我們需要通過內部請求中用反向代理請求發起外部請求
請求tmall和淘寶搜索的時候直接通過瀏覽器可以搜索,但是使用反向代理后無法正常返回內容,天貓和淘寶做了反爬蟲處理
?? ?location /tmall {
??????????????? internal;
??????????????? proxy_pass "https://list.tmall.com/search_product.htm?q=ipone";
??????? }
??????? location /ordertmall {
??????????????? content_by_lua_block {
??????????????????????? local res = ngx.location.capture("/tmall");
??????????????????????? ngx.say(res.status)
??????????????????????? ngx.say(res.body)
??????????????? }
??????? }
類似的例子:
?? ?location /baidu {
?? ??? ?internal;
?? ??? ?# 防止返回亂碼
?? ??? ?proxy_set_header Accept-Encoding?? ' ';
?? ??? ?proxy_pass "https://www.baidu.com/s?wd=iphone";
?? ??? ?#proxy_pass "https://www.baidu.com";
?? ?}
?? ?location /orderbaidu {
?? ??? ?content_by_lua_block {
?? ??? ??? ?local res = ngx.location.capture("/baidu");
?? ??? ??? ?ngx.say(res.status)
?? ??? ??? ?ngx.say(res.body)
?? ??? ?}
?? ?}
在商品服務那邊用的proxy_pass 請求外部http請求,這樣就達到了請求外部http的目的。
請求返回了302,表示請求成功了;
但發現都是亂碼,這個是什么原因呢?
一開始想到的是字符編碼的問題,需要把虛擬主機的server模塊配置一個字符編碼
charset UTF-8;?? 設置為utf-8。重啟nginx重新訪問
還是亂碼,這是為什么呢,編碼不是改了嗎?這個是因為baidu這個web服務器加了gzip壓縮,
他返回給我們的結果是經過壓縮的,我們再接受過來的時候需要解壓才行,那怎么辦?
我們可以讓taobao服務返回不需要壓縮的數據嗎?? 我們可以在請求外部鏈接那邊設置
proxy_set_header Accept-Encoding?? ' ';#讓后端不要返回壓縮(gzip或deflate)的內容
最終
?? ?location /baidu {
?? ??? ?internal;
?? ??? ?# 防止返回亂碼
?? ??? ?proxy_set_header Accept-Encoding?? ' ';
?? ??? ?proxy_pass "https://www.baidu.com/s?wd=iphone";
?? ??? ?#proxy_pass "https://www.baidu.com";
?? ?}
重啟nginx,再次訪問,結果輸出
以上我們介紹了 內部訪問 和? 外部訪問
三)動態變量
剛才我們請求外部請求,是寫死了wd=iphone,那我們用capture傳參
?? ?location /baidu02 {
?? ??? ?internal;
?? ??? ?resolver 8.8.8.8;
?? ??? ?proxy_set_header Accept-Encoding ' ';
?? ??? ?proxy_pass "https://www.baidu.com/s?wd=$arg_wd";
?? ?}
?? ?location /orderbaidu02 {
?? ??? ?content_by_lua_block {
?? ??? ??? ?local get_args = ngx.req.get_uri_args();
?? ??? ??? ?local res = ngx.location.capture("/baidu02",{
?? ??? ??? ??? ?method = ngx.HTTP_GET,
?? ??? ??? ??? ?args = {wd=get_args["wd"]}
?? ??? ??? ?});
?? ??? ??? ?ngx.say(res.status)
?? ??? ??? ?ngx.say(res.body)
?? ??? ?}
?? ?}
訪問:http://192.168.10.164/orderbaidu02?wd=huawei
添加搜索參數 wd=huawei就會返回華為手機的結果
注意:在proxy_pass 中使用變量,需要使用resolver指令解析變量中的域名
# google 域名解析
resolver 8.8.8.8;
這樣我們請求傳wd參數的值,隨便由用戶決定查詢什么值。
我們這邊就發現了請求外部服務的時候發現比較復雜,我們可以借用第三方的庫 resty.http ,
從可實現外部請求,而且使用很方便,下篇文章我們就介紹resty.http模塊
轉載于:https://www.cnblogs.com/reblue520/p/11434871.html
總結
以上是生活随笔為你收集整理的openresty开发系列29--openresty中发起http请求的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: openresty开发系列28--ope
- 下一篇: openresty开发系列30--ope