首頁 >後端開發 >php教程 >使用ngx_lua建立高並發應用

使用ngx_lua建立高並發應用

WBOY
WBOY原創
2016-07-30 13:31:381036瀏覽

這篇文章主要著重討論如何透過ngx_lua同後端的memcached、redis進行非阻塞通訊。

1. Memcached

        在Nginx中存取Memcached需要模組的支援,這裡選用HttpMemcModule,這個模組可以與後端的Memcached進行非阻塞的通訊。我們知道官方提供了Memcached,這個模組只支援get操作,而Memc支援大部分Memcached的指令。

        Memc模組以入口變數作為參數傳遞,所有以$memc_為前綴的變數都是Memc的入口變數。 memc_pass指向後端的Memcached Server。

  設定:

[plain] view plaincopyprint?

  1. #使用HttpMemcModule  
  2. location = /me {  
  3.     set $memc_key  $arg_key;  
  4.     set $memc_value $
  5.     set $memc_exptime $arg_exptime;  
  6.          11';  
  7. }  
  8.         輸出:
  9. [plain]  plaincopyprint?

$ curl  'http://localhost/memc?cmd=set&key=foo&val=Hello'  

calhost/memc?cmd=get&key =foo'  

    $ Hello  
  1.         這實現了memcached的訪問,請看一下如何在lua中訪問memcached。
  2.   設定:
  3. [plain] view plaincopyprint?
#Lua中訪問Memcached  

