web高性能使用openresty的一些高級用法、redis的常用法、簡易防火牆、禁止終端

記錄使用openresty在web中的高級使用技術

openresty內部處理流程說明

nginx中這三個階段的執行順序:access--》content--》body_filter;

access_by_lua_file:獲取協議版本-->獲取bodys數據-->協議解碼-->設置body數據

content_by_lua_file:正常處理業務邏輯,零修改

body_filter_by_lua_file:判斷協議版本-->協議編碼

1、與其他location配合使用

       內部調用:例如對數據庫、內部公共函數的統一接口,可以把它們放到統一的location中,通常情況下,爲了保護這些內部接口函數,都會把這些設置爲internal。可以讓內部接口相對獨立,不受外界干擾

如圖 30行。interal設置爲內部location,直接訪問報404。

其中的函數介紹:

   ngx.req.get_uri_args()函數爲回去請求url的參數。

   ngx.location.capture("/sum",{args={a=10,b=33}})這個函數是lua內部來調用nginx的其他location函數,第二參數表示入參吧

注意:

   ngx.sleep(0.1)  休眠0.1毫秒

   ngx.now()  獲取服務器當前時間;

   ngx.location.capture_multi({},{})  : 表示並行執行兩個請求,當兩個請求沒有依賴關係,這種方式可以極大的提高查詢效率。可以被廣泛應用於廣告系統,高併發前端展示(並行無依賴界面,降級開關等) 如上圖 58行。

  

2、nginx內部跳轉

ngx.exec() 純粹是內部跳轉並且沒有引入任何額外的HTTP信號。

alias 與root 的區別:

因爲root屬性指定的值是要加入到最終路徑的,所以訪問的位置變成了/var/lib/www/website/。而我不想把訪問的URI加入到路徑中。所以就需要使用alias屬性,其會拋棄URI,直接訪問alias指定的位置, 所以最終路徑變成/var/lib/www/

3、外部重定向

ngx.redirect():當請求http://10.23.148.149:9002/foo_rewrite會直接重定向到http://10.23.148.149:9002/foo,外部重定向是可以跨域名的

4、獲取url的參數

      ngx.req.get_uri_args、ngx.req.get_post_args一個獲取get請求的參數,一個是post請i去的參數

      

注意:在解析body post的參數之前, 一定要先讀取body,不然會報錯。

ngx.req.read_body():讀取body函數

5、傳遞請求uri的參數

      當獲取到了參數,自然是需要這些參數來完成業務邏輯。URI內容傳遞過程中是需要調用ngx.encode_args進行規則轉義

      也可以不使用ngx.encode_args,但是寫法上比較醜。

注意:對於使用ngx.location.capture,args參數是可以接受字符串或者lua表的。

6、獲取請求body

   在nnginx的典型應用場景中,幾乎都是讀取HTTP請求頭信息,例如負載均衡、反向代理等,對於API或者web 服務,獲取body信息在某些場景就是有必要的了。

未獲取到jack body 信息,注意:需要添加lua指令 lua_need_request_body on;默認讀取body信息。

添加後即可獲取到body信息。

大多數場景可以使用 ngx.req.read_body()  函數來獲取body 的post參數即可。不推薦使用lua_need_request_body指令

7、簡易防火牆、黑白名單的配置

其中的 ngx.var.remote_addr 表示獲取nginx的內置綁定變量。

8、防止SQL注入問題。可以使用resty.mysql的lua腳本庫來建立與mysql的鏈接

9、使用resty.http庫資源,函數完成了連接池、http請求。

9、redis的使用以及redis連接池

這是一個標準的redis接口調用,如果代碼調用頻率不高,沒有任何問題,如果重度依賴redis,且有大量的創建連接---數據操作--關閉連接,這就發現代碼中很多的重複。

local function close_redis(red)  
    if not red then  
        return  
    end  
    local ok, err = red:close()  
    if not ok then  
        ngx.say("close redis error : ", err)  
    end  
end  
  
local redis = require("resty.redis")  
  
--創建實例  
local red = redis:new()  
--設置超時(毫秒)  
red:set_timeout(1000)  
--建立連接  
local ip = "127.0.0.1"  
local port = 6660  
local ok, err = red:connect(ip, port)  
if not ok then  
    ngx.say("connect to redis error : ", err)  
    return close_redis(red)  
end  
--調用API進行處理  
ok, err = red:set("msg", "hello world")  
if not ok then  
    ngx.say("set msg error : ", err)  
    return close_redis(red)  
end  
  
--調用API獲取數據  
local resp, err = red:get("msg")  
if not resp then  
    ngx.say("get msg error : ", err)  
    return close_redis(red)  
end  
--得到的數據爲空處理  
if resp == ngx.null then  
    resp = ''  --比如默認值  
end  
ngx.say("msg : ", resp)  
  
close_redis(red)  

使用連接池來複用連接

建立TCP連接需要三次握手而釋放TCP連接需要四次握手,而這些往返時延僅需要一次,以後應該複用TCP連接,此時就可以考慮使用連接池,即連接池可以複用連接

我們只需要將之前的close_redis函數改造爲如下即可: 

local function close_redis(red)  
    if not red then  
        return  
    end  
    --釋放連接(連接池實現)  
    local pool_max_idle_time = 10000 --毫秒  
    local pool_size = 100 --連接池大小  
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)  
    if not ok then  
        ngx.say("set keepalive error : ", err)  
    end  
end  

即設置空閒連接超時時間防止連接一直佔用不釋放;設置連接池大小來複用連接。

此處假設調用red:set_keepalive(),連接池大小通過nginx.conf中http部分的如下指令定義:

#默認連接池大小,默認30

lua_socket_pool_size 30;

#默認超時時間,默認60s

lua_socket_keepalive_timeout 60s;

注意:

a、連接池是每Worker進程的,而不是每Server的;

b、當連接超過最大連接池大小時,會按照LRU算法回收空閒連接爲新連接使用;

c、連接池中的空閒連接出現異常時會自動被移除;

d、連接池是通過ip和port標識的,即相同的ip和port會使用同一個連接池(即使是不同類型的客戶端如Redis、Memcached);

e、連接池第一次set_keepalive時連接池大小就確定下了,不會再變更;

 

10、PipeLine壓縮請求數量

       當我們有連續多個命令需要發送給redis時,如果m每個命令都以一個數據包發送給redis,將會降低服務端的併發能力。因爲每發送一個TCP報文,會存在網絡延遲及操作系統的處理延遲。大部分情況下,網絡延遲要遠大於CPU的處理延遲,所以網絡延遲會成功系統性能的瓶頸,使得併發上不去。pipeline的機制是將多個命令匯聚到一個請求中,可以極大的減少請求數量減少網絡延時。在實際應用場景中,正確使用pipeline對性能的提升十分明顯。

11、script壓縮複雜請求

從pipeline中,我們知道對於多個簡單的redis命令可以匯聚到一個請求中,提升服務端的併發能力。然後,在有些場景下,我們每次命令的輸入需要引用上一個命令的結果作爲入參,甚至還需要對第一個結果進行一些加工,再把加工結果當成第二個命令的輸入。pipeline難以處理這樣的場景。但是可以使用redis裏面的script來壓縮這些複雜的命令。

其核心思想是redism命令裏嵌入lua腳本,來實現一些複雜操作。其腳本相關命令有:

eval、evalsha、script exists、 script flush、 script kill 、script load

這樣就可以將兩個get請求放到一個TCP請求中,做到減少TCP請求數據,減少網絡延時。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章