當前位置: 妍妍網 > 碼農

Nginx+Lua+Redis 實作灰度上線系統

2024-04-03碼農

原文連結:https://www.cnblogs.com/Jcloud/p/17910370.html

授人以魚不如授人以漁 .先學會用,在學原理,在學創造,可能一輩子用不到這種能力,但是不能不具備這種能力。這篇文章主要是沈澱使用nginx+lua+redis實作灰度,當我們具備了這種能力,隨時可以基於這種能力和思想調整實作方案:比如nginx+lua+(其他資料來源)、nginx+(其他手稿語言)

一、灰度方案:

常見的灰度實作方案:

  1. 請求路由:透過請求中的標識(如使用者ID、裝置ID、請求頭等)來決定是否將請求路由到灰度環境。可以使用反向代理(如Nginx、Envoy)或API閘道器(如Kong、Apigee)來實作路由規則。

  2. 權重控制:將流量按照一定的權重比例分配到不同的環境中。可以透過負載均衡器(如HAProxy、Kubernetes Ingress)或代理伺服器(如Nginx、Envoy)來實作權重控制。

  3. 特性開關:透過在程式碼中嵌入特性開關(Feature Flag)來控制功能的開啟與關閉。可以使用配置檔、資料庫、鍵值儲存或特性管理平台(如LaunchDarkly、Unleash)來管理特性開關。

  4. 分階段釋出:將功能的釋出分為多個階段,從內部測試到灰度環境再到全量釋出。可以使用部署工具(如Jenkins、GitLab CI/CD)或雲平台(如AWS、Azure)來支持分階段釋出。

  5. A/B測試:將流量分為多個不同版本的應用程式,比較它們的效能和使用者反饋。可以使用A/B測試平台(如Optimizely、Google Optimize)來管理和監控A/B測試。

  6. 金絲雀釋出:將新版本的應用程式逐步引入生產環境,僅將少量流量導向新版本,並根據其效能和穩定性逐步增加流量。可以使用部署工具、容器編排平台或雲平台來實作金絲雀釋出。

常用的灰度釋出方案:

  1. 基於使用者ID的灰度釋出:基於使用者ID來劃分灰度使用者或百分比灰度,例如根據使用者ID的哈希值或隨機數來決定使用者是否路由到灰度環境。

  2. 基於IP地址的灰度釋出:根據使用者的IP地址來劃分灰度使用者,例如將某一範圍的IP地址指定為灰度使用者,將請求從這些IP地址路由到灰度環境。

  3. Cookie/Session的灰度釋出:透過在使用者的Cookie或會話中設定特定的標識來劃分灰度使用者。例如,將特定的Cookie或會話變量設定為灰度標識,將具有該標識的請求路由到灰度環境。

  4. 請求頭的灰度釋出:基於請求頭中的特定標識來劃分灰度使用者。例如,根據請求頭中的自訂標識或特定的HTTP Header來路由請求到灰度環境。

  5. 權重或百分比的灰度釋出:將請求隨機分配給不同的環境,可以透過給不同環境設定不同的權重或百分比來控制流量的分配。

  6. A/B測試:將流量分為多個不同版本的應用程式,在實驗期間比較它們的效能和使用者反饋,最終選擇最佳版本進行全量釋出。

二、nginx+lua+redis實作灰度

理論:

1、安裝並配置 Nginx 和 Redis。確保 Nginx 啟用 Lua 模組,並可以存取 Redis。

2、在 Nginx 配置中定義灰度規則。您可以使用 Lua 指令碼來判斷使用者是否應該被路由到灰度環境。範例配置如下:

server {
listen80;
server_name example.com;
location / {
access_by_lua_block {
local redis = require"resty.redis"
local red = redis:new()
-- 連線到 Redis
local ok, err = red:connect("redis_host", redis_port)
ifnot ok then
ngx.log(ngx.ERR, "failed to connect to Redis: ", err)
ngx.exit(500)
end
-- 使用 Redis 根據使用者 ID 判斷是否路由到灰度環境
local user_id = ngx.req.get_headers()["X-User-ID"]
local is_gray = red:get("gray:" .. user_id)
if is_gray == "1" then
ngx.var.upstream = "gray_backend"
end
}
proxy_pass http://backend;
}
location /gray {
# 灰度環境的配置
proxy_pass http://gray_backend;
}
location /admin {
# 管理後台的配置
proxy_pass http://admin_backend;
}
}

