Nginx+lua+redis构建灰度服务(cookie)

来自Linux78|wiki

本系统采用nginx+lua+redis来实现灰度系统的动态路由控制。

1.采集灰度用户放入redis中

2.根据用户的cookie值反解析用户信息和之前存储的目标灰度用户进行对比。如果存在则请求灰度服务,否则请求正常服务。

nginx配置信息:

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;
   lua_package_path '/webapp/openresty/nginx/conf/?.lua;;';
   sendfile        on;
   #tcp_nopush     on;
   #keepalive_timeout  0;
   keepalive_timeout  65;
   #gzip  on;
    upstream client1 {
    server  127.0.0.1:8081 weight=1;
   }
    upstream client2{
    server  127.0.0.1:8082 weight=1;
   }
   server {
       listen       8080;
       server_name  localhost;
       #charset koi8-r;
       #access_log  logs/host.access.log  main;
       location / {
           root   html;
           index  index.html index.htm;
       }
       #前端请求到这里,解析并执行lua脚本
     location /hnmccClient {
       default_type text/plain; 
           content_by_lua_file /webapp/nginx/conf/waf/test.lua; 
           root   /;
       }
    location @client1{
      proxy_next_upstream    error timeout;
       proxy_redirect          off;
       proxy_set_header        Host $host;
       #proxy_set_header        X-Real-IP $remote_addr;
       proxy_set_header        X-Real-IP $http_x_forwarded_for;
       proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
       client_max_body_size    100m;
       client_body_buffer_size 256k;
       proxy_connect_timeout  180;
       proxy_send_timeout      180;
       proxy_read_timeout      180;
       proxy_buffer_size      8k;
       proxy_buffers          8 64k;
       proxy_busy_buffers_size 128k;
       proxy_temp_file_write_size 128k;
          proxy_pass http://client1;
      }
      location client2{
           proxy_next_upstream    error timeout;
           proxy_redirect          off;
           proxy_set_header        Host $host;
           #proxy_set_header        X-Real-IP $remote_addr;
           proxy_set_header        X-Real-IP $http_x_forwarded_for;
           proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
           client_max_body_size    100m;
           client_body_buffer_size 256k;
           proxy_connect_timeout  180;
           proxy_send_timeout      180;
           proxy_read_timeout      180;
           proxy_buffer_size      8k;
           proxy_buffers          8 64k;
           proxy_busy_buffers_size 128k;
           proxy_temp_file_write_size 128k;
          proxy_pass http://client2;
      }

具体的lua脚本如下:

local userCookie = ngx.var.cookie_hncmjsSSOCookie
if(userCookie==nil or userCookie=="") then
 ngx.exec("@client1")
 return
end
local resultCookie = (string.gsub(userCookie,"@hn.ac.10086.cn",""))
if(resultCookie==nil or resultCookie=="") then
ngx.exec("@client1")
return
end
--引入nginx-redis模块
local redis = require "resty.redis"
--初始化redis客户端1
local red = redis:new()
--设置超时时间300ms
red:set_timeout(300) 
--建立连接
local ok, err = red:connect("127.0.0.1", 6380)
--如果建立连接失败,则请求正常服务
if not ok then
   ngx.exec("@client1")
   return
end
--通过cookie获取用户信息
local cookie = (string.gsub(resultCookie,"\"",""))
local userInfo = red:get(cookie)
local foo = require "foo"
local userInfoArr = foo.split(userInfo,"|")
--解析用户信息里面的手机号
local reqMobile = userInfoArr[10]
--如果手机号为空,则请求正常服务
if(reqMobile==nil or reqMobile=="") then
   ngx.exec("@client1")
   return
end
--初始化redis客户端2
local red2 = redis:new()
--设置超时时间300ms
red2:set_timeout(300) 
--建立连接
local ok2, err2 = red2:connect("127.0.0.1", 6380)
--如果建立连接失败,则请求正常服务
if not ok2 then
   ngx.exec("@client1")
   return
end
--判断当前手机号是否在灰度目标群体中,如果存在,则请求灰度服务,否则请求正常服务
local flag = red2:sismember("AB_TEST",reqMobile)
if(flag == 1) then
   ngx.exec("@client2")
   return
end
ngx.exec("@client1")

上面用到了一个字符串拆分函数:

local _M = {}
function _M.split(s, delim)
   if type(delim) ~= "string" or string.len(delim) <= 0 then
       return
   end
   local start = 1
   local t = {}
   while true do
   local pos = string.find (s, delim, start, true) -- plain find
       if not pos then
         break
       end
       table.insert (t, string.sub (s, start, pos - 1))
       start = pos + string.len (delim)
   end
   table.insert (t, string.sub (s, start))
   return t
end
return _M