Openresrt最佳案例

第1篇:Nginx介紹

Nginx是一個高性能的Web 服務器,同時是一個高效的反向代理服務器,它仍是一個IMAP/POP3/SMTP 代理服務器。javascript

因爲Nginx採用的是事件驅動的架構,可以處理併發百萬級別的tcp鏈接,高度的模塊化設計和自由的BSD許可,使得Nginx有着很是豐富的第三方模塊。好比Openresty、API網關Kong。php

BSD開源協議是一個給予使用者很大自由的協議。基本上使用者能夠」隨心所欲」,能夠自由的使用,修改源代碼,也能夠將修改後的代碼做爲開源或者專有軟件再發布。css

Nginx的優勢

  • 高併發響應性能很是好,官方Nginx處理靜態文件併發5w/s
  • 反向代理性能很是強。(可用於負載均衡)
  • 內存和cpu佔用率低。(爲Apache的1/5-1/10)
  • 對後端服務有健康檢查功能。
  • 支持PHP cgi方式和fastcgi方式。
  • 配置代碼簡潔且容易上手。

Nginx的安裝

Centos系統安裝,請參考這裏http://www.linuxidc.com/Linux/2016-09/134907.htm。先複製粘貼下它的文章。html

1.gcc 安裝

安裝 nginx 須要先將官網下載的源碼進行編譯,編譯依賴 gcc 環境,若是沒有 gcc 環境,則須要安裝:java

yum install gcc-c++node

2.PCRE pcre-devel 安裝

PCRE(Perl Compatible Regular Expressions) 是一個Perl庫,包括 perl 兼容的正則表達式庫。nginx 的 http 模塊使用 pcre 來解析正則表達式,因此須要在 linux 上安裝 pcre 庫,pcre-devel 是使用 pcre 開發的一個二次開發庫。nginx也須要此庫。命令:mysql

yum install -y pcre pcre-devellinux

3.zlib 安裝

zlib 庫提供了不少種壓縮和解壓縮的方式, nginx 使用 zlib 對 http 包的內容進行 gzip ,因此須要在 Centos 上安裝 zlib 庫。nginx

yum install -y zlib zlib-develc++

4.OpenSSL 安裝

OpenSSL 是一個強大的安全套接字層密碼庫,囊括主要的密碼算法、經常使用的密鑰和證書封裝管理功能及 SSL 協議,並提供豐富的應用程序供測試或其它目的使用。 nginx 不只支持 http 協議,還支持 https(即在ssl協議上傳輸http),因此須要在 Centos 安裝 OpenSSL 庫。

yum install -y openssl openssl-devel

5.官網下載

1.直接下載.tar.gz安裝包,地址:https://nginx.org/en/download.html

2.使用wget命令下載(推薦)。

wget -c https://nginx.org/download/nginx-1.10.1.tar.gz

6.解壓

依然是直接命令:

tar -zxvf nginx-1.10.1.tar.gz cd nginx-1.10.1

7.配置

其實在 nginx-1.10.1 版本中你就不須要去配置相關東西,默認就能夠了。固然,若是你要本身配置目錄也是能夠的。 使用默認配置

./configure

8.編譯安裝

make make install

查找安裝路徑:

whereis nginx

Nginx的模塊組成

Nginx的模塊從結構上分爲核心模塊、基礎模塊和第三方模塊:

  • 核心模塊:HTTP模塊、EVENT模塊和MAIL模塊
  • 基礎模塊:HTTP Access模塊、HTTP FastCGI模塊、HTTP Proxy模塊和HTTP Rewrite模塊,
  • 第三方模塊:HTTP Upstream Request Hash模塊、Notice模塊和HTTP Access Key模塊。

Nginx的高併發得益於其採用了epoll模型,與傳統的服務器程序架構不一樣,epoll是linux內核2.6之後纔出現的。Nginx採用epoll模型,異步非阻塞,而Apache採用的是select模型。

  • Select特色:select 選擇句柄的時候,是遍歷全部句柄,也就是說句柄有事件響應時,select須要遍歷全部句柄才能獲取到哪些句柄有事件通知,所以效率是很是低。
  • epoll的特色:epoll對於句柄事件的選擇不是遍歷的,是事件響應的,就是句柄上事件來就立刻選擇出來,不須要遍歷整個句柄鏈表,所以效率很是高。

Nginx經常使用命令

nginx 環境變量配置:

export PATH=$PATH:/usr/servers/nginx/sbin

  • 查看nginx進程 ps -ef|grep nginx
  • 啓動nginx nginx 啓動結果顯示nginx的主線程和工做線程,工做線程的數量跟nginx.conf中的配置參數worker_processes有關。
  • 平滑啓動nginx kill -HUP cat /var/run/nginx.pid 或者 nginx -s reload
  • 強制中止nginx pkill -9 nginx
  • 檢查對nginx.conf文件的修改是否正確 nginx -t
  • 中止nginx的命令 nginx -s stop或者pkill nginx
  • 查看nginx的版本信息 nginx -v
  • 查看完整的nginx的配置信息 nginx -V