在上面的範例中,我們連線到 Redis,並根據請求中的使用者 ID 判斷是否將請求路由到灰度環境。ngx.var.upstream 變量用於動態設定上遊地址,從而實作灰度環境的路由。

3、在 Redis 中設定灰度使用者。您可以在 Redis 中維護一個鍵值對,其中鍵是使用者 ID,值表示是否是灰度使用者(例如,1 表示是灰度使用者,0 表示不是)。您可以使用 Redis 的 SET 和 GET 命令來操作這些值。

-- 設定使用者為灰度使用者
local ok, err = red:set("gray:" .. user_id, 1)
ifnot ok then
ngx.log(ngx.ERR, "failed to set gray status for user: ", err)
ngx.exit(500)
end
-- 設定使用者為非灰度使用者
local ok, err = red:set("gray:" .. user_id, 0)
ifnot ok then
ngx.log(ngx.ERR, "failed to set gray status for user: ", err)
ngx.exit(500)
end

透過在 Redis 中設定使用者的灰度狀態,您可以動態地控制使用者是否應該被路由到灰度環境。

4、根據需要,配置其他路徑或功能的灰度規則。您可以根據需要在 Nginx 配置中添加其他路徑或功能的灰度規則,以實作更復雜的灰度釋出策略。

實踐:

這裏主要使用OpenResty

nginx+lua 實作灰度----主要使用OpenResty

OpenResty(又稱:ngx_openresty) 是一個基於 NGINX 的可伸縮的 Web 平台,OpenResty 是一個強大的 Web 套用伺服器,Web 開發人員可以使用 Lua 手稿語言調動 Nginx 支持的各種 C 以及 Lua 模組

openresty的api文件: https://www.kancloud.cn/qq13867685/openresty-api-cn/159190

1、根據post請求url參數匹配進行路由

nginx配置如下:

#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 '$time_local 客戶端地址:$remote_addr$remote_port 請求的URI和HTTP協定:$request 請求地址:$http_hostHTTP請求狀態:$status upstream狀態:$upstream_status 負載地址:$upstream_addr url跳轉來源:$http_referer$body_bytes_sent$http_user_agent$request_uri';
log_format logFormat '$group$time_local 客戶端:$remote_addr$remote_port 請求的URI和HTTP協定:$request 請求:$http_hostHTTP狀態:$status upstream狀態:$upstream_status 負載:$upstream_addr
url跳轉:$http_referer$body_bytes_sent$http_user_agent$request_uri 請求參數 $query_string$args$document_root$uri
-----$request_uri$request_filename$http_cookie';
access_log logs/access.log logFormat;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
server{
listen 80; #監聽埠
server_name 網域名稱; #監聽地址
access_log logs/xx.com.access.log logFormat;
location /hello {
default_type 'text/plain';
content_by_lua 'ngx.say("hello ,lua scripts")';
}
location /myip {
default_type 'text/plain';
content_by_lua '
clientIP = ngx.req.get_headers()["x_forwarded_for"]
ngx.say("Forwarded_IP:",clientIP)
if clientIP == nli then
clientIP = ngx.var.remote_addr
ngx.say("Remote_IP:",clientIP)
end
';
}
location / {
default_type 'text/plain';
lua_need_request_body on;
#content_by_lua_file /etc/nginx/lua/dep.lua;
#content_by_lua_file D:/sortware/openresty/openresty-1.17.8.2-win64/conf/dep.lua; # 指定由lua檔處理http請求
content_by_lua_file D:/user/Downloads/openresty-1.19.9.1-win64/conf/dep.lua; # 指定由lua檔處理http請求
}
location @default_version {
proxy_pass http://default;
proxy_set_header Host$http_host;
proxy_set_header X-Real-IP$remote_addr;
proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;
}
location @new_version {
proxy_pass http://new_version;
proxy_set_header Host$http_host;
#proxy_redirect default;
proxy_set_header X-Real-IP$remote_addr;
proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;
}
location @old_version {
proxy_pass http://old_version;
proxy_set_header Host$http_host;
proxy_set_header X-Real-IP$remote_addr;
proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;
}
}
#標準預發環境
upstream default {
server ip:port;
}
#預發2
upstream new_version {
server ip:port;
}
#預發3
upstream old_version {
server ip:port;
}
}

lua指令碼如下:

