Nginx默認採用多進程工做方式,Nginx啓動後,會運行一個master進程和多個worker進程。其中master充當整個進程組與用戶的交互接口,同時對進程進行監護,管理worker進程來實現重啓服務、平滑升級、更換日誌文件、配置文件實時生效等功能。worker用來處理基本的網絡事件,worker之間是平等的,他們共同競爭來處理來自客戶端的請求。生產環境通常採用的是多進程+多路複用的形式。這裏能夠從服務器上能夠很直觀的看到:html
進程模型:前端
1. 在建立master進程時,先創建須要監聽的socket(listenfd),而後從master進程中fork()出多個worker進程,如此一來每一個worker進程均可以監聽用戶請求的socket。通常來講,當一個鏈接進來後,全部Worker都會收到通知,可是隻有一個進程能夠接受這個鏈接請求,其它的都失敗,這是所謂的驚羣現象。nginx提供了一個accept_mutex(互斥鎖),有了這把鎖以後,同一時刻,就只會有一個進程在accpet鏈接,這樣就不會有驚羣問題了。java
2. 先打開accept_mutex選項,只有得到了accept_mutex的進程纔會去添加accept事件。nginx使用一個叫ngx_accept_disabled的變量來控制是否去競爭accept_mutex鎖。ngx_accept_disabled = nginx單進程的全部鏈接總數 / 8 -空閒鏈接數量,當ngx_accept_disabled大於0時,不會去嘗試獲取accept_mutex鎖,ngx_accept_disable越大,讓出的機會就越多,這樣其它進程獲取鎖的機會也就越大。不去accept,每一個worker進程的鏈接數就控制下來了,其它進程的鏈接池就會獲得利用,這樣,nginx就控制了多進程間鏈接的平衡。mysql
3.每一個worker進程都有一個獨立的鏈接池,鏈接池的大小是worker_connections。這裏的鏈接池裏面保存的其實不是真實的鏈接,它只是一個worker_connections大小的一個ngx_connection_t結構的數組。而且,nginx會經過一個鏈表free_connections來保存全部的空閒ngx_connection_t,每次獲取一個鏈接時,就從空閒鏈接鏈表中獲取一個,用完後,再放回空閒鏈接鏈表裏面。一個nginx能創建的最大鏈接數,應該是worker_connections * worker_processes。固然,這裏說的是最大鏈接數,對於HTTP請求本地資源來講,可以支持的最大併發數量是worker_connections * worker_processes,而若是是HTTP做爲反向代理來講,最大併發數量應該是worker_connections * worker_processes/2。由於做爲反向代理服務器,每一個併發會創建與客戶端的鏈接和與後端服務的鏈接,會佔用兩個鏈接。linux
相關的配置:nginx
worker_processes 1; // 工做進程數,建議設成CPU總核心數。 events { // 多路複用IO模型機制,epoll . select ....根據操做系統不一樣來選擇。linux 默認epoll use epoll; //io 模型 worker_connections 1024; // 每一個woker進程的最大鏈接數,數值越大,併發量容許越大 } http{ sendfile on;//開啓零拷貝 }
Nginx 做爲反向代理服務器,全部的流量都會通過 Nginx,因此 Nginx 自己的可靠性是咱們首先要考慮的問題。c++
keepalived:redis
Keepalived 是 Linux 下一個輕量級別的高可用解決方案,Keepalived 軟件起初是專爲 LVS 負載均衡軟件設計的,LVS 是 Linux Virtual Server 的縮寫,也就是 Linux 虛擬服務器,在 linux2.4 內核之後,已經徹底內置了 LVS 的各個功能模塊。它是工做在四層的負載均衡,相似於 Haproxy, 主要用於實現對服務器集羣的負載均衡。用來管理並監控 LVS 集羣系統中各個服務節點的狀態,後來又加入了能夠實現高可用的 VRRP 功能。所以,Keepalived 除了可以管理 LVS 軟件外,還能夠做爲其餘服務(例如:Nginx、Haproxy、MySQL 等)的高可用解決方案軟件。算法
Keepalived 軟件主要是經過 VRRP 協議實現高可用功能的。VRRP 是 Virtual Router RedundancyProtocol(虛擬路由器冗餘協議)的縮寫,VRRP 出現的目的就是爲了解決靜態路由單點故障問題的,它可以保證當個別節點宕機時,整個網絡能夠不間斷地運行;(簡單來講,vrrp 就是把兩臺或多態路由器設備虛擬成一個設備,實現主備高可用)。因此,Keepalived 一方面具備配置管理 LVS 的功能,同時還具備對 LVS 下面節點進行健康檢查的功能,另外一方面也可實現系統網絡服務的高可用功能sql
關於四層負載,咱們知道 osi 網絡層次模型的 7 層模模型(應用層、表示層、會話層、傳輸層、網絡層、數據鏈路層、物理層);四層負載就是基於傳輸層,也就是ip+端口的負載;而七層負載就是須要基於 URL 等應用層的信息來作負載,同時還有二層負載(基於 MAC)、三層負載(IP);常見的四層負載有:LVS、F5; 七層負載有:Nginx、HAproxy; 在軟件層面,Nginx/LVS/HAProxy 是使用得比較普遍的三種負載均衡軟件。
keepalived 安裝:
1.tar -zxvf keepalived-2.0.10.tar.gz
2. ./configure --prefix=/data/program/keepalived --sysconf=/etc 我這邊使用默認安裝路徑,執行./configure --sysconf=/etc 會安裝到 /usr/local 下
3.若是缺乏依賴庫 安裝 yum -y install pcre-devel zlib-devel openssl openssl-devel,yum install gcc gcc-c++
4.make && make install
5.進入安裝後的路徑 cd /usr/local, 建立軟鏈接: ln -s sbin/keepalived /sbin
6.複製運行命令 cp /mysoft/keepalived-2.0.10/keepalived/etc/init.d/keepalived /etc/init.d/
7.添加到系統服務 chkconfig --add keepalived
8.啓用該服務 systemctl enable keepalived.service
9.啓動 service keepalived start 並查看狀態,安裝成功。在配置完conf文件後啓動會有以下信息
keepalived 的配置 vim /etc/keepalived/keepalived.conf,因爲keepalive只做爲vrrp協議的實現,負載代理的工做是交給後面的nginx或者haproxy來實現的。因此部署到一個節點上便可。
MASTER:(192.168.254.139)與nginx在同一個機器上
! Configuration File for keepalived
global_defs {
router_id MASTER_DEVEL #運行 keepalived 服務器的標識,在一個網絡內應該是惟一的
}
vrrp_instance VI_1 { #vrrp 實例定義部分
state MASTER #設置 lvs 的狀態,MASTER 和 BACKUP 兩種,必須大寫
interface ens33 #設置對外服務的接口
virtual_router_id 51 #設置虛擬路由標示,這個標示是一個數字,同一個 vrrp 實例使用惟一標示
priority 150 #定義優先級,數字越大優先級越高,在一個 vrrp——instance 下,master 的優先級必須大於 backup
advert_int 1 #設定 master 與 backup 負載均衡器之間同步檢查的時間間隔,單位是秒
authentication { #設置驗證類型和密碼
auth_type PASS
auth_pass 1111 #驗證密碼,同一個 vrrp_instance 下 MASTER 和 BACKUP 密碼必須相同}
virtual_ipaddress { #設置虛擬 ip 地址,能夠設置多個,每行一個
192.168.254.111
}
}
virtual_server 192.168.254.111 80 { #設置虛擬服務器,須要指定虛擬 ip 和服務端口
delay_loop 6 #健康檢查時間間隔
lb_algo rr #負載均衡調度算法
lb_kind NAT #負載均衡轉發規則
persistence_timeout 50 #設置會話保持時間
protocol TCP #指定轉發協議類型,有 TCP 和 UDP 兩種
real_server 192.168.254.139 80 { #配置服務器節點 1,須要指定 real server 的真實 IP 地址和端口weight 1 #設置權重,數字越大權重越高
TCP_CHECK { #realserver 的狀態監測設置部分單位秒
connect_timeout 3 #超時時間
delay_before_retry 3 #重試間隔
connect_port 80 #監測端口
}
}
}
BACKUP:(192.168.254.137)與nginx在同一個機器上
! Configuration File for keepalived
global_defs {
router_id BACKUP_DEVEL #運行 keepalived 服務器的標識,在一個網絡內應該是惟一的
}
vrrp_instance VI_1 { #vrrp 實例定義部分
state BACKUP #設置 lvs 的狀態,MASTER 和 BACKUP 兩種,必須大寫
interface ens33 #設置對外服務的接口
virtual_router_id 51 #設置虛擬路由標示,這個標示是一個數字,同一個 vrrp 實例使用惟一標示
priority 100 #定義優先級,數字越大優先級越高,在一個 vrrp——instance 下,master 的優先級必須大於 backup
advert_int 1 #設定 master 與 backup 負載均衡器之間同步檢查的時間間隔,單位是秒
authentication { #設置驗證類型和密碼
auth_type PASS
auth_pass 1111 #驗證密碼,同一個 vrrp_instance 下 MASTER 和 BACKUP 密碼必須相同}
virtual_ipaddress { #設置虛擬 ip 地址,能夠設置多個,每行一個
192.168.254.111
}
}
virtual_server 192.168.254.111 80 { #設置虛擬服務器,須要指定虛擬 ip 和服務端口
delay_loop 6 #健康檢查時間間隔
lb_algo rr #負載均衡調度算法
lb_kind NAT #負載均衡轉發規則
persistence_timeout 50 #設置會話保持時間
protocol TCP #指定轉發協議類型,有 TCP 和 UDP 兩種
real_server 192.168.254.137 80 { #配置服務器節點 1,須要指定 real server 的真實 IP 地址和端口weight 1 #設置權重,數字越大權重越高
TCP_CHECK { #realserver 的狀態監測設置部分單位秒
connect_timeout 3 #超時時間
delay_before_retry 3 #重試間隔
connect_port 80 #監測端口
}
}
}
經過腳本實現動態切換(與nginx心跳的綁定):
經過腳本的方式來檢測 nginx 服務是否存活,一旦nginx掛了,那麼能夠經過這個機制把 keepalived 關閉,把機會讓給哪些還存活的節點。詳細配置以下:
首先編寫shell 腳本:我這邊是在keepalived.conf 所在目錄下執行:vim vim nginx_service_check.sh,輸入如下信息並保存。
#!bin/sh #! /bin/sh 是指此腳本使用/bin/sh 來執行
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ]
then
echo 'nginx server is died'
service keepalived stop
fi
能夠經過sh nginx_service_check.sh 來驗證該腳本是否正確。在nginx 沒有啓動的時候要是能輸出以下信息就說明是OK的:
爲給文件添加權限 : chmod +x nginx_service_check.sh 。
而後修改 keepalived.conf 文件:加入如下信息
global_defs {
router_id MASTER_DEVEL
enable_script_security
}
vrrp_script check_nginx_service {
script "/etc/keepalived/nginx_service_check.sh"
interval 3
weight -10
user root
}
#還有在vrrp_instance VI_1 這個模塊中添加以下信息
vrrp_instance VI_1 {
track_script{
check_nginx_service
}
}
而後啓動集羣,把配置了該監聽腳本的節點的nginx服務停掉,會發現此刻經過瀏覽器訪問會切到另一個節點上。說明配置完成,或者在nginx服務沒有啓動的時候去啓動 keepalived 服務,會發現該服務不管如何都是沒法啓動的,啓動後查看狀態一直會是 dead 的狀態。高可用方案到此結束。
OpenResty 是一個經過 Lua 擴展 Nginx 實現的可伸縮的 Web 平臺,內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用於方便地搭建可以處理超高併發、擴展性極高的動態 Web 應用、Web 服務和動態網關。
安裝:
1. 下載安裝包 https://openresty.org/cn/download.html
2. 安裝軟件包 tar -zxvf openresty-1.13.6.2.tar.gz。cd openrestry-1.13.6.2。./configure [默認會安裝在/usr/local/openresty 目錄] --prefix= 指定路徑。make && make install
3. 可能存在的錯誤,第三方依賴庫沒有安裝的狀況下會報錯 yum install readline-devel / pcre-devel /openssl-devel
安裝過程和 Nginx 是同樣的,由於他是基於 Nginx 作的擴展。開始第一個程序,HelloWorld cd /usr/local/openresty/nginx/conf 編輯 nginx 配置文件 nginx.conf
location / { default_type text/html; content_by_lua_block { ngx.say("helloworld"); } }
在 sbin 目錄下執行.nginx 命令就能夠運行,訪問能夠看到 helloworld。
創建工做空間:
爲了避免影響默認的安裝目錄,咱們能夠建立一個獨立的空間來練習,先到在安裝目錄下建立 demo 目錄,安裝目錄爲/usr/local/openresty .mkdir demo。而後在 demo 目錄下建立三個子目錄,一個是 logs 、一個是 conf,一個是 lua。
進接下去演示一個實現 API網關功能的簡單demo,而後提供一個算法去計算傳入參數a,b的和。入conf 建立配置文件 vim nginx.conf :
worker_processes 1; error_log logs/error.log; events { worker_connections 1024; } http { lua_package_path '$prefix/lua/?.lua'; //這裏的¥prefix在啓動的時候指定 lua_code_cache off; // lua腳本不須要從新加載 server { listen 80; // 正則匹配訪問路徑 location ~ ^/api/([-_a-zA-Z0-9]+) { // 請求過濾一下 access_by_lua_file lua/check.lua; content_by_lua_file lua/$1.lua; } } }
當來到的請求符合 ^/api/([-_a-zA-Z0-9/] 時 會在NGX_HTTP_CONTENT_PHASE HTTP請求內容階段 交給 lua/$1.lua來處理。好比:
/api/addition 交給 lua/addition.lua 處理。
/api/lua/substraction 交給 lua/substraction .lua 處理。
接下去建立 三個 lua 腳本:params.lua:
local _M ={} // 定義一個模塊 //定義一個方法 //該方法用於判斷參數是否爲數字 function _M.is_number(...) local args={...} local num; for i,v in ipairs(arg) do num=tonumber(v); if nil ==num then return false; end end return true; end //將該模塊返回出去 return _M;
寫一個用於起到網關過濾的檢查腳本check.lua:
//導入模塊
local param=require("params");
//獲取uri的參數 local args=ngx.req.get_uri_args();
//判斷a,b是否爲空且是否爲數字 if not args.a or not args.b or not param.is_number(args.a,args.b) then ngx.exit(ngx.HTTP_BAD_REQUEST); return; end
算法腳本 add.lua:
local args =ngx.req.get_uri_args(); ngx.say(args.a+args.b);
進入nginx的sbin目錄執行:./nginx -p /usr/local/openresty/demo 【-p 主要是指明 nginx 啓動時的配置目錄】,此時會提示一個警告信息,無需理會,有強迫症把對應配置關了就還了:nginx: [alert] lua_code_cache is off; this will hurt performance in /usr/local/openresty/demo/conf/nginx.conf:12。經過訪問http://192.168.254.137/api/add?a=1&b=3 能顯示最後的值:
庫文件使用:經過上面的案例,咱們基本上對 openresty 有了一個更深的認識,其中咱們用到了自定義的 lua 模塊。實際上 openresty 提供了很豐富的模塊。讓咱們在實現某些場景的時候更加方便。能夠在 /openresty/lualib 目錄下看到;好比在 resty 目錄下能夠看到 redis.lua、mysql.lua 這樣的操做 redis 和操做數據庫的模塊。更多的庫能夠去百度,或者查找相關書籍。
從一個房間到另外一個房間,必須必需要通過一扇門,一樣,從一個網絡向另外一個網絡發送信息,必須通過一道「關口」,這道關口就是網關。顧名思義,網關(Gateway)就是一個網絡鏈接到另外一個網絡的「關口」。那什麼是 api 網關呢?
在微服務流行起來以前,api 網關就一直存在,最主要的應用場景就是開放平臺,也就是 open api; 這種場景你們接觸的必定比較多,好比阿里的開放平臺;當微服務流行起來之後,api 網關就成了上層應用集成的標配組件.
爲何須要網關?
對微服務組件地址進行統一抽象,API 網關意味着你要把 API 網關放到你的微服務的最前端,而且要讓 API 網關變成由應用所發起的每一個請求的入口。這樣就能夠簡化客戶端實現和微服務應用程序之間的溝通方式.
當服務愈來愈多之後,咱們須要考慮一個問題,就是對某些服務進行安全校驗以及用戶身份校驗。甚至包括對流量進行控制。 咱們會對須要作流控、須要作身份認證的服務單獨提供認證功能,可是服務愈來愈多之後,會發現不少組件的校驗是重複的。這些東西很明顯不是每一個微服務組件須要去關心的事情。微服務組件只須要負責接收請求以及返回響應便可。能夠把身份認證、流控都放在 API 網關層進行控制。
在單一架構中,隨着代碼量和業務量不斷擴大,版本迭代會逐步變成一個很困難的事情,哪怕是一點小的修改,都必需要對整個應用從新部署。 可是在微服務中,各個模塊是是一個獨立運行的組件,版本迭代會很方便,影響面很小。同時,爲服務化的組件節點,對於咱們去實現灰度發佈(金絲雀發佈:將一部分流量引導到新的版本)來講,也會變的很簡單;因此經過 API 網關,能夠對指定調用的微服務版本,經過版原本隔離。以下圖所示
OpenResty 實現 API 網關限流及登陸受權
OpenResty 爲何能作網關?
前面咱們瞭解到了網關的做用,經過網關,能夠對 api 訪問的前置操做進行統一的管理,好比鑑權、限流、負載均衡、日誌收集、請求分片等。因此 API 網關的核心是全部客戶端對接後端服務以前,都須要統一接入網關,經過網關層將全部非業務功能進行處理。OpenResty 爲何能實現網關呢? OpenResty 有一個很是重要的因素是,對於每個請求,Openresty 會把請求分爲不一樣階段,從而可讓第三方模塊經過掛載行爲來實現不一樣階段的自定義行爲。而這樣的機制可以讓咱們很是方便的設計 api 網關。
Nginx 自己在處理一個用戶請求時,會按照不一樣的階段進行處理,總共會分爲 11個階段。而 openresty 的執行指令,就是在這 11 個步驟中掛載 lua 執行腳本實現擴展,咱們分別看看每一個指令的做用
init_by_lua : 當 Nginx master 進程加載 nginx 配置文件時會運行這段 lua 腳本,通常用來註冊全局變量或者預加載 lua 模塊。
init_woker_by_lua: 每一個 Nginx worker 進程啓動時會執行的 lua 腳本,能夠用來作健康檢查。
set_by_lua:設置一個變量。
rewrite_by_lua:在 rewrite 階段執行,爲每一個請求執行指定的 lua 腳本。
access_by_lua:爲每一個請求在訪問階段調用 lua 腳本。
content_by_lua:前面演示過,經過 lua 腳本生成 content 輸出給 http 響應。
balancer_by_lua:實現動態負載均衡,若是不是走 contentbylua,則走 proxy_pass,再經過 upstream 進行轉發。
header_filter_by_lua: 經過 lua 來設置 headers 或者 cookie。
body_filter_by_lua:對響應數據進行過濾。
log_by_lua : 在 log 階段執行的腳本,通常用來作數據統計,將請求數據傳輸到後端進行分析。
灰度發佈的實現:
1.跟前面同樣建立一個新的工做空間 mkdir gray。而後在 demo 目錄下建立三個子目錄,一個是 logs 、一個是 conf,一個是 lua。
2.編寫 Nginx 的配置文件 nginx.conf
worker_processes 1; error_log logs/error.log; events{ worker_connections 1024; } http{ lua_package_path "$prefix/lualib/?.lua;;"; lua_package_cpath "$prefix/lualib/?.so;;"; //生產環境 upstream prod {//tomcat地址 server 192.168.254.137:8080; } // 預生產環境 upstream pre {//tomcat地址 server 192.168.254.139:8080; } server { listen 80; server_name localhost; //當訪問該路徑就會進入lua腳本 location / { content_by_lua_file lua/gray.lua; } // 定義變量在lua中會使用 location @prod { proxy_pass http://prod; } location @pre { proxy_pass http://pre; } } }
3.編寫 gray.lua 文件
local redis=require "resty.redis"; local red=redis:new(); red:set_timeout(1000); local ok,err=red:connect("192.168.254.138",6379); if not ok then ngx.say("failed to connect redis",err); return; end ok, err = red:auth("wuzhenzhao"); local_ip=ngx.var.remote_addr; local ip_lists=red:get("gray"); if string.find(ip_lists,local_ip) == nil then ngx.exec("@prod"); else ngx.exec("@pre"); end local ok,err=red:close();
4.
1.進入sbin目錄 執行命令啓動 nginx: ./nginx -p /usr/local/openresty/gray
2. 啓動 redis,並設置 set gray 192.168.254.1,因爲我這邊是訪問虛擬機,因此我本地ip去訪問就是這個。
3. 經過瀏覽器運行: http://192.168.254.137/查看運行結果
修改 redis gray 的值, 到 redis 中 set gray 192.168.254.2. 再次運行結果,便可看到訪問結果已經發生了變化.