Nginx的配置

一般狀況下,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;
        }
     }
}

Nginx 內置綁定變量

名稱 說明
$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,可能和最初的值有不一樣,好比通過重定向之類的

第2篇:Lua入門

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環境的麻煩。

第一個Lua程序

安裝好環境後,我採用EmmyLua插件的形式,對Lua的入門語法進行一個簡單的講解。 打開EmmyLua的終端,在終端上輸入:

print("hi you")

按ctrl+enter,終端顯示:

hi you

Lua基本數據類型

lua的基本數據類型有nil、string、boolean、number、function類型。

nil 類型

nil相似於Java中的null ,表示空值。變量第一次賦值爲nil。

local num
print(num)
num=100
print(num)

終端輸出:

nil

100

number (數字)

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

string 字符串

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 (表)

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

function(函數)

在 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 邏輯非
  • a and b 若是 a 爲 nil,則返回 a,不然返回 b;
  • a or b 若是 a 爲 nil,則返回 b,不然返回 a。
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

for 控制結構

Lua 提供了一組傳統的、小巧的控制結構,包括用於條件判斷的 if 用於迭代的 while、repeat 和 for,本章節主要介紹 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 泛型

泛型 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

第3篇:Openresty的安裝

個人服務器爲一臺全新的centos 7的服務器,因此從頭安裝openresty,並記錄了安裝過程當中出現的問題,以及解決辦法。

1.首先安裝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

2.安裝gcc

yum -y install gcc automake autoconf libtool make

3.從新make

make clean && make && make install

ln -sf luajit-2.1.0-alpha /usr/local/bin/luajit

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

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

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

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

6.從新安裝opresty

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.

安裝成功了。

6.配置nginx

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")';  
    } 
}

7.環境變量:

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

8.將Lua項目化:

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/

第4篇:OpenResty常見的api

這篇文章主要講解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:

  • ngx.req.get_uri_args 獲取在uri上的get類型參數,返回的是一個table類型的數據結構。
  • ngx.req.read_body 讀取body,這在解析body以前,必定要先讀取body。
  • ngx.req.get_post_args 獲取form表單類型的參數,返回結果是一個table類型的數據。

使用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/>

獲取http的其餘方法

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:

  • ngx.header 向響應頭輸出內容
  • ngx.say 輸出響應體
  • ngx.print輸出響應體
  • ngx.exit 指定http狀態碼退出

使用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

能夠看到在日誌文件中已經輸出了日誌,這種日誌主要用於記錄和測試。

日誌級別:

  • ngx.STDERR – 標準輸出
  • ngx.EMERG – 緊急報錯
  • ngx.ALERT – 報警
  • ngx.CRIT – 嚴重,系統故障,觸發運維告警系統
  • ngx.ERR – 錯誤,業務不可恢復性錯誤
  • ngx.WARN – 告警,業務中可忽略錯誤
  • ngx.NOTICE – 提醒,業務比較重要信息
  • ngx.INFO – 信息,業務瑣碎日誌信息,包含不一樣狀況判斷等
  • ngx.DEBUG – 調試

內部調用

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 最佳實踐》

微信截圖_20170929150427.png

如上圖所示,openresty的執行階段分爲

這樣咱們就能夠根據咱們的須要,在不一樣的階段直接完成大部分典型處理了。

  • set_by_lua* : 流程分支處理判斷變量初始化
  • rewrite_by_lua* : 轉發、重定向、緩存等功能(例如特定請求代理到外網)
  • access_by_lua* : IP 准入、接口權限等狀況集中處理(例如配合 iptable 完成簡單防火牆)
  • content_by_lua* : 內容生成
  • header_filter_by_lua* : 響應頭部過濾處理(例如添加頭部信息)
  • body_filter_by_lua* : 響應體過濾處理(例如完成應答內容統一成大寫)

執行階段概念:

  • log_by_lua* : 會話完成後本地異步完成日誌記錄(日誌能夠記錄在本地,還能夠同步到其 他機器) 實際上咱們只使用其中一個階段
  • content_by_lua* ,也能夠完成全部的處理。但這樣作,會讓 咱們的代碼比較臃腫,越到後期愈加難以維護。把咱們的邏輯放在不一樣階段,分工明確,代 碼獨立,後期發力能夠有不少有意思的玩法。

第5篇:http和C_json模塊

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方法:

  • syntax: httpc = http.new() 建立一個 http對象
  • syntax: res, err = httpc:request_uri(uri, params)根據參數獲取內容,包括:
    • status 狀態碼
    • headers 響應頭
    • body 響應體

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,瀏覽器會顯示淘寶的搜索頁。

lua_cjson模塊

Json是一種常見的數據交換格式,經常使用於http通訊協議和其餘數據傳輸領域。在openresty默認內嵌了lua_cjson模塊,用來序列化數據。

lua_cjson模塊的地址:https://www.kyne.com.au/~mark/software/lua-cjson-manual.html