--get請求uri參數
function SaveTableContent(file, obj)
local szType = type(obj);
print(szType);
if szType == "number"then
file:write(obj);
elseif szType == "string"then
file:write(string.format("%q", obj));
elseif szType == "table"then
--把table的內容格式化寫入檔
--file:write("{\n");
for i, v inpairs(obj) do
SaveTableContent(file, i);
file:write(":");
SaveTableContent(file, v);
file:write(",");
end
--file:write("}\n");
else
error("can't serialize a "..szType);
end
end
function SaveTable(obj)
local file = io.open("D:\\user\\Downloads\\openresty-1.19.9.1-win64\\logs\\parmas.txt", "a");
assert(file);
SaveTableContent(file,obj);
file:close();
end
local request_method = ngx.var.request_method
local getargs = nil
local args = nil
local read_body = nil
local body_data = nil
local thirdPolicystatus = nil
if"GET" == request_method then
args = ngx.req.get_uri_args()
elseif"POST"== request_method then
getargs = ngx.req.get_uri_args()
args = ngx.req.get_post_args()
read_body = ngx.req.read_body()
body_data = ngx.req.get_body_data()
end
if getargs ~= nilthen
SaveTable(getargs);
thirdPolicystatus= getargs["thirdPolicystatus"];
if thirdPolicystatus ~= nilthen
SaveTable(thirdPolicystatus);
end
end
if args ~= nilthen
SaveTable(args);
end
if read_body ~= nilthen
SaveTable(read_body);
end
if body_data ~= nilthen
SaveTable(body_data);
end
if getargs ~= nilthen
thirdPolicystatus = getargs["thirdPolicystatus"]
if thirdPolicystatus ~= niland thirdPolicystatus == "1"then
SaveTable("new_version-getargs");
ngx.exec('@new_version')
elseif thirdPolicystatus ~= niland thirdPolicystatus == "2"then
SaveTable("old_version-getargs");
ngx.exec('@old_version')
else
SaveTable("default_version-getargs");
ngx.exec('@default_version')
end
end
if args ~= nilthen
iftype(args) == "table"then
thirdPolicystatus = tostring(args["thirdPolicystatus"])
if thirdPolicystatus ~= niland thirdPolicystatus == 1then
SaveTable("new_version-args-table");
ngx.exec('@new_version')
elseif thirdPolicystatus ~= niland thirdPolicystatus == 2then
SaveTable("old_version-args-table");
ngx.exec('@old_version')
else
SaveTable("default_version-args-table");
ngx.exec('@default_version')
end
elseiftype(args) == "string"then
local json = require("cjson")
local jsonObj = json.decode(args)
thirdPolicystatus = jsonObj['thirdPolicystatus']
if thirdPolicystatus ~= niland thirdPolicystatus == 1then
SaveTable("new_version-args-string");
ngx.exec('@new_version')
elseif thirdPolicystatus ~= niland thirdPolicystatus == 2then
SaveTable("old_version-args-string");
ngx.exec('@old_version')
else
SaveTable("default_version-args-string");
ngx.exec('@default_version')
end
end
end
return







host如下:

127.0.0.1 網域名稱

存取地址:

網域名稱

選單營運數據---保單數據,預設走default集群,保單狀態承保成功走new_version集群,保單狀態終止走old_version集群。

2、根據請求參數或ip等進行匹配redis緩存數據進行路由,靈活性更高。

redis下載地址:https://github.com/tporadowski/redis/releases

nginx配置如下:

#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 '$time_local 客戶端地址:$remote_addr$remote_port 請求的URI和HTTP協定:$request 請求地址:$http_hostHTTP請求狀態:$status upstream狀態:$upstream_status 負載地址:$upstream_addr url跳轉來源:$http_referer$body_bytes_sent$http_user_agent$request_uri';
log_format logFormat '$group$time_local 客戶端:$remote_addr$remote_port 請求的URI和HTTP協定:$request 請求:$http_hostHTTP狀態:$status upstream狀態:$upstream_status 負載:$upstream_addr
url跳轉:$http_referer$body_bytes_sent$http_user_agent$request_uri 請求參數 $query_string$args$document_root$uri
-----$request_uri$request_filename$http_cookie';
access_log logs/access.log logFormat;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
server{
listen 80; #監聽埠
server_name 網域名稱; #監聽地址
access_log logs/xx.com.access.log logFormat;
location /redis {
default_type 'text/plain';
content_by_lua 'ngx.say("hello ,lua scripts redis")';
}
location / {
default_type 'text/plain';
lua_need_request_body on;
content_by_lua_file D:/user/Downloads/openresty-1.19.9.1-win64/conf/redis.lua; # 指定由lua檔處理http請求
}
location @pre-prd {
proxy_pass http://pre-prd;
proxy_set_header Host$http_host;
#proxy_redirect default;
proxy_set_header X-Real-IP$remote_addr;
proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;
}
location @prd {
proxy_pass http://prd;
proxy_set_header Host$http_host;
proxy_set_header X-Real-IP$remote_addr;
proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;
}
}
#預發2演示線上
upstream prd {
server ip:port;
}
#預發演示預發線上
upstream pre-prd {
server ip:port;
}
}

lua指令碼如下:

--get請求uri參數
function SaveTableContent(file, obj)
local szType = type(obj);
print(szType);
if szType == "number"then
file:write(obj);
elseif szType == "string"then
file:write(string.format("%q", obj));
elseif szType == "table"then
--把table的內容格式化寫入檔
--file:write("{\n");
for i, v inpairs(obj) do
SaveTableContent(file, i);
file:write(":");
SaveTableContent(file, v);
file:write(",");
end
--file:write("}\n");
else
error("can't serialize a "..szType);
end
end
function SaveTable(obj)
--local file = io.open("D:\\user\\Downloads\\openresty-1.19.9.1-win64\\logs\\parmas.txt", "a");
local file = io.open("D:\\user\\Downloads\\openresty-1.19.9.1-win64\\logs\\redis.txt", "a");
assert(file);
SaveTableContent(file,obj);
file:close();
end

local request_method = ngx.var.request_method
local getargs = nil
local args = nil
local read_body = nil
local body_data = nil
local thirdPolicystatus = nil
if"GET" == request_method then
args = ngx.req.get_uri_args()
elseif"POST"== request_method then
getargs = ngx.req.get_uri_args()
args = ngx.req.get_post_args()
read_body = ngx.req.read_body()
body_data = ngx.req.get_body_data()
end
if getargs ~= nilthen
SaveTable("getargs");
SaveTable(getargs);
thirdPolicystatus= getargs["thirdPolicystatus"];
if thirdPolicystatus ~= nilthen
SaveTable("thirdPolicystatus");
SaveTable(thirdPolicystatus);
end
end
if args ~= nilthen
SaveTable("args");
SaveTable(args);
end
if read_body ~= nilthen
SaveTable("read_body");
SaveTable(read_body);
end
if body_data ~= nilthen
SaveTable("body_data");
SaveTable(body_data);
end

local redis = require"resty.redis"
local cache = redis.new()
cache:set_timeout(60000)
local ok, err = cache.connect(cache, '127.0.0.1', 6379)
ifnot ok then
SaveTable("not ok");
ngx.exec("@prd")
return
end
local local_ip = ngx.req.get_headers()["X-Real-IP"]
if local_ip == nilthen
local_ip = ngx.req.get_headers()["x_forwarded_for"]
SaveTable("local_ip1");
if local_id ~= nilthen
SaveTable(local_id);
end
end
if local_ip == nilthen
local_ip = ngx.var.remote_addr
SaveTable("local_ip2");
if local_id ~= nilthen
SaveTable(local_id);
end
end
-- 在 redis 中根據客戶端 ip 獲取是否存在值
local res, err = cache:get(local_ip)
-- 如果存在則轉發到 @pre-prd
if res == "1"then
SaveTable(res);
SaveTable("pre-prd");
ngx.exec("@pre-prd")
return
else
SaveTable("-------");
SaveTable(local_ip);
SaveTable(res);
cache:set(local_ip)
end
-- 如果不存在,則轉發到 @prd
SaveTable("prd");
ngx.exec("@prd")
local ok, err = cache:close()
ifnot ok then
ngx.say("failed to close:", err)
return
end
return











使用時這雷根據redis緩裏緩存的ip地址進行負載路由。

三、相關配置與語法

1、Nginx配置檔詳解

源碼:https://trac.nginx.org/nginx/browser

官網:http://www.nginx.org/

windows 安裝包下載地址:https://nginx.org/en/download.html

nginx.conf

########### 每個指令必須有分號結束。#################
# 全域塊 比如工作行程數,定義日誌路徑;
#配置使用者或者組,預設為nobody nobody。
#user nobody;
#user administrator administrators;
#允許生成的行程數,預設為1,一般建議設成CPU核數1-2倍
worker_processes 1;
#worker_processes 8;

