nginx是俄羅斯人寫的輕量級http服務器,Nginx 以事件驅動的方式編寫,有很是好的性能,同時也是一個很是高效的反向代理、負載均衡。
nginx 穩定性高,模塊庫豐富,配置靈活,系統資源的消耗低。響應靜態頁面的速度很是快html
nginx 是異步非阻塞的方式處理請求。採用epoll事件循環,多個獨立worker處理請求,並非併發,避免加鎖和上下文切換帶來的性能問題。
深刻淺出nodejs對事件循環講的很詳細
前端
具體請求的處理java
分爲幾個模塊:node
默認狀況下,這個配置文件一般命名爲 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; } } }
開啓nginxgit
nginx -c /usr/local/nginx/conf/nginx.conf
nginx -s signal
signal 能夠爲下列命令之一:github
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反向代理和負載均衡配置比較簡單。
# 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; }
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 種調度算法:
例子用的是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通常用作降級處理。
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提供了好多個指令,好比:
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正則表達式匹配 ...
在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
讀到這裏大體就應該會搞幾個小demo了,好多api沒有介紹,看一下文檔就會用了。大概後續會補充一些nginx安全,負載均衡算法,等等一些高級應用。