初探nginx

nginx

nginx是俄羅斯人寫的輕量級http服務器,Nginx 以事件驅動的方式編寫,有很是好的性能,同時也是一個很是高效的反向代理、負載均衡。
nginx 穩定性高,模塊庫豐富,配置靈活,系統資源的消耗低。響應靜態頁面的速度很是快html

nginx 作什麼

  1. 處理靜態文件
  2. 反向代理,負載均衡和容錯
  3. 大併發
  4. 易配置,易拓展

nginx 處理過程

nginx 是異步非阻塞的方式處理請求。採用epoll事件循環,多個獨立worker處理請求,並非併發,避免加鎖和上下文切換帶來的性能問題。
深刻淺出nodejs對事件循環講的很詳細
前端

具體請求的處理java

  1. 解析配置文件, 獲得須要監聽的端口與 ip 地址,而後在 Nginx 的 master 進程裏面,先初始化好這個監控的 socket
  2. 而後再 fork 出多個子進程出來,而後子進程會競爭 accept 新的鏈接
  3. 與客戶端三次握手獲得這個創建好的鏈接的 socket,而後建立 Nginx 對鏈接的封裝
  4. Nginx 或客戶端來主動關掉鏈接

nginx的配置

分爲幾個模塊:node

  1. main: Nginx 在運行時與具體業務功能(好比http服務或者email服務代理)無關的一些參數,好比工做進程數,運行的身份等。
  2. http: 與提供 http 服務相關的一些配置參數。例如:是否使用 keepalive 啊,是否使用gzip進行壓縮等。
  3. server: http 服務上支持若干虛擬主機。每一個虛擬主機一個對應的 server 配置項,配置項裏面包含該虛擬主機相關的配置。
  4. location: http 服務中,某些特定的URL對應的一系列配置項。

默認狀況下,這個配置文件一般命名爲 nginx.conf 而且會放置在 /usr/local/nginx/conf,/etc/nginx,或者 /usr/local/etc/nginx
示例配置nginx

user  nobody;
    worker_processes  1;
    error_log  logs/error.log  info;

    events {
        worker_connections  1024;
    }

    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
        gzip  on;
        server {
            listen          80;
            server_name     localhost;
            access_log  /var/log/nginx/host.access.log  main;
            location / {
                index index.html;
                root  /usr/local/openresty/nginx/html;
            }
        }
    }

nginx 的使用

開啓nginxgit

nginx -c /usr/local/nginx/conf/nginx.conf
nginx -s signal

signal 能夠爲下列命令之一:github

  • stop — 直接關閉 nginx
  • quit — 會在處理完當前正在的請求後退出,也叫優雅關閉
  • reload — 從新加載配置文件,至關於重啓 // 滾動升級
  • reopen — 從新打開日誌文件

nginx with lua -- openresty

OpenResty® 是一個基於 Nginx 與 Lua 的高性能 Web 平臺,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用於方便地搭建可以處理超高併發、擴展性極高的動態 Web 應用、Web 服務和動態網關。
經過融合lua,能夠將 Nginx 有效地變成一個強大的web服務器!web

環境搭建

直接搭建openresty, nginx + lua 套餐正則表達式

brew tap homebrew/nginx
brew install homebrew/nginx/openresty

若是一切順利,OpenResty 應該已經安裝好了。
爲了方便,這邊直接用docker裝OpenResty(《前端到docker入門》):
新建一個Dockerfile,寫入:算法

#Dockerfile
 FROM openresty/openresty:trusty
 RUN apt-get update && apt-get install -y vim

能夠直接裝openresty的,可是容器的bash裏面沒有vim,在裏面改代碼很麻煩,因此就本身構建了一個image。

而後構建image

docker build -t openresty .
docker container run -itd -p 80:80 --name test openresty

構建一個名叫test的容器。
ok,如今已經跑起來了,訪問http://localhost,已經出現了openresty的歡迎頁

運行

docker ps -a

能夠看到:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                NAMES
06c9a63607eb        openresty           "/usr/local/openrest…"   4 hours ago         Up 25 minutes               0.0.0.0:80->80/tcp   test

已經起了服務。
咱們進入容器看一下:

docker exec -it test bash

裏面就是nginx的目錄。

咱們的配置在

vi usr/local/openresty/nginx/conf/nginx.conf
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