#指定nginx行程執行檔存放地址
#pid /nginx/pid/nginx.pid;
#制定日誌路徑,級別。這個設定可以放入全域塊,http塊,server塊,級別依次為:#debug|info|notice|warn|error|crit|alert|emerg
error_log logs/error.log error;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#Events塊 設定處理輪詢事件模型,每個工作行程最大連線數及http層的keep-alive超時時間;
events {
#使用epoll的I/O 模型處理輪詢事件。
#可以不設定,nginx會根據作業系統選擇合適的模型
#事件驅動模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
#use epoll;
#工作行程的最大連線數量, 預設1024個
worker_connections 2048;
#設定網路連線序列化,防止驚群現象發生,預設為on
accept_mutex on;
#設定一個行程是否同時接受多個網路連線,預設為off
multi_accept on;
}
# http塊 路由匹配、靜態檔伺服器、反向代理、負載均衡等
http {
# 匯入副檔名與檔型別對映表 mime.types
include mime.types;
#預設檔型別,預設為text/plain
default_type application/octet-stream;
#取消服務日誌
#access_log off;
#日誌格式及access日誌路徑 自訂格式
log_format myFormat '$time_local 客戶端地址:$remote_addr–$remote_port 請求的URI和HTTP協定:$request 請求地址:$http_host HTTP請求狀態:$status upstream狀態:$upstream_status 負載地址:$upstream_addr url跳轉來源:$http_referer $upstream_addr $body_bytes_sent $http_user_agent';
#combined為日誌格式的預設值
access_log logs/access.log myFormat;
#允許sendfile方式傳輸檔,預設為off,可以在http塊,server塊,location塊。
sendfile on;
#sendfile開啟時才開啟。
tcp_nopush on;
server_names_hash_bucket_size 64;
#每個行程每次呼叫傳輸數量不能大於設定的值,預設為0,即不設上限。
sendfile_max_chunk 100k;
#連線超時時間,預設為75s,可以在http,server,location塊。
keepalive_timeout 65;
#--------------------靜態檔壓縮-----------------------------#
#Nginx可以對網站的css、js 、xml、html 檔在傳輸前進行壓縮,大幅提高頁面載入速度。經過Gzip壓縮後頁面大小可以變為原來的30%甚至更小。使用時僅需開啟Gzip壓縮功能即可。你可以在http全域塊或server塊增加這個配置。
# 開啟gzip壓縮功能
#gzip on;
gzip on;
# 設定允許壓縮的頁面最小字節數; 這裏表示如果檔小於10k,壓縮沒有意義.
gzip_min_length 10k;
# 設定壓縮比率,最小為1,處理速度快,傳輸速度慢;
# 9為最大壓縮比,處理速度慢,傳輸速度快; 推薦6
gzip_comp_level 6;
# 設定壓縮緩沖區大小,此處設定為16個8K記憶體作為壓縮結果緩沖
gzip_buffers 16 8k;
# 設定哪些檔需要壓縮,一般文本,css和js建議壓縮。圖片視需要要鎖。
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
#--------------------靜態檔壓縮-----------------------------#
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
#http server塊
server {
keepalive_requests 120; #單連線請求上限次數。
listen 8081; #監聽埠
server_name 網域名稱 #監聽地址
#ssi on;
#autoindex on;
charset utf-8;
client_max_body_size 10M; # 限制使用者上傳檔大小,預設1M
#access_log logs/host.access.log myFormat; #定義存取日誌,可以針對每一個server(即每一個站點)設定它們自己的存取日誌。
# 轉發動態請求到web套用伺服器
#location ^~ /api {
#rewrite ^/api/(.*)$ /$1 break;
#proxy_pass https://stream;
#break;#終止匹配
#}
location / {
# 使用proxy_pass轉發請求到透過upstream定義的一組套用伺服器
proxy_pass http://stream ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
}
location ~*^.+$ { #請求的url過濾,正則匹配,~為區分大小寫,~*為不區分大小寫。
proxy_pass http://stream ; #請求轉向stream 定義的伺服器列表
}
#location / {
#autoindex on;
#try_files $uri $uri/ /index.html?$args;
#}
# 規則1:通用匹配
#location / {
#ssi on;
#autoindex on; #自動顯示目錄
#autoindex_exact_size off; #人性化方式顯示檔大小否則以byte顯示
#autoindex_localtime on; #按伺服器時間顯示,否則以gmt時間顯示
#root /root; #定義伺服器的預設網站根目錄位置
#index index.html index.htm; #定義首頁索引檔的名稱 設定預設頁
# 使用proxy_pass轉發請求到透過upstream定義的一組套用伺服器
#proxy_pass http://mysvr; #負載配置
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#proxy_set_header Host $http_host;
#proxy_redirect off;
#proxy_set_header X-Real-IP $remote_addr;
#deny ip; # 拒絕的ip
#allow ip; # 允許的ip
#}
# 規則2:處理以/static/開頭的url
location ^~ /static {
alias /usr/share/nginx/html/static; # 靜態資源路徑
}
#= 精確匹配 1
#^~ 以某個字串開頭 2
#~ 區分大小寫的正則匹配 3
#~* 不區分大小寫的正則匹配 4
#!~ 區分大小寫的不匹配正則 5
#!~* 不區分大小寫的不匹配正則 6
#/ 通用匹配,任何請求都會匹配到 7
#location ~*^.+$ { #請求的url過濾,正則匹配,~為區分大小寫,~*為不區分大小寫。
#root path; #根目錄
#index vv.txt; #設定預設頁
#proxy_pass http://stream; #請求轉向stream 定義的伺服器列表
#deny 127.0.0.1; #拒絕的ip
#allow ip; #允許的ip
#}
#-----------------------------靜態檔緩存--------------------#
#緩存可以加快下次靜態檔載入速度。我們很多與網站樣式相關的檔比如css和js檔一般不怎麽變化,緩存有效器可以透過expires選項設定得長一些。
# 使用expires選項開啟靜態檔緩存,10天有效
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
root /var/www/big.server.com/static_files;
expires 10d;
}
#-----------------------------靜態檔緩存--------------------#
# 錯誤頁面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
#-------------$符號的全域變量含義--------------#
#$args, 請求中的參數;
#$content_length, HTTP請求資訊裏的"Content-Length";
#$content_type, 請求資訊裏的"Content-Type";
#$document_root, 針對當前請求的根路徑設定值;
#$document_uri, 與$uri相同;
#$host, 請求資訊中的"Host",如果請求中沒有Host行,則等於設定的伺服器名;
#$limit_rate, 對連線速率的限制;
#$request_method, 請求的方法,比如"GET"、"POST"等;
#$remote_addr, 客戶端地址;
#$remote_port, 客戶端埠號;
#$remote_user, 客戶端使用者名稱,認證用;
#$request_filename, 當前請求的檔路徑名
#$request_body_file,當前請求的檔
#$request_uri, 請求的URI,帶查詢字串;
#$query_string, 與$args相同;
#$scheme, 所用的協定,比如http或者是https,比如rewrite ^(.+)$
#$scheme://example.com$1 redirect;
#$server_protocol, 請求的協定版本,"HTTP/1.0"或"HTTP/1.1";
#$server_addr, 伺服器地址;
#$server_name, 請求到達的伺服器名;
#$server_port, 請求到達的伺服器埠號;
#$uri, 請求的URI,可能和最初的值有不同,比如經過重新導向之類的。
#-------------$符號的全域變量含義--------------#

#錯誤頁面
#error_page 404 https://www.baidu.com; #錯誤頁
#error_page 404 500 502 503 504 403 /error.shtml;
# 負載均衡
upstream insurance-pre {
#weigth參數列示權值,權值越高被分配到的機率越大
#--------------------負載均衡方式------------------#
#1.輪詢(預設)
#2.權重,weight越大,承擔任務越多
#server ip:port weight=5
#3.ip_hash
#ip_hash;
#4.url_hash
#hash $request_uri;
#5. fair(第三方)--按後端伺服器的響應時間來分配請求,響應時間短的優先分配。使用這個演算法需要安裝nginx-upstream-fair這個庫。
#fair;
#--------------------負載均衡方式------------------#
server ip:port weight=5; # weight越高,權重越大
server ip:port weight=1;
server ip:port weight=1;
server ip:port backup; # 熱備
}
# 轉發動態請求
#server {
#listen 80;
#server_name localhost;
#client_max_body_size 1024M;
#location / {
#proxy_pass http://localhost:8080;
#proxy_set_header Host $host:$server_port;
#}
#}
# http請求重新導向到https請求
#server {
#listen 80;
#server_name 網域名稱;
#return 301 https://$server_name$request_uri;
#}
server {
keepalive_requests 120; #單連線請求上限次數。
listen 80; #監聽埠
server_name 網域名稱 #監聽地址
#ssi on;
#autoindex on;
charset utf-8;
client_max_body_size 10M; # 限制使用者上傳檔大小,預設1M
#access_log logs/host.access.log myFormat; #定義存取日誌,可以針對每一個server(即每一個站點)設定它們自己的存取日誌。
# 轉發動態請求到web套用伺服器
#location ^~ /api {
#rewrite ^/api/(.*)$ /$1 break;
#proxy_pass https://網域名稱;
#break;#終止匹配
#}
location / {
# 使用proxy_pass轉發請求到透過upstream定義的一組套用伺服器
proxy_pass http://tomcat_gray1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
}
location ~*^.+$ { #請求的url過濾,正則匹配,~為區分大小寫,~*為不區分大小寫。
proxy_pass http://網域名稱; #請求轉向網域名稱 定義的伺服器列表
}
}
#標準預發環境
upstream tomcat_gray1 {
server ip;
server 網域名稱;
}
upstream tomcat_gray2 {
server 網域名稱;
}
}

