它經常使用的API以下:

  • local cjson = require 「cjson」 獲取一個cjson對象
  • local str = cjson.encode(obj) obj轉換成string
  • local obj = cjson.decode(str) 將string轉obj

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

第6篇:OpenResty鏈接Mysql

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');

openresty鏈接mysql

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方法:

  • syntax: db, err = mysql:new() 建立一個mysql數據庫鏈接對象
  • syntax: ok, err = db:connect(options) 嘗試遠程鏈接mysql
    • host mysql的主機名
    • port 端口
    • database 數據庫名
    • user 用戶名
    • password 密碼
    • charset 編碼
  • syntax: db:set_timeout(time) 設置數據庫鏈接超時時間
  • syntax: ok, err = db:set_keepalive(max_idle_timeout, pool_size) 設置鏈接池
  • syntax: ok, err = db:close() 關閉數據庫
  • syntax: bytes, err = db:send_query(query) 發送查詢

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

第7篇:模塊開發、OpenResty鏈接Redis

在實際的開發過程當中,不可能把全部的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

...

安裝redis

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鏈接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

第8篇:RBAC介紹、sql和redis模塊工具類

RBAC(Role-Based Access Control,基於角色的訪問控制),用戶基於角色的訪問權限控制。簡單地說,一個用戶擁有若干角色,每個角色擁有若干權限。這樣,就構形成「用戶-角色-權限」的受權模型。在這種模型中,用戶與角色之間,角色與權限之間,通常都是多對多的關係。如圖所示:

WX20171105-214559@2x.png

sql_tool

在本案例中,採用的就是這種權限設計的方式。具體的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

tokentool

在以前的文章講述瞭如何使用Openresty鏈接redis,並操做redis。 這小節將講述如何使用openresty鏈接redis,並寫幾個方法,用於存儲用戶的token等,並將這些信息模塊化,主要有如下幾個方法:

  • close_redis(red) 經過鏈接池的方式釋放一個鏈接
  • connect() 鏈接redis
  • has_token(token) redis中存在token 與否
  • get_user_id(token) 根據token獲取用戶id
  • set_permissions(user_id,permissions) 根據userid設置權限
  • get_permissions(user_id)根據userid獲取權限

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

第9篇:Openresty實現的網關權限控制

採用openresty 開發出的api網關有不少,好比比較流行的kong、orange等。這些API 網關經過提供插件的形式,提供了很是多的功能。這些組件化的功能每每可以知足大部分的需求,若是要想達到特定場景的需求,可能須要二次開發,好比RBAC權限系統。本小節經過整合前面的知識點,來構建一個RBAC權限認證系統。

技術棧

本小節採用瞭如下的技術棧:

  • Openresty(lua+nginx)
  • mysql
  • redis
  • cjson

驗證流程

  • 用戶請求通過nginx,nginx的openresty的模塊經過攔截請求來進行權限判斷
  • openresty的access_by_lua_file模塊,進行了一系列的判斷
    • 用戶的請求是否爲白名單uri,若是爲白名單uri,則直接經過驗證,進入下一個驗證環節content_by_lua_file,這個環節直接打印一句話:「恭喜,請求經過。」
    • 若是用戶請求不爲白名單url,則須要取出請求header中的token,若是請求的header不存在token,則直接返回結果401,無權限訪問。
    • 若是用戶請求的uri的請求頭包含token ,則取出token,解密token取出用戶id
    • 根據取出的userid去查詢數據庫獲取該用戶的權限,若是權限包含了該請求的uri,請求能夠經過,不然,請求不經過。
  • 請求若是經過access_by_lua_file模塊,則進入到content_by_lua_file模塊,該模塊直接返回一個字符串給用戶請求,在實際的開發中,可能爲路由到具體的應用程序的服務器。

驗證流程圖以下所示:

WX20171106-114542@2x.png

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

在上述代碼中:

  • is_include(value, tab),該方法判斷某個字符串在不在這個table中。
  • white_uri={「/user/login」,」/user/validate」} 是一個白名單的列表。
  • local headers = ngx.req.get_headers()從請求的uri的請求頭獲取token
  • is_include(url,white_uri)判斷該url是否爲白名單url
  • local user_id=tokentool.get_user_id(token)根據token獲取該token對應的用戶的user_id,在常見狀況下,是根據token解析出user_id,但在不一樣的語言加密和加密token存在鹽值不同的狀況,比較麻煩,因此我偷了個懶,直接存了redis,用戶登陸成功後存一下。
  • permissions =tokentool.get_permissions(user_id)根據user_id 從redis獲取該用戶的權限。
  • permissions= mysqltool.select_user_permission(user_id)若是redis沒有存該用戶的權限,則從數據庫讀。
  • tokentool.set_permissions(user_id,permissions),將從數據庫中讀取的權限點存在reddis中。
  • local is_contain_permission = is_include(url,permissions),判斷該url 在不在該用戶對應的權限列表中。

若是全部的判斷經過,則該用戶請求的具備權限訪問,則進入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,請求結果以下:

WX20171104-182432@2x.png

相關文章
相關標籤/搜索