淺談 OpenResty

一.前言

我們都知道Nginx有很多的特性和好處,但是在Nginx上開發成了一個難題,Nginx模塊需要用C開發,而且必須符合一系列複雜的規則,最重要的用C開發模塊必須要熟悉Nginx的源代碼,使得開發者對其望而生畏。爲了開發人員方便,所以接下來我們要介紹一種整合了Nginx和lua的框架,那就是OpenResty,它幫我們實現了可以用lua的規範開發,實現各種業務,並且幫我們弄清楚各個模塊的編譯順序。關於OpenResty,我想大家應該不再陌生,隨着系統架構的不斷升級、優化,OpenResty在被廣泛的應用。


二.OpenResty運行原理

Nginx 採用的是 master-worker 模型,一個 master 進程管理多個 worker 進程,基本的事件處理都是放在 woker 中,master 負責一些全局初始化,以及對 worker 的管理。在OpenResty中,每個 woker 使用一個 LuaVM,當請求被分配到 woker 時,將在這個 LuaVM 裏創建一個 coroutine(協程)。協程之間數據隔離,每個協程具有獨立的全局變量_G。

ps. 協程和多線程下的線程類似:有自己的堆棧,自己的局部變量,有自己的指令指針,但是和其他協程程序共享全局變量等信息。線程和協程的主要不同在於:多處理器的情況下,概念上來說多線程是同時運行多個線程,而協程是通過代碼來完成協程的切換,任何時刻只有一個協程程序在運行。並且這個在運行的協程只有明確被要求掛起時纔會被掛起。

原理圖如下:


三.OpenResty的優勢

首先我們選擇使用OpenResty,其是由Nginx核心加很多第三方模塊組成,其最大的亮點是默認集成了Lua開發環境,使得Nginx可以作爲一個Web Server使用。

藉助於Nginx的事件驅動模型和非阻塞IO,可以實現高性能的Web應用程序。

而且OpenResty提供了大量組件如Mysql、Redis、Memcached等等,使在Nginx上開發Web應用更方便更簡單。目前在京東如實時價格、秒殺、動態服務、單品頁、列表頁等都在使用Nginx+Lua架構,其他公司如淘寶、去哪兒網等。


四.Nginx和lua的簡介

1. Nginx:

(1) Nginx的優點

  • 輕量級同樣起web 服務比apache佔用更少內存及資源 

  • 抗併發nginx 處理請求異步非阻塞而apache 則阻塞型高併發下nginx 能保持低資源低消耗高性能 

  • 高度模塊化設計編寫模塊相對簡單 

  • 社區活躍各種高性能模塊出品迅速啊

(2) Nginx爲什麼性能高,佔用內存少

衆所周知,nginx性能高,而nginx的高性能與其架構是分不開的。在這裏,我們簡單粗略的介紹一下nginx的架構。

  • 首先,nginx採用的是多多進程模式,好處是什麼呢?首先,對於每個worker進程來說,獨立的進程,不需要加鎖,所以省掉了鎖帶來的開銷,同時在編程以及問題查找時,也會方便很多。其次,採用獨立的進程,可以讓互相之間不會影響,一個進程退出後,其它進程還在工作,服務不會中斷,master進程則很快啓動新的worker進程。當然,worker進程的異常退出,肯定是程序有bug了,異常退出,會導致當前worker上的所有請求失敗,不過不會影響到所有請求,所以降低風險。

  • Nginx是採用異步非阻塞的方式去處理請求的,什麼是異步非阻塞呢?其實就是當一個線程調用出現等待的io之類的情況時,而不是阻塞在這裏,而是去處理別的事情,等io準備好了,然後再去執行,具體的我就不在這裏和大家描述了。

2. lua:

(1) Lua 是一個小巧的腳本語言。作者是巴西人。該語言的設計目的是爲了嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能

(2) Lua的特點:

  • Lua腳本可以很容易的被C/C++代碼調用,也可以反過來調用C/C++的函數,這使得Lua在應用程序中可以被廣泛應用。不僅僅作爲擴展腳本,也可以作爲普通的配置文件,代替XML,Ini等文件格式,並且更容易理解和維護。

  • Lua由標準C編寫而成,代碼簡潔優美,幾乎在所有操作系統和平臺上都可以編譯,運行。一個完整的Lua解釋器不過200k,在目前所有腳本引擎中,Lua的速度是最快的。這一切都決定了Lua是作爲嵌入式腳本的最佳選擇。


五.OpenResty的安裝

上面我門對OpenResty進行了簡單的介紹,運行原理的說明,還有Nginx、Lua優點的介紹,是爲了更好的讓大家理解。讓大家知道用這個的好處,接下來我簡單的介紹一下OpenResty的安裝和搭建一個簡單的限流的例子,這是爲了讓大家明白,除了上面說的好處,它到底能夠做什麼。

OpenResty的安裝:

(1) 需要事先安裝一下所需的插件

1
yum install readline-devel pcre-devel openssl-devel

(2) 下載ngx_openresty-1.7.7.2.tar.gz並解壓 

1
wget http://openresty.org/download/ngx_openresty-1.7.7.2.tar.gz

(3) 安裝LuaJIT

1
2
3
cd bundle/LuaJIT-2.1-20150120/
make clean && make && make install
ln -sf luajit-2.1.0-alpha /usr/local/bin/luajit

(4) 下載ngx_cache_purge模塊,該模塊用於清理nginx緩存

1
wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz

(5) 下載nginx_upstream_check_module模塊,該模塊用於ustream健康檢查

1
wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/v0.3.0.tar.gz

(6) 安裝ngx_openresty

1
2
3
4
5
6
7
8
9
cd /usr/servers/ngx_openresty-1.7.7.2
./configure 
--prefix=/usr/servers 
--with-http_realip_module  
--with-pcre  
--with-luajit 
--add-module=./bundle/ngx_cache_purge-2.3/ 
--add-module=./bundle/nginx_upstream_check_module-0.3.0/ -j2
make && make install

(7) 到/usr/servers目錄下,會發現多出來瞭如下目錄,說明安裝成功

(8) 啓動nginx:/usr/servers/nginx/sbin/nginx(其實我們可以配置好系統的環境變量,配置好後就可以直接數據nginx命令了)


六.防刷(黑白名單)簡單例子的搭建

1. 首先我們先可以在/usr/servers/nginx/conf/ 目錄下創建我們自己的一個簡單的lua文件,就叫做example.lua好了。

輸入如下命令,我們就可以創建一個example.conf文件,或者我們可以cp nginx.conf  example.conf 或者mkdir example.conf

2. 接下來我們需要編輯nginx.conf這個啓動配置的文件了

ok,我們進入到編輯界面,接下來我們需要將我們的example.conf文件給引入進來,在nginx.conf的http體裏添加一下命令:include example.conf

nginx.conf 的具體內容如下:

其中大家可以看到用紅線框框的是自己項目文件的引入和lua模塊的引入。

3. 接下來我們可以在example.conf文件中寫我們的邏輯實現了

4. 我們可以看到,在example.conf中的server裏面配置多個location,而location中嵌入了content_by_lua_file  /usr/example/lua/redis_black_limit.lua,從這可以看出在content階段嵌入了lua腳本,進行了內容的響應,在這裏,我們進去看看相應的代碼是如何來處理的:

這是一個防刷的demo,從中我們可以看出,我是更具ip,從redis裏面取值,然後通過請求ip的匹配,來做到防刷的功能,除此之外,這裏有一個語句,我們的注意到,那就是require,這就相當於我們的類加載器,class.forname,從而做到加載進來lua的模塊。

5. 除此之外,還簡單的做了一個設置黑名單和取消黑名單的功能,用來充當我們以後的管理中心,具體邏輯寫在set_black.lua和cancel.lua中,然後在lua.conf中配置好url,具體如下圖所示:

在lua.conf中server體里加入一下代碼:

接下來我們需要寫set_black.lua和cancel.lua文件,文件內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
v  set_black.lua:
local redis =require "resty.redis"
local cache =redis.new()
cache:set_timeout(6000)
local ok,err=cache.connect(cache,'192.168.150.61',6379)
if not ok then
        ngx.say("failed to connect:",err)
        return
end
local ip = ngx.var.remote_addr
cache:set("user:"..ip..":block",1)
ngx.say("user:"..ip..":block".."設置黑名單成功")
1
2
3
4
5
6
7
8
9
10
11
12
v  cancel_black.lua:
local redis =require "resty.redis"
local cache =redis.new()
cache:set_timeout(6000)
local ok,err=cache.connect(cache,'192.168.150.61',6379)
if not ok then
        ngx.say("failed to connect:",err)
        return
end
local ip = ngx.var.remote_addr
cache:expire("user:"..ip..":block",0)
ngx.say("user:"..ip..":block".."移除黑名單")


7. 效果

做好了以上配置,我們可以開始先設置好黑名單,然後訪問請求,返回的結果是被黑名單了,然後再取消黑名單,再訪問請求,顯示訪問成功,具體結果如下圖。

七.總結

上文簡單的介紹一些OpenResty,以及搭建了一個簡單的應用,目的是想讓大家對這個框架有一個大體的瞭解。隨着架構的升級,我們會慢慢的把一些不太複雜的業務可以移到nginx層,從而提高我們的吞吐量,解決一些性能上的瓶頸。例如在nginx這一層做簡單的限流、黑白名單,緩存之類的業務複雜性不是太強的工作,從而增加我們的吞吐率,也可以再nginx這一層過濾掉一些垃圾流量,從而讓tomcat層只需要更加專注於業務。(以上是我簡單粗略的一些理解,如果有什麼問題,歡迎大家一起討論)


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