host 配置

127.0.0.1 網域名稱

瀏覽器存取 網域名稱

可以透過觀察access.log發現請求接入日誌。

2、lua基礎語法

教程:https://www.runoob.com/lua/if-else-statement-in-lua.html

lua的IDE編輯器:https://github.com/rjpcomputing/luaforwindows

3、nginx實作灰度

根據前端請求參數進行灰度到不同節點。

#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 '$time_local 客戶端地址:$remote_addr$remote_port 請求的URI和HTTP協定:$request 請求地址:$http_hostHTTP請求狀態:$status upstream狀態:$upstream_status 負載地址:$upstream_addr url跳轉來源:$http_referer$body_bytes_sent$http_user_agent$request_uri';
log_format logFormat '$group$time_local 客戶端:$remote_addr$remote_port 請求的URI和HTTP協定:$request 請求:$http_hostHTTP狀態:$status upstream狀態:$upstream_status 負載:$upstream_addr
url跳轉:$http_referer$body_bytes_sent$http_user_agent$request_uri 請求參數 $query_string$args$document_root$uri
-----$request_uri$request_filename$http_cookie';
access_log logs/access.log logFormat;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80; #監聽埠
server_name 網域名稱; #監聽地址
access_log logs/xx.com.access.log logFormat;
#方式二nginx+lua實作灰度
## 1將對localhost存取由/opt/app/lua/dep.lua進行處理
## 2根據邏輯處理後,決定回呼如下兩個其中1個內部跳轉
#方式三根據請求參數值匹配進行路由
#/policy/policyInfoList?thirdPolicystatus=2
set$group"default";
if ($query_string~*"thirdPolicystatus=1"){ #動態控制路由
set$group new_version;
}
if ($query_string~*"thirdPolicystatus=2"){
set$group old_version;
}
location /
{
default_type "text/html";
#content_by_lua_file D:/sortware/openresty/openresty-1.17.8.2-win64/conf/dep.lua; # 指定由lua檔處理http請求
proxy_pass http://$group;
proxy_set_header Host$host;
proxy_set_header X-Real-IP$remote_addr;
proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;
index index.html index.htm;
}
}
#標準預發環境
upstream default {
server ip:port;
}
#預發2
upstream new_version {
server ip:port;
}
#預發3
upstream old_version {
server ip:port;
}
}

host如下:

127.0.0.1 網域名稱

存取地址:

網域名稱

選單營運數據---保單數據,預設走default集群,保單狀態承保成功走new_version集群,保單狀態終止走old_version集群

根據cookie內的參數進行負載


#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 '$time_local 客戶端地址:$remote_addr$remote_port 請求的URI和HTTP協定:$request 請求地址:$http_hostHTTP請求狀態:$status upstream狀態:$upstream_status 負載地址:$upstream_addr url跳轉來源:$http_referer$body_bytes_sent$http_user_agent$request_uri';
log_format logFormat '$http_cookie$group$time_local 客戶端:$remote_addr$remote_port 請求的URI和HTTP協定:$request 請求:$http_hostHTTP狀態:$status upstream狀態:$upstream_status 負載:$upstream_addr
url跳轉:$http_referer$body_bytes_sent$http_user_agent$request_uri 請求參數 $query_string$args$document_root$uri
-----$request_uri$request_filename ';
access_log logs/access.log logFormat;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80; #監聽埠
server_name 網域名稱; #監聽地址
access_log logs/xx.com.access.log logFormat;
#方式二nginx+lua實作灰度
## 1將對localhost存取由/opt/app/lua/dep.lua進行處理
## 2根據邏輯處理後,決定回呼如下兩個其中1個內部跳轉
#方式三根據請求參數值匹配進行路由
#網域名稱policy/policyInfoList?thirdPolicystatus=2
set$group"default";
if ($query_string~*"thirdPolicystatus=1"){ #動態控制路由
set$group new_version;
}
if ($query_string~*"thirdPolicystatus=2"){
set$group old_version;
}
if ($http_cookie~*"sso.xx.com=BJ.E2C7D319112E7F6252BF010770269E235820211121073248"){
set$group pro_version;
}
if ($http_cookie~*"sso.xx.com!=BJ.E2C7D319112E7F6252BF010770269E235820211121073248"){
set$group grey_version;
}
location /
{
default_type "text/html";
#content_by_lua_file D:/sortware/openresty/openresty-1.17.8.2-win64/conf/dep.lua; # 指定由lua檔處理http請求
proxy_pass http://$group;
proxy_set_header Host$host;
proxy_set_header X-Real-IP$remote_addr;
proxy_set_header X-Forwarded-For$proxy_add_x_forwarded_for;
index index.html index.htm;
}
}
#標準預發環境
upstream default {
server ip:port;
}
#預發2
upstream new_version {
server ip:port;
}
#預發3
upstream old_version {
server ip:port;
}
#預發2
upstream pro_version {
server ip:port;
}
#預發3
upstream grey_version {
server ip:port;
}
}