location = /memc 

    set $memc_cmd get;  

    set $memc_key  $ arg_key;  
  1.     memc_pass '127.0.0.1:11211';  
  2.     content_by_lua '  
  3.         local res = ngx.lo.capture("/cation  local res = ngx.lo.capture("/cation.capture("/cation. memc", {  
  4.             args = { key = ngx.var.
  5.         if res.status == 200 then  
  6.     
  7.         end  
  8.     ';  
  9. [plain] view plaincopyprint?
  10. $ curl  'http://localhost/lua_memc?key=foo'  
  11. $Hello 
  12. $Hello 
  13.         透過lua存取memcached,主要是透過子請求採用類似函數呼叫的方式來實現。首先,定義了一個memc location用於透過後端memcached通信,就相當於memcached storage。由於整個Memc模組時非阻塞的,ngx.location.capture也是非阻塞的,所以整個操作非阻塞。

    2. Redis

            訪問redis需要HttpRedis2Module的支持,它也可以與redis進行非阻塞通行。不過,redis2的回應是redis的原生回應,所以在lua中使用時,需要解析這個回應。可以採用LuaRedisModule,這個模組可以建構redis的原生請求,並解析redis的原生回應。

            設定:

    [plain] view plaincopyprint?

    1. #在Lua中訪問Redis  
    2. location = /redis {  
    3.     redis2_query get $arg_key;  
    4.     redis2_pass '127.0. 0.1:6379';  
    5. }   
    6. location = /lua_redis { # 
    7.         local parser = require("redis.parser")   ngx.location.capture("/redis", {  
    8.             args =   })  
    9.         if res.status == 200 then  
    10.  = parser.parse_reply(res.body)  
    11.             ngx.say(reply)  
    12.     ';  
    13. }  
    14.        輸出:
    15.       plaincopyprint?
    16. $ curl  'http://localhost/lua_redis?key=foo'  
    17. ,需要提供一個redis storage專門用於查詢redis,然後透過子請求去呼叫redis。
    18. 3. Redis Pipeline        實際存取redis時,有可能需要同時查詢多個key的狀況。我們可以採用ngx.location.capture_multi透過傳送多個子請求給redis storage,然後在解析回應內容。但是,這會有個限制,Nginx核心規定一次可以發起的子請求的個數不能超過50個,所以在key個數多於50時,這種方案不再適用。
    19.         幸好redis提供pipeline機制,可在一次連線中執行多個指令,這樣可以減少多次執行指令的往返延遲。在客戶端透過pipeline發送多個指令後,redis順序接收這些指令並執行,然後再依照順序把指令的結果輸出出去。在lua中使用pipeline需要用到redis2模組的redis2_raw_queries進行redis的原生請求查詢。         設定:
    [plain] view plaincopyprint?
    1. #在Lua中訪問Redis  
    2. location = /redis {  
    3.   
    4.     redis2_raw_queries $args $echo_request_body;  '127.0.0.1:6379';  
    5. }   
    6.           content_by_lua 'conf/pipeline.lua';  
    7. }   
    8.     
    9.     .lua
    10. [plain] view plaincopyprint?
    11. -- conf/pipeline.lua file  
    local parser = require('redis.parlocal parser  

        {'get', 'one' }, {'get', 'two'}   
    1. }  
    2. -- 構造原生的redis查詢,get onerngetcomq.
    3. for i, req in ipairs (reqs)  do  
    4.       table.insert(raw_reqs, parser.build_query(req))  .location.capture('/redis?'..#reqs, {身體 = table.concat(raw_reqs, '') })  
    5.       
    6. 🠎   -- 解析redis的原生回應  
    7.        local replies = parser. parse_replies(res.body, #reqs)  
    8.        for i, reply in ipairs(   ngx.say(reply[1])  
    9.        end  
    10. end  
    11. end  
    12. end  
    13. end  
    14. end  
    15. end         輸出:
    16. [plain] view plaincopyprint?
    17. $ curl  'http://localhost/pipeline'  
    $ 4. Connection Pool        前面造訪redis和memcached的例子中,在每次處理一個請求時,都會和後端的server建立連接,然後在請求處理完之後這個連接就會被釋放。這個過程中,會有3次握手、timewait等一些開銷,這對於高並發的應用是不可容忍的。這裡引入connection pool來消除這個開銷。

            連接池需要HttpUpstreamKeepaliveModule模組的支援。

            設定:
    1. [plain] view plaincopyprint?
      1. http {  
      2.     # 需要HttpUpstreamKeepaliveModule  
      3.     upstream redis_pool {  
      4.         server 127.0.0.1:6379;  
      5.         # 可以容納1024個連接的連接池  
      6.         keepalive 1024 single;  
      7.     }  
      8.              location = /redis {  
      9.               redis_pool;  
      10.         }  
      11.     } 
      12. }  
      13.         這個模組提供keepalive指令,它的context是upstream。我們知道upstream在使用Nginx做反向代理時使用,實際upstream是指“上游”,這個“上游”可以是redis、memcached或是mysql等一些server。 upstream可以定義一個虛擬server集群,而這些後端的server可以享受負載平衡。 keepalive 1024就是定義連接池的大小,當連接數超過這個大小後,後續的連接就會自動退化為短連接。連接池的使用很簡單,直接替換掉原來的ip和連接埠號碼即可。
      14.         有人曾經測過,在沒有使用連接池的情況下,訪問memcached(使用之前的Memc模組),rps為20000。在使用連線池之後,rps一路飆到140000。在實際情況下,這麼大的提升可能達不到,但基本上100-200%的提高還是可以的。
      15. 5. 小結
      16.         這裡對memcached、redis的訪問做個小結。         1. Nginx提供了強大的程式設計模型,location相當於函數,子請求相當於函數調用,並且location還可以向自己發送子請求,這樣構成一個遞歸的模型,所以採用這種模型實現複雜的業務邏輯。
              2. Nginx的IO操作必須是非阻塞的,如果Nginx在那阻著,則會大幅降低Nginx的效能。所以在Lua中必須透過ngx.location.capture發出子請求將這些IO操作委託給Nginx的事件模型。         3. 在需要使用tcp連接時,盡量使用連接池。這樣可以消除大量的建立、釋放連線的開銷。

      以上就介紹了使用ngx_lua建立高並發應用,包括了方面的內容,希望對PHP教程有興趣的朋友有所幫助。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn