Nginx是一個高性能的Web 服務器,同時是一個高效的反向代理服務器,它仍是一個IMAP/POP3/SMTP 代理服務器。javascript
因爲Nginx採用的是事件驅動的架構,可以處理併發百萬級別的tcp鏈接,高度的模塊化設計和自由的BSD許可,使得Nginx有着很是豐富的第三方模塊。好比Openresty、API網關Kong。php
BSD開源協議是一個給予使用者很大自由的協議。基本上使用者能夠」隨心所欲」,能夠自由的使用,修改源代碼,也能夠將修改後的代碼做爲開源或者專有軟件再發布。css
Centos系統安裝,請參考這裏http://www.linuxidc.com/Linux/2016-09/134907.htm。先複製粘貼下它的文章。html
安裝 nginx 須要先將官網下載的源碼進行編譯,編譯依賴 gcc 環境,若是沒有 gcc 環境,則須要安裝:java
yum install gcc-c++node
PCRE(Perl Compatible Regular Expressions) 是一個Perl庫,包括 perl 兼容的正則表達式庫。nginx 的 http 模塊使用 pcre 來解析正則表達式,因此須要在 linux 上安裝 pcre 庫,pcre-devel 是使用 pcre 開發的一個二次開發庫。nginx也須要此庫。命令:mysql
yum install -y pcre pcre-devellinux
zlib 庫提供了不少種壓縮和解壓縮的方式, nginx 使用 zlib 對 http 包的內容進行 gzip ,因此須要在 Centos 上安裝 zlib 庫。nginx
yum install -y zlib zlib-develc++
OpenSSL 是一個強大的安全套接字層密碼庫,囊括主要的密碼算法、經常使用的密鑰和證書封裝管理功能及 SSL 協議,並提供豐富的應用程序供測試或其它目的使用。 nginx 不只支持 http 協議,還支持 https(即在ssl協議上傳輸http),因此須要在 Centos 安裝 OpenSSL 庫。
yum install -y openssl openssl-devel
1.直接下載.tar.gz安裝包,地址:https://nginx.org/en/download.html
2.使用wget命令下載(推薦)。
wget -c https://nginx.org/download/nginx-1.10.1.tar.gz
依然是直接命令:
tar -zxvf nginx-1.10.1.tar.gz cd nginx-1.10.1
其實在 nginx-1.10.1 版本中你就不須要去配置相關東西,默認就能夠了。固然,若是你要本身配置目錄也是能夠的。 使用默認配置
./configure
make make install
查找安裝路徑:
whereis nginx
Nginx的模塊從結構上分爲核心模塊、基礎模塊和第三方模塊:
Nginx的高併發得益於其採用了epoll模型,與傳統的服務器程序架構不一樣,epoll是linux內核2.6之後纔出現的。Nginx採用epoll模型,異步非阻塞,而Apache採用的是select模型。
nginx 環境變量配置:
export PATH=$PATH:/usr/servers/nginx/sbin
cat /var/run/nginx.pid
或者 nginx -s reload一般狀況下,Nginx的配置在Ngix的安裝目錄下的/conf/config.default 文件裏,基本配置以下:
worker_process # 表示工做進程的數量,通常設置爲cpu的核數 worker_connections # 表示每一個工做進程的最大鏈接數 server{} # 塊定義了虛擬主機 listen # 監聽端口 server_name # 監聽域名 location {} # 是用來爲匹配的 URI 進行配置,URI 即語法中的「/uri/」 location /{} # 匹配任何查詢,由於全部請求都以 / 開頭 root # 指定對應uri的資源查找路徑,這裏html爲相對路徑,完整路徑爲 # /opt/nginx-1.7.7/html/ index # 指定首頁index文件的名稱,能夠配置多個,以空格分開。若有多 # 個,按配置順序查找。
location 經常使用配置以下:
模式 | 含義 |
---|---|
location = /uri | = 表示精確匹配,只有徹底匹配上才能生效 |
location ^~ /uri | ^~ 開頭對URL路徑進行前綴匹配,而且在正則以前。 |
location ~ pattern | 開頭表示區分大小寫的正則匹配 |
location ~* pattern | 開頭表示不區分大小寫的正則匹配 |
location /uri | 不帶任何修飾符,也表示前綴匹配,可是在正則匹配以後 |
location / | 通用匹配,任何未匹配到其它location的請求都會匹配到,至關於switch中的default |
Nginx的經常使用配置很是多,如下內容摘自於布爾教育課件,僅供參考:
#定義Nginx運行的用戶和用戶組 user www www; #啓動進程,一般設置成和cpu的數量相等 worker_processes 8; worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000; #爲每一個進程分配cpu,上例中將8個進程分配到8個cpu,固然能夠寫多個,或者將一個進程分配到多個cpu。 worker_rlimit_nofile 102400; #這個指令是指當一個nginx進程打開的最多文件描述符數目,理論值應該是最多打 #開文件數(ulimit -n)與nginx進程數相除,可是nginx分配請求並非那麼均勻 #,因此最好與ulimit -n的值保持一致。 #全局錯誤日誌及PID文件 error_log /usr/local/nginx/logs/error.log; #錯誤日誌定義等級,[ debug | info | notice | warn | error | crit ] pid /usr/local/nginx/nginx.pid; #一個nginx進程打開的最多文件描述符數目,理論值應該是最多打開文件數(系統的值ulimit -n)與nginx進程數相除,可是nginx分配請求並不均勻. #因此建議與ulimit -n的值保持一致。 worker_rlimit_nofile 65535; #工做模式及鏈接數上限 events { use epoll; #epoll是多路複用IO(I/O Multiplexing)中的一種方式,可是僅用於linux2.6以上內核,能夠大大提升nginx的性能 worker_connections 102400; #單個後臺worker process進程的最大併發連接數 (最大鏈接數=鏈接數*進程數) multi_accept on; #儘量多的接受請求 } #設定http服務器,利用它的反向代理功能提供負載均衡支持 http { #設定mime類型,類型由mime.type文件定義 include mime.types; default_type application/octet-stream; #設定日誌格式 access_log /usr/local/nginx/log/nginx/access.log; sendfile on; #sendfile 指令指定 nginx 是否調用 sendfile 函數(zero copy 方式)來輸出文件,對於普通應用必須設爲 on #若是用來進行下載等應用磁盤IO重負載應用,可設置爲 off,以平衡磁盤與網絡I/O處理速度,下降系統的uptime. #autoindex on; #開啓目錄列表訪問,合適下載服務器,默認關閉。 tcp_nopush on; #防止網絡阻塞 keepalive_timeout 60; #keepalive超時時間,客戶端到服務器端的鏈接持續有效時間,當出現對服務器的後,繼請求時,keepalive-timeout功能可避免創建或從新創建鏈接。 tcp_nodelay on; #提升數據的實時響應性 #開啓gzip壓縮 gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 2; #壓縮級別大小,最大爲9,值越小,壓縮後比例越小,CPU處理更快。 #值越大,消耗CPU比較高。 gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; client_max_body_size 10m; #容許客戶端請求的最大單文件字節數 client_body_buffer_size 128k; #緩衝區代理緩衝用戶端請求的最大字節數, proxy_connect_timeout 90; #nginx跟後端服務器鏈接超時時間(代理鏈接超時) proxy_send_timeout 90; #後端服務器數據回傳時間(代理髮送超時) proxy_read_timeout 90; #鏈接成功後,後端服務器響應時間(代理接收超時) proxy_buffer_size 4k; #設置代理服務器(nginx)保存用戶頭信息的緩衝區大小 proxy_buffers 4 32k; #proxy_buffers緩衝區,網頁平均在32k如下的話,這樣設置 proxy_busy_buffers_size 64k; #高負荷下緩衝大小(proxy_buffers*2) #設定請求緩衝 large_client_header_buffers 4 4k; client_header_buffer_size 4k; #客戶端請求頭部的緩衝區大小,這個能夠根據你的系統分頁大小來設置,通常一個請求的頭部大小不會超過1k #不過因爲通常系統分頁都要大於1k,因此這裏設置爲分頁大小。分頁大小能夠用命令getconf PAGESIZE取得。 open_file_cache max=102400 inactive=20s; #這個將爲打開文件指定緩存,默認是沒有啓用的,max指定緩存數量,建議和打開文件數一致,inactive是指通過多長時間文件沒被請求後刪除緩存。 open_file_cache_valid 30s; #這個是指多長時間檢查一次緩存的有效信息。 open_file_cache_min_uses 1; #open_file_cache指令中的inactive參數時間內文件的最少使用次數,若是超過這個數字,文件描述符一直是在緩存中打開的,如上例,若是有一個文件在inactive #包含其它配置文件,如自定義的虛擬主機 include vhosts.conf; }
配置詳解2以下:
#這裏爲後端服務器wugk應用集羣配置,根據後端實際狀況修改便可,tdt_wugk爲負載均衡名稱,能夠任意指定 #但必須跟vhosts.conf虛擬主機的pass段一致,不然不能轉發後端的請求。weight配置權重,在fail_timeout內檢查max_fails次數,失敗則剔除均衡。 upstream tdt_wugk { server 127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=30s; server 127.0.0.1:8081 weight=1 max_fails=2 fail_timeout=30s; } #虛擬主機配置 server { #偵聽80端口 listen 80; #定義使用www.wuguangke.cn訪問 server_name www.wuguangke.cn; #設定本虛擬主機的訪問日誌 access_log logs/access.log main; root /data/webapps/wugk; #定義服務器的默認網站根目錄位置 index index.php index.html index.htm; #定義首頁索引文件的名稱 #默認請求 location ~ /{ root /data/www/wugk; #定義服務器的默認網站根目錄位置 index index.php index.html index.htm; #定義首頁索引文件的名稱 #如下是一些反向代理的配置. proxy_next_upstream http_502 http_504 error timeout invalid_header; #若是後端的服務器返回50二、50四、執行超時等錯誤,自動將請求轉發到upstream負載均衡池中的另外一臺服務器,實現故障轉移。 proxy_redirect off; #後端的Web服務器能夠經過X-Forwarded-For獲取用戶真實IP proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://tdt_wugk; #請求轉向後端定義的均衡模塊 } # 定義錯誤提示頁面 error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } #配置Nginx動靜分離,定義的靜態頁面直接從Nginx發佈目錄讀取。 location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$ { root /data/www/wugk; #expires定義用戶瀏覽器緩存的時間爲3天,若是靜態頁面不常更新,能夠設置更長,這樣能夠節省帶寬和緩解服務器的壓力。 expires 3d; } #PHP腳本請求所有轉發到 FastCGI處理. 使用FastCGI默認配置. location ~ \.php$ { root /root; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /data/www/wugk$fastcgi_script_name; include fastcgi_params; } #設定查看Nginx狀態的地址 location /NginxStatus { stub_status on; } } }
名稱 | 說明 |
---|---|
$arg_name | 請求中的name參數 |
$args | 請求中的參數 |
$binary_remote_addr | 遠程地址的二進制表示 |
$body_bytes_sent | 已發送的消息體字節數 |
$content_length HTTP | 請求信息裏的」Content-Length」 |
$content_type | 請求信息裏的」Content-Type」 |
$document_root | 針對當前請求的根路徑設置值 |
\(document_uri | 與\)uri相同; 好比 /test2/test.php | |
$host | 請求信息中的」Host」,若是請求中沒有Host行,則等於設置的服務器名 |
$hostname | 機器名使用 gethostname系統調用的值 |
$http_cookie | cookie 信息 |
$http_referer | 引用地址 |
$http_user_agent | 客戶端代理信息 |
$http_via | 最後一個訪問服務器的Ip地址。 |
$http_x_forwarded_for | 至關於網絡訪問路徑 |
$is_args | 若是請求行帶有參數,返回「?」,不然返回空字符串 |
$limit_rate | 對鏈接速率的限制 |
$nginx_version | 當前運行的nginx版本號 |
$pid worker | 進程的PID |
\(query_string | 與\)args相同 | |
\(realpath_root | 按root指令或alias指令算出的當前請求的絕對路徑。其中的符號連接都會解析成真是文件路徑,使用 Nginx 內置綁定變量 | | 207\)remote_addr | 客戶端IP地址 |
$remote_port | 客戶端端口號 |
$remote_user | 客戶端用戶名,認證用 |
$request | 用戶請求 |
$request_body | 這個變量(0.7.58+) 包含請求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比較有意義 |
$request_body_file | 客戶端請求主體信息的臨時文件名 |
$request_completion | 若是請求成功,設爲」OK」;若是請求未完成或者不是一系列請求中最後一部分則設爲空 |
$request_filename | 當前請求的文件路徑名,好比/opt/nginx/www/test.php |
$request_method | 請求的方法,好比」GET」、」POST」等 |
$request_uri | 請求的URI,帶參數 |
$scheme | 所用的協議,好比http或者是https |
$server_addr | 服務器地址,若是沒有用listen指明服務器地址,使用這個變量將發起一次系統調用以取得地址(形成資源浪費) |
$server_name | 請求到達的服務器名 |
$server_port | 請求到達的服務器端口號 |
$server_protocol | 請求的協議版本,」HTTP/1.0」或」HTTP/1.1」 |
$uri | 請求的URI,可能和最初的值有不一樣,好比通過重定向之類的 |
Lua 是一種輕量小巧的腳本語言,用標準C語言編寫並以源代碼形式開放, 其設計目的是爲了嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。
Lua 是巴西里約熱內盧天主教大學(Pontifical Catholic University of Rio de Janeiro)裏的一個研究小組,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所組成並於1993年開發。 —摘抄 http://www.runoob.com/lua/lua-tutorial.html
注意: 在上一篇文章中,OpenResty已經有了Lua的環境,這裏安裝的是單獨的Lua環境,用於學習和開發Lua。大多數的電腦是Windowds版本的電腦,Windows版本下載地址http://luaforge.net/projects/luaforwindows/。
Linux和Mac電腦下載地址:http://luajit.org/download.html,安裝命令以下:
wget http://luajit.org/download/LuaJIT-2.1.0-beta1.tar.gz tar -xvf LuaJIT-2.1.0-beta1.tar.gz cd LuaJIT-2.1.0-beta1 make sudo make install
使用IDEA開發的同窗,能夠經過安裝插件的形式來集成Lua的環境,插件名爲EmmyLua,安裝插件後,在Idea的右側欄就會出現Lua的圖標,點擊圖標,就會出現運行Lua代碼的窗口。建議使用該插件,能夠免去安裝Lua環境的麻煩。
安裝好環境後,我採用EmmyLua插件的形式,對Lua的入門語法進行一個簡單的講解。 打開EmmyLua的終端,在終端上輸入:
print("hi you")
按ctrl+enter,終端顯示:
hi you
lua的基本數據類型有nil、string、boolean、number、function類型。
nil相似於Java中的null ,表示空值。變量第一次賦值爲nil。
local num print(num) num=100 print(num)
終端輸出:
nil
100
Number 類型用於表示實數,和 Java裏面的 double 類型很相似。可使用數學函數 math.floor(向下取整) 和 math.ceil(向上取整) 進行取整操做。
local order = 3.99 local score = 98.01 print(math.floor(order)) print(math.ceil(score))
輸出:
3
99
Lua 中有三種方式表示字符串: 一、使用一對匹配的單引號。例:’hello’。 二、使用一對匹配的雙引號。例:」abclua 3.字符串還能夠用一種長括號(即[[ ]]) 括起來的方式定義
ocal str1 = 'hello world' local str2 = "hello lua" local str3 = [["add\name",'hello']] local str4 = [=[string have a [[]].]=] print(str1) -->output:hello world print(str2) -->output:hello lua print(str3) -->output:"add\name",'hello' print(str4) --
Table 類型實現了一種抽象的「關聯數組」。「關聯數組」是一種具備特殊索引方式的數組,索引一般是字符串(string) 或者 number 類型,但也能夠是除 nil 之外的任意類型的值。
local corp = { web = "www.google.com", --索引爲字符串,key = "web", -- value = "www.google.com" telephone = "12345678", --索引爲字符串 staff = {"Jack", "Scott", "Gary"}, --索引爲字符串,值也是一個表 100876, --至關於 [1] = 100876,此時索引爲數字 -- key = 1, value = 100876 100191, --至關於 [2] = 100191,此時索引爲數字 [10] = 360, --直接把數字索引給出 ["city"] = "Beijing" --索引爲字符串 } print(corp.web) -->output:www.google.com print(corp["telephone"]) -->output:12345678 print(corp[2]) -->output:100191 print(corp["city"]) -->output:"Beijing" print(corp.staff[1]) -->output:Jack print(corp[10]) -->output:36
在 Lua 中,函數 也是一種數據類型,函數能夠存儲在變量中,能夠經過參數傳遞給其餘函 數,還能夠做爲其餘函數的返回值。
local function foo() print("in the function") --dosomething() local x = 10 local y = 20 return x + y end local a = foo --把函數賦給變量 print(a()) --output: in the function 30
~= 不等於
邏輯運算符 | 說明 |
---|---|
and | 邏輯與 |
or | 邏輯或 |
not | 邏輯非 |
local c = nil local d = 0 local e = 100 print(c and d) -->打印 nil print(c and e) -->打印 nil print(d and e) -->打印 100 print(c or d) -->打印 0 print(c or e) -->打印 100 print(not c) -->打印 true print(not d) --> 打印 false
在 Lua 中鏈接兩個字符串,可使用操做符「..」(兩個點).
print("Hello " .. "World") -->打印 Hello World print(0 .. 1) -->打印 01
單個 if 分支 型
x = 10 if x > 0 then print("x is a positive number") end
兩個分支 if-else 型
x = 10 if x > 0 then print("x is a positive number") else print("x is a non-positive number") end
多個分支 if-elseif-else 型:
score = 90 if score == 100 then print("Very good!Your score is 100") elseif score >= 60 then print("Congratulations, you have passed it,your score greater or equal to 60") --此處能夠添加多個elseif else print("Sorry, you do not pass the exam! ") end
Lua 提供了一組傳統的、小巧的控制結構,包括用於條件判斷的 if 用於迭代的 while、repeat 和 for,本章節主要介紹 for 的使用.
for 語句有兩種形式:數字 for(numeric for) 和範型 for(generic for) 。 數字型 for 的語法以下:
for var = begin, finish, step do --body end
實例1:
for i = 1, 5 do print(i) end -- output: 1 2 3 4 5
實例2:
for i = 1, 10, 2 do print(i) end -- output: 1 3 5 7 9
泛型 for 循環經過一個迭代器(iterator) 函數來遍歷全部值:
-- 打印數組a的全部值 local a = {"a", "b", "c", "d"} for i, v in ipairs(a) do print("index:", i, " value:", v) end -- output: index: 1 value: a index: 2 value: b index: 3 value: c index: 4 value: d
lua的入門就到這裏,由於lua語法雖少,但細節有不少,不可能花不少時間去研究這個。入個門,遇到問題再去查資料就好了。另外須要說明的是本文大部份內容爲複製粘貼於OPenResty 最佳實踐,感謝原做者的開源電子書,讓我獲益匪淺。更多內容請參考:
lua入門教程:http://www.runoob.com/lua/lua-tutorial.html
OPenResty 最佳實踐: https://moonbingbing.gitbooks.io/openresty-best-practices/content/index.html
個人服務器爲一臺全新的centos 7的服務器,因此從頭安裝openresty,並記錄了安裝過程當中出現的問題,以及解決辦法。
cd /usr mkdir servers mkdir downloads yum install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl cd /usr/servers wget https://openresty.org/download/openresty-1.11.2.4.tar.gz tar -zxvf openresty-1.11.2.4.tar.gz cd /usr/servers/bunble/LuaJIT-2.1-20170405 安裝Lua make clean && make && make install
安裝過程當中出現如下的錯誤:
gcc: Command not found
yum -y install gcc automake autoconf libtool make
make clean && make && make install ln -sf luajit-2.1.0-alpha /usr/local/bin/luajit
cd /usr/servers/ngx_openresty–1.11.2.4/bundle
wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz
tar -xvf 2.3.tar.gz
cd /usr/servers/ngx_openresty-1.11.2.4/bundle
wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/v0.3.0.tar.gz
tar -xvf v0.3.0.tar.gz
cd /usr/servers/ngx_openresty-1.11.2.4 ./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
提示錯誤,安裝pcre庫
yum install -y pcre pcre-devel
<1> gcc 安裝 安裝 nginx 須要先將官網下載的源碼進行編譯,編譯依賴 gcc 環境,若是沒有 gcc 環境,則須要安裝:
yum install gcc-c++
<2> PCRE pcre-devel 安裝
PCRE(Perl Compatible Regular Expressions) 是一個Perl庫,包括 perl 兼容的正則表達式庫。nginx 的 http 模塊使用 pcre 來解析正則表達式,因此須要在 linux 上安裝 pcre 庫,pcre-devel 是使用 pcre 開發的一個二次開發庫。nginx也須要此庫。命令:
yum install -y pcre pcre-devel
<3> zlib 安裝 zlib 庫提供了不少種壓縮和解壓縮的方式, nginx 使用 zlib 對 http 包的內容進行 gzip ,因此須要在 Centos 上安裝 zlib 庫。
yum install -y zlib zlib-devel
<4> OpenSSL 安裝 OpenSSL 是一個強大的安全套接字層密碼庫,囊括主要的密碼算法、經常使用的密鑰和證書封裝管理功能及 SSL 協議,並提供豐富的應用程序供測試或其它目的使用。 nginx 不只支持 http 協議,還支持 https(即在ssl協議上傳輸http),因此須要在 Centos 安裝 OpenSSL 庫。
yum install -y openssl openssl-devel
<5>.從新安裝OpenResty
cd /usr/servers/ngx_openresty-1.11.2.4 ./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
<6>.啓動Nginx
/usr/servers/nginx/sbin/nginx
瀏覽器訪問http://116.196.177.123:
Welcome to OpenResty! If you see this page, the OpenResty web platform is successfully installed and working. Further configuration is required. For online documentation and support please refer to openresty.org. Thank you for flying OpenResty.
安裝成功了。
vim /usr/servers/nginx/conf/nginx.conf
錯誤提示沒有安裝vim
yum -y install vim*
一、在http部分添加以下配置
lua模塊路徑,多個之間」;」分隔,其中」;;」表示默認搜索路徑,默認到/usr/servers/nginx下找
lua_package_path 「/usr/servers/lualib/?.lua;;」; #lua 模塊
lua_package_cpath 「/usr/servers/lualib/?.so;;」; #c模塊
二、在nginx.conf中的http部分添加include lua.conf包含此文件片斷 Java代碼 收藏代碼 include lua.conf;
在/usr/server/nginx/conf下
vim lua.conf
#lua.conf server { listen 80; server_name _; location /lua { default_type 'text/html'; content_by_lua 'ngx.say("hello world")'; } }
vim /etc/profile
JAVA_HOME=/usr/local/jdk/jdk1.8.0_144 JRE_HOME=$JAVA_HOME/jre PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin CLASSPATH=:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib/dt.jar export JAVA_HOME JRE_HOME PATH CLASSPATH export PATH=$PATH:/usr/servers/nginx/sbin
source /etc/profile
測試:
nginx -t
nginx: the configuration file /usr/servers/nginx/conf/nginx.conf syntax is ok nginx: configuration file /usr/servers/nginx/conf/nginx.conf test is successful
nginx -s reload
瀏覽器訪問http://116.196.177.123/lua ,瀏覽器顯示:
hello world
mkdir /usr/example cp -r /usr/servers/lualib/ /usr/example/ mkdir /usr/example/lua
cd /usr/example vim example.conf
server { listen 80; server_name _; location /lua { default_type 'text/html'; lua_code_cache off; content_by_lua_file /usr/example/lua/test.lua; } }
vim /usr/example/lua/test.lua
ngx.say("hello world");
cd /usr/servers/nginx/conf/
vim nginx.conf
http模塊:
http { include mime.types; default_type application/octet-stream; lua_package_path "/usr/example/lualib/?.lua;;"; #lua 模塊 lua_package_cpath "/usr/example/lualib/?.so;;"; #c模塊 include /usr/example/example.conf; .... .... }
nginx -t
nginx: [alert] lua_code_cache is off; this will hurt performance in /usr/example/example.conf:7 nginx: the configuration file /usr/servers/nginx/conf/nginx.conf syntax is ok nginx: configuration file /usr/servers/nginx/conf/nginx.conf test is successful
nginx -s reload
瀏覽器訪問http://116.196.177.123/lua ,
hello world
導出history的全部命令:
在你的帳戶目錄下 輸入命令 ls -a 找到 .bash_history 這個就是記錄命令文件。 輸入命令: cat .bash_history >> history.txt
http://www.linuxidc.com/Linux/2016-09/134907.htm
http://jinnianshilongnian.iteye.com/blog/2186270
https://openresty.org/en/
這篇文章主要講解OpenResty常見的api。
vim /usr/example/example.conf
location /lua_var { default_type 'text/plain'; content_by_lua_block { ngx.say(ngx.var.arg_a) } }
從新加載nginx配置文件: nginx -s reload
在瀏覽器上訪問http://116.196.177.123/lua_var?a=323,瀏覽器顯示:
323
在上述代碼中,涉及到了2個api, 一是ngx.say(直接返回請求結果);二是ngx.var,它是獲取請求的參數,好比本例子上的?a=323,獲取以後,直接輸出爲請求結果。
vim /usr/example/example.conf
location /lua_request{ default_type 'text/html'; lua_code_cache off; content_by_lua_file /usr/example/lua/lua_request.lua; }
vim /usr/example/lua/lua_request.lua ,添加一下代碼:
local arg = ngx.req.get_uri_args() for k,v in pairs(arg) do ngx.say("[GET ] key:", k, " v:", v) end ngx.req.read_body() -- 解析 body 參數以前必定要先讀取 body local arg = ngx.req.get_post_args() for k,v in pairs(arg) do ngx.say("[POST] key:", k, " v:", v) end
在上述例子中有如下的api:
使用curl模擬請求:
curl ‘http://116.196.177.123/lua_request?a=323&b=ss’ -d ‘c=12w&d=2se3’
返回的結果:
[GET ] key:b v:ss [GET ] key:a v:323 [POST] key:d v:2se3 [POST] key:c v:12w
vim /usr/example/lua/lua_request.lua ,在原有的代碼基礎上,再添加一下代碼:
local headers = ngx.req.get_headers() ngx.say("headers begin", "<br/>") ngx.say("Host : ", headers["Host"], "<br/>") ngx.say("user-agent : ", headers["user-agent"], "<br/>") ngx.say("user-agent : ", headers.user_agent, "<br/>") for k,v in pairs(headers) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ","), "<br/>") else ngx.say(k, " : ", v, "<br/>") end end
從新加載nginx -s reload
使用curl模擬請求:
curl ‘http://116.196.177.123/lua_request?a=323&b=ss’ -d ‘c=12w&d=2se3’
[GET ] key:b v:ss [GET ] key:a v:323 [POST] key:d v:2se3 [POST] key:c v:12w headers begin<br/> Host : 116.196.77.157<br/> user-agent : curl/7.53.0<br/> user-agent : curl/7.53.0<br/> host : 116.196.77.157<br/> content-type : application/x-www-form-urlencoded<br/> accept : */*<br/> content-length : 12<br/> user-agent : curl/7.53.0<br/>
vim /usr/example/lua/lua_request.lua ,在原有的代碼基礎上,再添加一下代碼:
ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "<br/>") --請求方法 ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "<br/>") --原始的請求頭內容 ngx.say("ngx.req.raw_header : ", ngx.req.raw_header(), "<br/>") --請求的body內容體 ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "<br/>") ngx.say("<br/>")
從新加載nginx -s reload
使用curl模擬請求:
curl ‘http://116.196.177.123/lua_request?a=323&b=ss’ -d ‘c=12w&d=2se3’
//.... ngx.req.http_version : 1.1<br/> ngx.req.get_method : POST<br/> ngx.req.raw_header : POST /lua_request?a=323&b=ss HTTP/1.1 Host: 116.196.77.157 User-Agent: curl/7.53.0 Accept: */* Content-Length: 12
vim /usr/example/example.conf,添加一個location,代碼以下:
location /lua_response{ default_type 'text/html'; lua_code_cache off; content_by_lua_file /usr/example/lua/lua_response.lua ; }
vim /usr/example/lua/lua_response.lua 添加一下代碼:
ngx.header.a="1" ngx.header.b={"a","b"} ngx.say("hello","</br>") ngx.print("sss") return ngx.exit(200)
上述代碼中有如下api:
使用curl模擬請求, curl ‘http://116.196.177.123/lua_response’ ,獲取的響應體以下:
hello sss
在配置文件vim /usr/example/example.conf 加上如下代碼:
location /lua_log{ default_type 'text/html'; lua_code_cache off; content_by_lua_file /usr/example/lua/lua_log.lua; }
vim /usr/example/lua/lua_log.lua ,加上如下代碼:
local log="i'm log" local num =10 ngx.log(ngx.ERR, "log",log) ngx.log(ngx.INFO,"num:" ,num)
從新加載配置文件nginx -s reload
curl ‘http://116.196.177.123/lua_log’
打開nginx 的logs目錄下的error.log 文件:
tail -fn 1000 /usr/servers/nginx/logs/error.log
能夠看到在日誌文件中已經輸出了日誌,這種日誌主要用於記錄和測試。
日誌級別:
vim /usr/example/example.conf 添加如下代碼:
location /lua_sum{ # 只容許內部調用 internal; # 這裏作了一個求和運算只是一個例子,能夠在這裏完成一些數據庫、 # 緩存服務器的操做,達到基礎模塊和業務邏輯分離目的 content_by_lua_block { local args = ngx.req.get_uri_args() ngx.say(tonumber(args.a) + tonumber(args.b)) } }
internal 關鍵字,表示只容許內部調用。使用curl模擬請求,請求命令以下:
$ curl ‘http://116.196.177.123/lua_sum?a=1&b=2’
因爲該loction是一個內部調用的,外部不能返回,最終返回的結果爲404,以下:
<html> <head><title>404 Not Found</title></head> <body bgcolor="white"> <center><h1>404 Not Found</h1></center> <hr><center>openresty/1.11.2.4</center> </body> </html>
vim /usr/example/example.conf 添加如下代碼:
location = /lua_sum_test { content_by_lua_block { local res = ngx.location.capture("/lua_sum", {args={a=3, b=8}}) ngx.say("status:", res.status, " response:", res.body) } }
上述的代碼經過ngx.location.capture去調用內部的location,並得到返回結果,最終將結果輸出,採用curl模擬請求:
$ curl ‘http://116.196.177.123/lua_sum_test’
返回結果以下:
status:200 response:11
vim /usr
location /lua_redirect{ default_type 'text/html'; content_by_lua_file /usr/example/lua/lua_redirect.lua; } ngx.redirect("http://www.fangzhipeng.com", 302)
http://116.196.177.123/lua_redirect
vim /usr/servers/nginx/cong/nginx.conf
在http模塊加上如下:
lua_shared_dict shared_data 1m; location /lua_shared_dict{ default_type 'text/html'; content_by_lua_file /usr/example/lua/lua_shared_dict.lua; } local shared_data = ngx.shared.shared_data local i = shared_data:get("i") if not i then i = 1 shared_data:set("i",i) end i = shared_data:incr("i",1) ngx.say("i:",i)
屢次訪問 http://116.196.177.123/lua_shared_dict,瀏覽器打印:
i:1 i:2 i:3 i:4 i:5
如下內容來自於《openresty 最佳實踐》
如上圖所示,openresty的執行階段分爲
這樣咱們就能夠根據咱們的須要,在不一樣的階段直接完成大部分典型處理了。
執行階段概念:
Openresty沒有提供默認的Http客戶端,須要下載第三方的http客戶端。
下載lua-resty-http到lualib目錄下,使用如下的命令下載:
cd /usr/example/lualib/resty/ wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua
lua-resty-http模塊的地址爲https://github.com/pintsized/lua-resty-http
安裝成功後,經過require(「resty.http」)引入 lua_http模塊,它有如下的api方法:
vim /usr/example/lua/test_http.lua,寫如下代碼:
local http = require("resty.http") local httpc = http.new() local resp, err = httpc:request_uri("http://s.taobao.com", { method = "GET", path = "/search?q=hello", headers = { ["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36" } }) if not resp then ngx.say("request error :", err) return end ngx.status = resp.status for k, v in pairs(resp.headers) do if k ~= "Transfer-Encoding" and k ~= "Connection" then ngx.header[k] = v end end ngx.say(resp.body) httpc:close()
vim /usr/example/example.conf 加上如下的配置:
location /lua_http { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_http.lua; }
在Nginx的配置文件nginx.conf的http部分,加上如下dns解析:
vim /usr/servers/nginx/conf/nginx.conf
resolver 8.8.8.8;
瀏覽器訪問:http://116.196.177.123/lua_http,瀏覽器會顯示淘寶的搜索頁。
Json是一種常見的數據交換格式,經常使用於http通訊協議和其餘數據傳輸領域。在openresty默認內嵌了lua_cjson模塊,用來序列化數據。
lua_cjson模塊的地址:https://www.kyne.com.au/~mark/software/lua-cjson-manual.html
它經常使用的API以下:
vim /usr/example/lua/test_cjson.lua,添加如下內容:
local cjson = require("cjson") local obj = { id = 1, name = "zhangsan", age = nil, is_male = false, hobby = {"film", "music", "read"} } local str = cjson.encode(obj) ngx.say(str, "<br/>") str = '{"hobby":["film","music","read"],"is_male":false,"name":"zhangsan","id":1,"age":null}' local obj = cjson.decode(str) ngx.say(obj.age, "<br/>") ngx.say(obj.age == nil, "<br/>") ngx.say(obj.age == cjson.null, "<br/>") ngx.say(obj.hobby[1], "<br/>")
vim /usr/example/example.conf添加如下內容:
location ~ /lua_cjson { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_cjson.lua; }
在瀏覽器上訪問http://116.196.177.123/lua_cjson,瀏覽器顯示如下內容:
{"hobby":["film","music","read"],"is_male":false,"name":"zhangsan","id":1} null false true film
Centos系統下安裝mysql,先下載mysql-community-release-el7-5.noarch.rpm,而後經過yum安裝,安裝過程一直肯定【Y】便可。
cd /usr/downloads/ wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm rpm -ivh mysql-community-release-el7-5.noarch.rpm yum install mysql-community-server
安裝成功後,重啓mysql,並進入mysql數據庫,給root用戶設置一個密碼,密碼爲「123」。
service mysqld restart mysql -u root -p set password for root@localhost = password('123');
lua-resty-mysql模塊的官方文檔地址: https://github.com/openresty/lua-resty-mysql
lua-resty-mysql - Lua MySQL client driver for ngx_lua based on the cosocket API
lua-resty-mysql模塊是基於cosocket API 爲ngx_lua提供的一個Lua MySQL客戶端。它保證了100%非阻塞。
vim /usr/example/lua/test_mysql.lua,添加如下的代碼:
local function close_db(db) if not db then return end db:close() end local mysql = require("resty.mysql") local db, err = mysql:new() if not db then ngx.say("new mysql error : ", err) return end db:set_timeout(1000) local props = { host = "127.0.0.1", port = 3306, database = "mysql", user = "root", password = "123" } local res, err, errno, sqlstate = db:connect(props) if not res then ngx.say("connect to mysql error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end local drop_table_sql = "drop table if exists test" res, err, errno, sqlstate = db:query(drop_table_sql) if not res then ngx.say("drop table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end local create_table_sql = "create table test(id int primary key auto_increment, ch varchar(100))" res, err, errno, sqlstate = db:query(create_table_sql) if not res then ngx.say("create table error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end local insert_sql = "insert into test (ch) values('hello')" res, err, errno, sqlstate = db:query(insert_sql) if not res then ngx.say("insert error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end res, err, errno, sqlstate = db:query(insert_sql) ngx.say("insert rows : ", res.affected_rows, " , id : ", res.insert_id, "<br/>") local update_sql = "update test set ch = 'hello2' where id =" .. res.insert_id res, err, errno, sqlstate = db:query(update_sql) if not res then ngx.say("update error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end ngx.say("update rows : ", res.affected_rows, "<br/>") local select_sql = "select id, ch from test" res, err, errno, sqlstate = db:query(select_sql) if not res then ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end for i, row in ipairs(res) do for name, value in pairs(row) do ngx.say("select row ", i, " : ", name, " = ", value, "<br/>") end end ngx.say("<br/>") local ch_param = ngx.req.get_uri_args()["ch"] or '' local query_sql = "select id, ch from test where ch = " .. ngx.quote_sql_str(ch_param) res, err, errno, sqlstate = db:query(query_sql) if not res then ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end for i, row in ipairs(res) do for name, value in pairs(row) do ngx.say("select row ", i, " : ", name, " = ", value, "<br/>") end end local delete_sql = "delete from test" res, err, errno, sqlstate = db:query(delete_sql) if not res then ngx.say("delete error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end ngx.say("delete rows : ", res.affected_rows, "<br/>") close_db(db)
在上面的代碼中,展現了基本的創表、插入數據、修改數據、查詢數據、刪除數據的一些功能。
其中用到的lua-resty-mysql的一些API方法:
lua-resty-mysql的一些關鍵的API方法,見https://github.com/openresty/lua-resty-mysql#table-of-contents
vim /usr/example/example.conf 在配置文件配置:
location /lua_mysql { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_mysql.lua; }
瀏覽器訪問http://116.196.177.123/lua_mysql,瀏覽器顯示如下的內容:
insert rows : 1 , id : 2 update rows : 1 select row 1 : ch = hello select row 1 : id = 1 select row 2 : ch = hello2 select row 2 : id = 2 delete rows : 2
在實際的開發過程當中,不可能把全部的lua代碼寫在一個lua文件中,一般的作法將特定功能的放在一個lua文件中,即用lua模塊開發。在lualib目錄下,默認有如下的lua模塊。
lualib/ ├── cjson.so ├── ngx │ ├── balancer.lua │ ├── ocsp.lua │ ├── re.lua │ ├── semaphore.lua │ ├── ssl │ │ └── session.lua │ └── ssl.lua ├── rds │ └── parser.so ├── redis │ └── parser.so └── resty ├── aes.lua ├── core │ ├── base64.lua │ ├── base.lua │ ├── ctx.lua │ ├── exit.lua │ ├── hash.lua │ ├── misc.lua │ ├── regex.lua │ ├── request.lua │ ├── response.lua │ ├── shdict.lua │ ├── time.lua │ ├── uri.lua │ ├── var.lua │ └── worker.lua ├── core.lua ├── dns │ └── resolver.lua ├── limit │ ├── conn.lua │ ├── req.lua │ └── traffic.lua ├── lock.lua ├── lrucache │ └── pureffi.lua ├── lrucache.lua ├── md5.lua ├── memcached.lua ├── mysql.lua ├── random.lua ├── redis.lua ├── sha1.lua ├── sha224.lua ├── sha256.lua ├── sha384.lua ├── sha512.lua ├── sha.lua ├── string.lua ├── upload.lua ├── upstream │ └── healthcheck.lua └── websocket ├── client.lua ├── protocol.lua └── server.lua
在使用這些模塊以前,須要在nginx的配置文件nginx.conf中的http模塊加上如下的配置:
lua_package_path "/usr/example/lualib/?.lua;;"; #lua 模塊 lua_package_cpath "/usr/example/lualib/?.so;;"; #c模塊
如今來簡單的開發一個lua模塊:
vim /usr/example/lualib/module1.lua
在module1.lua文件加上如下的代碼:
local count = 0 local function hello() count = count + 1 ngx.say("count : ", count) end local _M = { hello = hello } return _M
開發時將全部數據作成局部變量/局部函數;經過 _M導出要暴露的函數,實現模塊化封裝。
在/usr/example/lua目錄下建立一個test_module_1.lua 文件,在該文件中引用上面的module1.lua文件。
vim /usr/example/lua/test_module_1.lua
加上如下代碼:
local module1 = require("module1") module1.hello()
經過require(「模塊名」)來加載模塊,若是是多級目錄,則須要經過require(「目錄1.目錄2.模塊名」)加載。
在/user/example/example.conf中加上如下的配置:
location /lua_module_1 { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_module_1.lua; }
屢次在瀏覽器上訪問:http://116.196.177.123/lua_module_1,瀏覽器顯示:
count : 1 count : 2 count : 3 ...
linux下安裝: cd /usr/servers
$ wget http://download.redis.io/releases/redis-3.2.6.tar.gz $ tar xzf redis-3.2.6.tar.gz $ cd redis-3.2.6 $ make
啓動redis:
nohup /usr/servers/redis-3.2.6/src/redis-server /usr/servers/redis-3.2.6/redis.conf &
查看是否啓動:
ps -ef |grep redis
終端顯示:
root 20985 14268 0 18:49 pts/0 00:00:00 /usr/servers/redis-3.2.6/src/redis-server 127.0.0.1:6379
可見redis已經啓動。
lua_resty_redis模塊地址:https://github.com/openresty/lua-resty-redis
lua-resty-redis - Lua redis client driver for the ngx_lua based on the cosocket API
lua_resty_redis 它是一個基於cosocket API的爲ngx_lua模塊提供Lua redis客戶端的驅動。
建立一個test_redis_basic.lua文件
vim /usr/example/lua/test_redis_basic.lua
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 local redis = require("resty.redis") local red = redis:new() red:set_timeout(1000) local ip = "127.0.0.1" local port = 6379 local ok, err = red:connect(ip, port) if not ok then ngx.say("connect to redis error : ", err) return close_redis(red) end ok, err = red:set("msg", "hello world") if not ok then ngx.say("set msg error : ", err) return close_redis(red) end 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)
上面的代碼很簡單,經過鏈接池鏈接Redis,鏈接上redis後,經過set一對鍵值對(msg,helloword)到redis中,而後get(msg),並經過ngx.say()返回給瀏覽器。
vim /usr/example/example.conf,添加如下的配置代碼:
location /lua_redis_basic { default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_redis_basic.lua; }
瀏覽器訪問:http://116.196.177.123/lua_redis_basic
瀏覽器顯示:
msg : hello world
lua_resty_redis支持全部的redis指令,自己Redis就支持lua語言操做。因此lua_resty_redis模塊可以提升全部的redis操做的功能。
在不少時候,Redis是設置了口令的,鏈接時,若是須要驗證口令,須要添加 local res, err = red:auth(「foobared」),示例代碼以下:
local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) -- 1 sec local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.say("failed to connect: ", err) return end local res, err = red:auth("foobared") if not res then ngx.say("failed to authenticate: ", err) return end
更多請關注的官方文檔https://github.com/openresty/lua-resty-redis 和開濤的博客http://jinnianshilongnian.iteye.com/blog/2187328
RBAC(Role-Based Access Control,基於角色的訪問控制),用戶基於角色的訪問權限控制。簡單地說,一個用戶擁有若干角色,每個角色擁有若干權限。這樣,就構形成「用戶-角色-權限」的受權模型。在這種模型中,用戶與角色之間,角色與權限之間,通常都是多對多的關係。如圖所示:
在本案例中,採用的就是這種權限設計的方式。具體的sql語句腳本以下:
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT , `name` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=2 ROW_FORMAT=COMPACT ; CREATE TABLE role( `id` int(11) NOT NULL AUTO_INCREMENT , `name` varchar(255) CHARACTER SET latin5 NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=2 ROW_FORMAT=COMPACT ; CREATE TABLE permission( `id` int(11) NOT NULL AUTO_INCREMENT , `permission` varchar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=3 ROW_FORMAT=COMPACT ; CREATE TABLE user_role( `id` int(11) NOT NULL AUTO_INCREMENT , `user_id` int(11) NULL DEFAULT NULL , `role_id` int(11) NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=2 ROW_FORMAT=COMPACT ; CREATE TABLE role_permission( `id` int(11) NOT NULL AUTO_INCREMENT , `role_id` int(11) NULL DEFAULT NULL , `permission_id` int(11) NULL DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=latin1 COLLATE=latin1_swedish_ci AUTO_INCREMENT=3 ROW_FORMAT=COMPACT ;
初始化如下的sql腳本,即給用戶id爲1的用戶關聯角色,角色並關聯權限:
INSERT INTO `permission` VALUES ('1', '/user/orgs'); INSERT INTO `role` VALUES ('1', 'user'); INSERT INTO `role_permission` VALUES ('1', '1', '1'); INSERT INTO `user` VALUES ('1', 'forezp'); INSERT INTO `user_role` VALUES ('1', '1', '1');
在本案例中,須要根據user表中的Id獲取該Id對應的權限。首先根據userId獲取該用戶對應的角色,再根據根據該角色獲取相應的權限,每每一個用戶具備多個角色,而角色又有多個權限。好比查詢userId爲1 的用戶的權限的sql語句以下:
SELECT a.id,a.permission from permission a ,role_permission b,role c,user_role d,user e WHERE a.id=b.permission_id and c.id=b.role_id and d.role_id=c.id and d.user_id=e.id and e.id=1"
在Openresty中怎麼鏈接數據庫,怎麼查詢sql語句,在以前的文章已將講述過了。根據用戶id獲取用戶的權限的功能是一個使用率極高的功能,因此考慮將這個功能模塊化。
vim /usr/example/lualib/sql_tool.lua ,編輯加入如下的代碼:
local mysql = require("resty.mysql") local function close_db(db) if not db then return end db:close() end local function select_user_permission(user_id) local db, err = mysql:new() if not db then ngx.say("new mysql error : ", err) return end db:set_timeout(1000) local props = { host = "127.0.0.1", port = 3306, database = "test", user = "root", password = "123" } local res, err, errno, sqlstate = db:connect(props) if not res then ngx.say("connect to mysql error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) close_db(db) end local select_sql = "SELECT a.id,a.permission from permission a ,role_permission b,role c,user_role d,user e WHERE a.id=b.permission_id and c.id=b.role_id and d.role_id=c.id and d.user_id=e.id and e.id="..user_id res, err, errno, sqlstate = db:query(select_sql) if not res then ngx.say("select error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate) return close_db(db) end local permissions={} for i, row in ipairs(res) do for name, value in pairs(row) do if name == "permission" then table.insert(permissions, 1, value) end end end return permissions end local _M = { select_user_permission= select_user_permission } return _M
在上面的代碼中,有一個select_user_permission(user_id)方法,該方法根據用戶名獲取該用戶的權限。查出來存在一個table 類型的 local permissions={}中。
vim /usr/example/example.conf 加上如下的代碼:
location ~ /sql_tool{ default_type 'text/html'; content_by_lua_file /usr/example/lua/test_sql_tool.lua; }
在瀏覽器上訪問http://116.196.177.123/sql_tool,瀏覽器顯示以下的內容:
/user/orgs
在以前的文章講述瞭如何使用Openresty鏈接redis,並操做redis。 這小節將講述如何使用openresty鏈接redis,並寫幾個方法,用於存儲用戶的token等,並將這些信息模塊化,主要有如下幾個方法:
vim /usr/example/lualib/tokentool.lua 編輯一下內容:
module("tokentool", package.seeall) local redis = require "resty.redis" local str = require "resty.string" local cjson = require("cjson") local redis_host = "127.0.0.1" local redis_port = 6379 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 local function connect() local red = redis:new() red:set_timeout(1000) local ok, err = red:connect(redis_host, redis_port) if not ok then return false end --local res, err = red:auth("xiaoantimes") --if not res then -- ngx.say("failed to authenticate: ", err) -- return false --end --ok, err = red:select(1) --if not ok then -- return false --end return red end function has_token(token) local red = connect() if red == false then return false end local res, err = red:get(token) if not res then return false end close_redis(red) return true end function set_permissions(user_id,permissions) if (permissions==null) or( permissions==ngx.null) then return false end local str = cjson.encode(permissions) ngx.log(ngx.ERR,"set redis p:"..str) local red=connect() if red== false then return false end local ok, err = red:set(user_id,str) if not ok then return false end return true end function get_permissions(user_id) local red=connect() if red== false then return false end local res, err = red:get(user_id) if (not res) or (res == ngx.null) then return end ngx.log(ngx.ERR,"get redis p:"..res); local permissions=cjson.decode(res) return permissions end function get_user_id(token) local red = connect() local resp, err = red:get(token) if not resp then ngx.say("get msg error : ", err) return close_redis(red) end close_redis(red) return resp end
vim /usr/example/lua/test_token_tool.lua,加上如下的內容:
local tokentool= require "tokentool" local ret = tokentool.has_token("msg") ngx.log(ngx.ERR,ret) if ret == true then ngx.say("ok") else ngx.say("oops,error") end
在/usr/example/example.conf加上如下的內容:
location ~ /token_tool{ default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/test_token_tool.lua; }
打開瀏覽器訪問http://116.196.177.123/token_tool,瀏覽器顯示:
ok
採用openresty 開發出的api網關有不少,好比比較流行的kong、orange等。這些API 網關經過提供插件的形式,提供了很是多的功能。這些組件化的功能每每可以知足大部分的需求,若是要想達到特定場景的需求,可能須要二次開發,好比RBAC權限系統。本小節經過整合前面的知識點,來構建一個RBAC權限認證系統。
本小節採用瞭如下的技術棧:
驗證流程圖以下所示:
vim /usr/example/example.conf ,加上如下的配置:
location / { default_type "text/html"; access_by_lua_file /usr/example/lua/api_access.lua; content_by_lua_file /usr/example/lua/api_content.lua; }
以上的配置表示,要不符合已有location路徑的全部請求,將走這個location爲/ 的路徑。符合這個location的請求將進入 access_by_lua_file和 content_by_lua_file的模塊判斷。
vim /usr/example/lua/access_by_lua_file ,加上如下代碼:
local tokentool = require "tokentool" local mysqltool = require "mysqltool" function is_include(value, tab) for k,v in ipairs(tab) do if v == value then return true end end return false end local white_uri={"/user/login","/user/validate"} --local user_id = ngx.req.get_uri_args()["userId"] --獲取header的token值 local headers = ngx.req.get_headers() local token=headers["token"] local url=ngx.var.uri if ( not token) or (token==null) or (token ==ngx.null) then if is_include(url,white_uri)then else return ngx.exit(401) end else ngx.log(ngx.ERR,"token:"..token) local user_id=tokentool.get_user_id(token) if (not user_id) or( user_id ==null) or ( user_id == ngx.null) then return ngx.exit(401) end ngx.log(ngx.ERR,"user_id"..user_id) local permissions={} permissions =tokentool.get_permissions(user_id) if(not permissions)or(permissions==null)or( permissions ==ngx.null) then permissions= mysqltool.select_user_permission(user_id) if permissions and permissions ~= ngx.null then tokentool.set_permissions(user_id,permissions) end end if(not permissions)or(permissions==null)or( permissions ==ngx.null) then return ngx.exit(401) end local is_contain_permission = is_include(url,permissions) if is_contain_permission == true then -- ngx.say("congratuation! you have pass the api gateway") else return ngx.exit(401) end end
在上述代碼中:
若是全部的判斷經過,則該用戶請求的具備權限訪問,則進入content_by_lua_file模塊,直接在這個模塊給請求返回「congratulations! you have passed the api gateway」。
vim /usr/example/lua/api_content.lua ,添加如下內容:
ngx.say("congratulations!"," you have passed ","the api gateway") ----200狀態碼退出 return ngx.exit(200)
打開瀏覽器訪問http://116.196.177.123/user/login,瀏覽器顯示:
congratulations! you have passed the api gateway
/user/login這個url 在白名單的範圍內,因此它是能夠經過權限驗證的。
打開瀏覽器訪問http://116.196.177.123/user/sss,顯示如下內容:
401 Authorization Required
openresty/1.11.2.4
在redis中添加一對key-value,key爲token_forezp,value爲1,即token_forezp對應的用戶的id爲1.
/usr/servers/redis-3.2.6 src/redis-cli set token_forezp 1
初始化如下的sql腳本,即給用戶id爲1的用戶關聯角色,角色並關聯權限:
INSERT INTO `permission` VALUES ('1', '/user/orgs'); INSERT INTO `role` VALUES ('1', 'user'); INSERT INTO `role_permission` VALUES ('1', '1', '1'); INSERT INTO `user` VALUES ('1', 'forezp'); INSERT INTO `user_role` VALUES ('1', '1', '1');
用postman請求,在請求頭中加入token,值爲token_forezp,請求結果以下: