社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  NGINX

nginx+lua+redis实现灰度发布

马哥Linux运维 • 3 月前 • 78 次点击  



前言:

授人以鱼不如授人以渔.先学会用,在学原理,在学创造,可能一辈子用不到这种能力,但是不能不具备这种能力。这篇文章主要是沉淀使用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 {
listen 80;
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)
if not 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)
if not 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)
if not 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_host HTTP请求状态:$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_host HTTP状态:$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 in pairs(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 ~= nil then
SaveTable(getargs);
thirdPolicystatus= getargs["thirdPolicystatus"];
if thirdPolicystatus ~= nil then
SaveTable(thirdPolicystatus);
end
end

if args ~= nil then
SaveTable(args);
end

if read_body ~= nil then
SaveTable(read_body);
end

if body_data ~= nil then
SaveTable(body_data);
end

if getargs ~= nil then
thirdPolicystatus = getargs["thirdPolicystatus"]
if thirdPolicystatus ~= nil and thirdPolicystatus == "1" then
SaveTable("new_version-getargs");
ngx.exec('@new_version')
elseif thirdPolicystatus ~= nil and thirdPolicystatus == "2" then
SaveTable("old_version-getargs");
ngx.exec('@old_version')
else
SaveTable("default_version-getargs");
ngx.exec('@default_version')
end
end

if args ~= nil then
if type(args) == "table" then
thirdPolicystatus = tostring(args["thirdPolicystatus"])
if thirdPolicystatus ~= nil and thirdPolicystatus == 1 then
SaveTable("new_version-args-table");
ngx.exec('@new_version')
elseif thirdPolicystatus ~= nil and thirdPolicystatus == 2 then
SaveTable("old_version-args-table");
ngx.exec('@old_version')
else
SaveTable("default_version-args-table");
ngx.exec('@default_version')
end
elseif type(args) == "string" then
local json = require("cjson")
local jsonObj = json.decode(args)
thirdPolicystatus = jsonObj['thirdPolicystatus']
if thirdPolicystatus ~= nil and thirdPolicystatus == 1 then
SaveTable("new_version-args-string");
ngx.exec('@new_version')
elseif thirdPolicystatus ~= nil and thirdPolicystatus == 2 then
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_host HTTP请求状态:$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_host HTTP状态:$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 in pairs(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 ~= nil then
SaveTable("getargs");
SaveTable(getargs);
thirdPolicystatus= getargs["thirdPolicystatus"];
if thirdPolicystatus ~= nil then
SaveTable("thirdPolicystatus");
SaveTable(thirdPolicystatus);
end
end

if args ~= nil then
SaveTable("args");
SaveTable(args);
end

if read_body ~= nil then
SaveTable("read_body");
SaveTable(read_body);
end

if body_data ~= nil then
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)
if not ok then
SaveTable("not ok");
ngx.exec("@prd")
return
end

local local_ip = ngx.req.get_headers()["X-Real-IP"]
if local_ip == nil then
local_ip = ngx.req.get_headers()["x_forwarded_for"]
SaveTable("local_ip1");
if local_id ~= nil then
SaveTable(local_id);
end
end

if local_ip == nil then
local_ip = ngx.var.remote_addr
SaveTable("local_ip2");
if local_id ~= nil then
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()
if not 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_host HTTP请求状态:$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_host HTTP状态:$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_host HTTP请求状态:$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_host HTTP状态:$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 是一个轻量级的开源代理服务器和通信总线,适用于云原生和微服务架构。它具有动态配置、负载平衡、流量管理等功能。

大家可以根据自己的想法或者兴趣进行研究,本文不做过多介绍

链接:https://www.cnblogs.com/Jcloud/p/17910370.html

(版权归原作者所有,侵删)

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/166827
 
78 次点击