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模塊nginx

相關文章
相關標籤/搜索