使用ngx lua構建高併發應用(2)

2021-06-12 10:04:02 字數 4433 閱讀 1498

之前的文章中,已經介紹了ngx_lua的一些基本介紹,這篇文章主要著重討論一下如何通過ngx_lua同後端的memcached、redis進行非阻塞通訊。

1. memcached

在nginx中訪問memcached需要模組的支援,這裡選用httpmemcmodule,這個模組可以與後端的memcached進行非阻塞的通訊。我們知道官方提供了memcached,這個模組只支援get操作,而memc支援大部分memcached的命令。

memc模組採用入口變數作為引數進行傳遞,所有以$memc_為字首的變數都是memc的入口變數。memc_pass指向後端的memcached server。

配置:[plain]view plain

copy

print?

#使用httpmemcmodule  

location = /memc   

輸出:[plain]view plain

這就實現了memcached的訪問,下面看一下如何在lua中訪問memcached。

配置:[plain]view plain

copy

print?

#在lua中訪問memcached  

location = /memc   

location = /lua_memc   

})  

if res.status == 200 then  

ngx.say(res.body)  

end  

';  

}  

輸出:

通過lua訪問memcached,主要是通過子請求採用一種類似函式呼叫的方式實現。首先,定義了乙個memc location用於通過後端memcached通訊,就相當於memcached storage。由於整個memc模組時非阻塞的,ngx.location.capture也是非阻塞的,所以整個操作非阻塞。

2. redis

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

配置:[plain]view plain

copy

print?

#在lua中訪問redis  

location = /redis    

location = /lua_redis   

})  

if res.status == 200 then  

reply = parser.parse_reply(res.body)  

ngx.say(reply)  

end  

';  

}  

輸出:

和訪問memcached類似,需要提供乙個redis storage專門用於查詢redis,然後通過子請求去呼叫redis。

3. redis pipeline         在實際訪問redis時,有可能需要同時查詢多個key的情況。我們可以採用ngx.location.capture_multi通過傳送多個子請求給redis storage,然後在解析響應內容。但是,這會有個限制,nginx核心規定一次可以發起的子請求的個數不能超過50個,所以在key個數多於50時,這種方案不再適用。

幸好redis提供pipeline機制,可以在一次連線中執行多個命令,這樣可以減少多次執行命令的往返時延。客戶端在通過pipeline傳送多個命令後,redis順序接收這些命令並執行,然後按照順序把命令的結果輸出出去。在lua中使用pipeline需要用到redis2模組的redis2_raw_queries進行redis的原生請求查詢。

配置:[plain]view plain

copy

print?

#在lua中訪問redis  

location = /redis    

location = /pipeline    

pipeline.lua

[plain]view plain

copy

print?

-- conf/pipeline.lua file  

local parser = require(『redis.parser』)  

local reqs = ,    

}  -- 構造原生的redis查詢,get one\r\nget two\r\n  

local raw_reqs = {}  

for i, req in ipairs(reqs)  do  

table.insert(raw_reqs, parser.build_query(req))  

end  

local res = ngx.location.capture(『/redis?』..#reqs, )  

if res.status and res.body then  

-- 解析redis的原生響應  

local replies = parser.parse_replies(res.body, #reqs)  

for i, reply in ipairs(replies)  do   

ngx.say(reply[1])  

end  

end  

輸出:

前面訪問redis和memcached的例子中,在每次處理乙個請求時,都會和後端的server建立連線,然後在請求處理完之後這個連線就會被釋放。這個過程中,會有3次握手、timewait等一些開銷,這對於高併發的應用是不可容忍的。這裡引入connection pool來消除這個開銷。

連線池需要httpupstreamkeepalivemodule模組的支援。

配置:[plain]view plain

這個模組提供keepalive指令,它的context是upstream。我們知道upstream在使用nginx做反向**時使用,實際upstream是指「上游」,這個「上游」可以是redis、memcached或是mysql等一些server。upstream可以定義乙個虛擬server集群,並且這些後端的server可以享受負載均衡。keepalive 1024就是定義連線池的大小,當連線數超過這個大小後,後續的連線自動退化為短連線。連線池的使用很簡單,直接替換掉原來的ip和埠號即可。

有人曾經測過,在沒有使用連線池的情況下,訪問memcached(使用之前的memc模組),rps為20000。在使用連線池之後,rps一路飆到140000。在實際情況下,這麼大的提公升可能達不到,但是基本上100-200%的提高還是可以的。

5. 小結

這裡對memcached、redis的訪問做個小結。

1. nginx提供了強大的程式設計模型,location相當於函式,子請求相當於函式呼叫,並且location還可以向自己傳送子請求,這樣構成乙個遞迴的模型,所以採用這種模型實現複雜的業務邏輯。

2. nginx的io操作必須是非阻塞的,如果nginx在那阻著,則會大大降低nginx的效能。所以在lua中必須通過ngx.location.capture發出子請求將這些io操作委託給nginx的事件模型。

3. 在需要使用tcp連線時,盡量使用連線池。這樣可以消除大量的建立、釋放連線的開銷。

使用ngx lua構建高併發應用(2)

在之前的文章中,已經介紹了ngx lua的一些基本介紹,這篇文章主要著重討論一下如何通過ngx lua同後端的memcached redis進行非阻塞通訊。1.memcached 在nginx中訪問memcached需要模組的支援,這裡選用httpmemcmodule,這個模組可以與後端的memca...

使用ngx lua構建高併發應用(2)

這篇文章簡單介紹了一下ngx lua的基本用法,後一篇會對ngx lua訪問redis memcached已經連線池進行詳細介紹。在之前的文章 中,已經介紹了ngx lua的一些基本介紹,這篇文章主要著重討論一下如何通過ngx lua同後端的memcached redis進行非阻塞通訊。1.memc...

Ngx lua與go高併發效能對比

language lifeibo 1 comment nginx在處理高併發能力上非常出色,而go作為新時代網際網路語言,在設計之初就為實現高併發。ngx lua由nginx來處理網路事件,並使用協程來實現非阻塞,從而實現高併發。go語言級別提供非阻塞的api,同樣使用協程來提供高併發處理。我們來測...