根據cookie內容轉發到不同的集群

四、相關可操作和替換性

想法一:如果這個時候我門需要一個動態化配置控制台則可以透過javaweb等工程進行操作redis實作即時更新redis數據從而控制灰度

想法二:切換其他資料來源比如

  1. MySQL/MariaDB: 使用 Lua 的 lua-mysql 或 LuaSQL 庫,您可以在 Lua 中連線和查詢 MySQL 或 MariaDB 資料庫。

  2. PostgreSQL: 使用 Lua 的 lua-postgres 或 LuaSQL 庫,您可以在 Lua 中連線和查詢 PostgreSQL 資料庫。

  3. MongoDB: 使用 Lua 的 mongo-lua-driver 庫,您可以在 Lua 中連線和操作 MongoDB 資料庫。

  4. HTTP API: 使用 Lua 的 LuaHTTP 庫,您可以在 Lua 中發起 HTTP 請求,並與遠端的 HTTP API 進行通訊。

  5. Cassandra: 使用 Lua 的 lua-cassandra 庫,您可以在 Lua 中連線和查詢 Cassandra 資料庫。

想法三:切換其他手稿語言

  1. JavaScript: 透過使用 Nginx 的 ngx_http_js_module,您可以在 Nginx 中使用 JavaScript。這可以讓您使用 JavaScript 指令碼來實作一些灰度釋出或其他功能。此外,JavaScript 也廣泛用於前端開發,因此可以在前後端一體化的計畫中更容易共享程式碼邏輯。

  2. LuaJIT: LuaJIT 是一個透過即時編譯實作高效能的 Lua 直譯器。它提供了與標準 Lua 直譯器相容的 API,但是比標準 Lua 直譯器更快。使用 LuaJIT,您可以獲得更高的效能,同時保持與 Lua 的相容性。

  3. Python: 如果您已經熟悉 Python,您可以使用 Python-NGINX-Module 在 Nginx 中嵌入 Python。這樣可以使用 Python 編寫 Nginx 的配置檔和處理請求的邏輯。

  4. Java: 使用 nginx-jvm-clojure 或 nginx-jwt 等模組,您可以在 Nginx 中嵌入 Java 或 Clojure。這些模組提供了在 Nginx 上執行 Java 或 Clojure 程式碼的功能,可以與其他 Java 或 Clojure 庫和框架進行整合。

想法四:切換其他web伺服器或反向代理伺服器

  1. Apache HTTP Server: Apache 是一個廣泛使用的開源 Web 伺服器和反向代理伺服器,它支持多種模組和擴充套件,提供了豐富的功能和配置選項。

  2. Microsoft IIS: Internet Information Services (IIS) 是由 Microsoft 開發的 Web 伺服器,專為 Windows 作業系統設計。它是 Windows Server 預設的 Web 伺服器,提供了廣泛的功能和整合。

  3. Caddy: Caddy 是一個用 Go 編寫的現代化的 Web 伺服器和反向代理伺服器。它具有簡單配置、自動 HTTPS、HTTP/2 支持等特性。

  4. HAProxy: HAProxy 是一個高效能的負載均衡器和反向代理伺服器,適用於高流量的 Web 應用程式。它具有豐富的負載均衡和代理功能。

  5. Envoy: Envoy 是一個輕量級的開源代理伺服器和通訊匯流排,適用於雲原生和微服務架構。它具有動態配置、負載平衡、流量管理等功能。

往期推薦


點亮,伺服器三年不宕機