其中引入了/etc/nginx/conf.d/*.conf;咱們的server配置就在這裏。
如今該這個etc/nginx/conf.d/default.conf就能夠愉快的進行nginx配置了。

location / {
        root   /usr/local/openresty/nginx/html;
        index  index.html index.htm;
    }

咱們訪問的主頁目錄就在root指定下的index.html,root是指定根目錄,index指定默認訪問文件。

訪問控制

經過lua來控制是否進入處理邏輯。其中ngx.var.remote_addr,ngx.HTTP_FORBIDDEN都是nginx的內置變量

access_by_lua_block {
                local black_ips = {["127.0.0.1"]=true}

                local ip = ngx.var.remote_addr
                if true == black_ips[ip] then
                    ngx.exit(ngx.HTTP_FORBIDDEN)
                end
            };
// 業務邏輯處理...

nginx 反向代理和負載均衡

nginx反向代理和負載均衡配置比較簡單。

反向代理

# 1. 用戶訪問 http://ip:port,則反向代理到 https://github.com
location / {
    proxy_pass  https://github.com;
    proxy_redirect     off;
    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: proxy_pass 後面跟着一個 URL,用來將請求反向代理到 URL 參數指定的服務器上。例如咱們上面例子中的 proxy_pass https://github.com,則將匹配的請求反向代理到 https://github.com
  • proxy_set_header 反向代理不會轉發原始請求中的 Host 頭部,若是須要轉發,就須要加上這句:proxy_set_header Host $host;

負載均衡

nginx經過upstream字段配置負載均衡。

upstream test.net{
    ip_hash;
    server 192.168.10.13:80;
    server 192.168.10.14:80  down;
    server 192.168.10.15:8009  max_fails=3  fail_timeout=20s;
    server 192.168.10.16:8080;
}
server {
    location / {
        proxy_pass  http://test.net;
    }
}

upstream 是 Nginx 的 HTTP Upstream 模塊,這個模塊經過一個簡單的調度算法來實現客戶端 IP 到後端服務器的負載均衡。
Nginx 的負載均衡模塊目前支持 6 種調度算法:

  1. 輪詢(默認)
  2. ip_hash
  3. fair
  4. url_hash
  5. least_conn
  6. hash

例子用的是ip_hash,是每一個請求按訪問 IP 的 hash 結果分配。
採用輪詢時能夠指定每一個server的權重:

upstream webservers {
    server 192.168.18.201 weight=1 max_fails=2 fail_timeout=2;
    server 192.168.18.202 weight=1 max_fails=2 fail_timeout=2;
}

利用 max_fails、fail_timeout 參數,控制異常狀況。
max_fails:容許請求失敗的次數,默認爲 1 。當超過最大次數時,返回 proxy_next_upstream 模塊定義的錯誤。
fail_timeout:在經歷了 max_fails 次失敗後,暫停服務的時間。max_fails 能夠和 fail_timeout 一塊兒使用。
還有:
down:表示當前的 server 暫時不參與負載均衡。
backup:預留的備份機器。當其餘全部的非 backup 機器出現故障或者忙的時候,纔會請求 backup 機器,所以這臺機器的壓力最輕。
backup通常用作降級處理。

日誌log

nginx 的日誌路徑是寫在nginx.conf裏的。
看一下 usr/local/openresty/nginx/conf/nginx.conf,把裏面註釋的這幾行放出來。

error_log  logs/error.log;


  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                     '$status $body_bytes_sent "$http_referer" '
                     '"$http_user_agent" "$http_x_forwarded_for"';

   access_log  logs/access.log  main;

error_log是錯誤日誌,access_log是訪問日誌,log_format是根據格式format日誌消息。
日誌文件在:usr/local/openresty/nginx/logs/access.log
能夠看到訪問的log。error.log是錯誤信息。

172.17.0.1 - - [24/Apr/2018:09:46:24 +0000] "GET /res HTTP/1.1" 200 68 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36" "-"
172.17.0.1 - - [24/Apr/2018:09:46:34 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36" "-"

若是看不到,先刪除已存在的access.log和error.log再重啓nginx應該就能夠了。

退出容器docker logs test 也能夠看到nginx的log。

如何結合lua

lua提供了好多個指令,好比:

content_by_lua_file 直接加lua file的路徑,接受請求,並處理響應。
 lua_package_path 設置用lua代碼寫的擴展庫路徑,lua文件裏require要用到。
set_by_lua_file $var <path-to-lua-script-file> [$arg1 $arg2 ...]; 設置一個Nginx變量,變量值從lua腳本里運算由return返回,能夠實現複雜的賦值邏輯;此處是阻塞的,Lua代碼要作到很是快.

 rewrite_by_lua_file lua文件的重定向操做
 access_by_lua_file lua文件的訪問控制 
 header_filter_by_lua_file 設置header 和 cookie
 init_by_lua_file ginx Master進程加載配置時執行;一般用於初始化全局配置/預加載Lua模塊
 ...

lua經常使用的方法和常量:

ngx.arg[index]  #ngx指令參數,當這個變量在set_by_lua或者set_by_lua_file內使用的時候是隻讀的,指的是在配置指令輸入的參數
ngx.var.varname #讀寫NGINX變量的值,最好在lua腳本里緩存變量值,避免在當前請求的生命週期內內存的泄漏
ngx.config.ngx_lua_version  #當前ngx_lua模塊版本號
ngx.config.nginx_version    #nginx版本
ngx.worker.pid              #當前worker進程的PID
...

print()    #與 ngx.print()方法有區別,print() 至關於ngx.log()
ngx.ctx    #這是一個lua的table,用於保存ngx上下文的變量,在整個請求的生命週期內都有效,詳細參考官方
ngx.location.capture()          #發出一個子請求
ngx.location.capture_multi()    #發出多個子請求
ngx.status                      #讀或者寫當前請求的相應狀態. 必須在輸出相應頭以前被調用
ngx.header.HEADER               #訪問或設置http header頭信息
ngx.req.set_uri()               #設置當前請求的URI
ngx.set_uri_args(args)          #根據args參數從新定義當前請求的URI參數
ngx.req.get_uri_args()          #返回一個lua table,包含當前請求的所有的URL參數
ngx.req.get_post_args()         #返回一個LUA TABLE,包括全部當前請求的POST參數
ngx.req.get_headers()           #返回一個包含當前請求頭信息的lua table
ngx.req.set_header()            #設置當前請求頭header某字段值.當前請求的子請求不會受到影響
ngx.req.read_body()             #在不阻塞ngnix其餘事件的狀況下同步讀取客戶端的body信息
ngx.time()                      #返回當前時間戳
ngx.re.match(subject,regex,options,ctx)     #ngx正則表達式匹配
...

開始openresty

在etc/nginx/conf.d/default.conf的location下面加一句

location /test {
        default_type text/html;

        content_by_lua_block {
            ngx.say("HelloWorld")
        }
    }

重啓nginx nginx -s reload
訪問http://localhost/test能夠看到咱們寫的helloworld。

再好比

location = /sum {
    # 只容許內部調用
    internal;

    # 這裏作了一個求和運算只是一個例子,能夠在這裏完成一些數據庫、
    # 緩存服務器的操做,達到基礎模塊和業務邏輯分離目的
    content_by_lua_block {
        local args = ngx.req.get_uri_args()
        ngx.say(tonumber(args.a) + tonumber(args.b))
    }
}

location = /app/test {
    content_by_lua_block {
        local res = ngx.location.capture(
                        "/sum", {args={a=3, b=8}}
                        )
        ngx.say("status:", res.status, " response:", res.body)
    }
}

請求內部接口,返回結果。

好比:
lua能夠幫咱們作跳轉。重寫url等等。

location = /stream {
    rewrite_by_lua_block {
        return ngx.redirect('http://open.toutiao.com');
    }
}

好比

location /print_param {
        default_type text/html;
        content_by_lua_block {
           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
       }
   }

訪問http://localhost/print_param?a=1&b=2&c=3
能夠看到[GET ] key:b v:2 [GET ] key:a v:1 [GET ] key:c v:3

location /res {
    default_type text/html;
    local table = {
        "hello, ",
        {"world: ", true, " or ", false,
            {": ", nil}}
    }
    ngx.print(table)
}

ngx.print吐出碎片化字符串。hello, world: true or false:

location /res {
     content_by_lua_block {
        ngx.header['Content-Type'] = 'application/json; charset=UTF-8'
        local str = '{"a":1,"b":"ss","c":{"c1":1,"c2":2},"d":[10,11],"1":100}'
        ngx.say(str)
    }
}

返回json

nginx

讀到這裏大體就應該會搞幾個小demo了,好多api沒有介紹,看一下文檔就會用了。大概後續會補充一些nginx安全,負載均衡算法,等等一些高級應用。

相關文章
相關標籤/搜索