nginx+lua+redis实现灰度发布 | 京东云技术团队
前言:
授人以鱼不如授人以渔.先学会用,在学原理,在学创造,可能一辈子用不到这种能力,但是不能不具备这种能力。这篇文章主要是沉淀使用nginx+lua+redis实现灰度,当我们具备了这种能力,随时可以基于这种能力和思想调整实现方案:比如nginx+lua+(其他数据源)、nginx+(其他脚本语言)
一、灰度方案:
常见的灰度实现方案:
常用的灰度发布方案:
二、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数据从而控制灰度
想法二:切换其他数据源比如
想法三:切换其他脚本语言
想法四:切换其他web服务器或反向代理服务器
大家可以根据自己的想法或者兴趣进行研究,本文不做过多介绍
作者:京东健康 马仁喜
来源:京东云开发者社区 转载请注明来源

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
.NET8 AOT与LLVM什么关系?
前言 .NET8的AOT为了支持各个平台,比如MacOS,Linux等平台,ILC(AOT Compiler)的目标文件用的是LLVM生成的。本篇看下。 原文:.NET8 AOT与LLVM什么关系? 公众号:jianghuht,欢迎关注 详述 为了更为细致的让大家了解LLVM在AOT里面的作用,这里用一个例子演示下。以下以最新版的.NET8.0/windows11为蓝本演示。 第一步:在nuget上面下载一个ILC组件,名称:runtime.win-x64.Microsoft.DotNet.ILCompiler 第二步:找到这个包在你当前电脑的路径,一般为如下,这个路径有个objwriter.dll: C:\Users\Administrator\.nuget\packages\runtime.win-x64.microsoft.dotnet.ilcompiler\8.0.0\tools 第三步:操作objwriter.dll 新建一个.NET8控制台应用程序,把这个objwriter.dll放入到控制台bin/x64/debug/net8.0目录下。Main写入如下代码: [DllI...
- 下一篇
记一次生产慢sql索引优化及思考 | 京东云技术团队
一 问题重现 夜黑风高的某一晚,突然收到一条运营后台数据库慢sql的报警,耗时竟然达到了60s。 看了一下,还好不是很频繁,内心会更加从容排查问题,应该是特定条件下没有走到索引导致,如果频繁出现慢查询,可能会将数据库连接池打满,导致数据库不可用,从而导致应用不可用。 二 问题排查 报警自带定位慢sql语句,这个是很早就上线的一条sql语句,下面对sql语句进行了简化: select * from xxx where gear_id=xxx and examine=xxx order by id desc limit 10,这是个简单的根据流量池gear_id查询,按照主键id倒序进行分页查询10条数据的语句。 在examine=2时查询速度很快,但是在examine=3时,查询速度极慢,然后分别在不同的examine下查看执行计划,得到的执行计划都是一致的。 查看执行计划,发现possible_keys中有idx_gear_id索引,但是实际用到的key却是PRIMARY,并且extra中明确用了where条件进行数据过滤。到现在就明白了这个sql是在主键聚簇索引上进行扫描,...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Red5直播服务器,属于Java语言的直播服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7