基于nginx-redis 前端灰度

基于nginx-redis 前端灰度

前提

项目采用的是前后端分离,前端使用vue 后端使用spring cloud。单点采用cas。本文暂且讨论前端基于cookie-token的灰度。

目前实现的方式有三种

  • nginx+lua:根据访问者ip地址区分,由于公司出口是一个ip地址,会出现访问网站要么都是老版,要么都是新版,采用这种方式并不适合;
  • nginx:根据cookie分流,灰度发布基于用户才更合理(本例子采用该种方式)。

整体思路:

1.首先用户经过cas登录后访问vue前端资源会携带access_token。
2.在lua脚本连接redis服务器,验证token是否在灰度列表和清单中。
3.token灰度验证通过,交由nginx服务器进行反向代理服务到灰度服务器;

准备

安装浏览器cookie插件 editthiscookie
在这里插入图片描述
nginx lua 环境安装

  mkdir /usr/local/openresty/gray
    
    mkdir /usr/local/openresty/gray/conf
    mkdir /usr/local/openresty/gray/logs
    mkdir /usr/local/openresty/gray/lua

nginx.conf

    user root;
    worker_processes 1;
    error_log /usr/local/openresty/gray/logs/error.log;
    
    events {
        worker_connections 1024;
    }
    
    http {
    
      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  /usr/local/openresty/gray/logs/access.log  main;
    
      #添加;;标识默认路径下的lualib
      lua_package_path "$prefix/lualib/?.lua;;";
      lua_package_cpath "$prefix/lualib/?.so;;";
    
      upstream prod1 {
        server 47.114.161.16:80;
      }
    
      upstream prod2 {
        server 47.114.50.9:80;
      }
    
      server {
        listen 8056;
        server_name localhost;
        location / {      #为每个请求执行gray.lua脚本
          content_by_lua_file /usr/local/openresty/gray/lua/gray.lua;
        }
    
        location @prod1 {
          proxy_pass http://prod1;
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    
        location @prod2 {
          proxy_pass http://prod2;
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        
      }
    }

gray.lua

    local cjson = require("cjson")
    --引入nginx-redis模块
    local redis=require "resty.redis";
    --初始化redis客户端
    local red=redis:new();
    --设置超时时间1000ms
    red:set_timeout(1000);
    --redis建立连接
    local ok,err=red:connect("39.100.254.100", 6379);
    --如果建立连接失败,则请求正常服务
    if not ok then
      ngx.say("failed to connect redis ",err);
      return;
    end
    
    -- 请注意这里 auth 的调用过程 这是redis设置密码的
    local count
    count, err = red:get_reused_times()
    if 0 == count then
        --密码
        ok, err = red:auth("2019")
        if not ok then
             ngx.say(cjson.encode({code = 500,message = err}))
            return
        end
    elseif err then
        	ngx.say("failed to get reused times: ", err, "<br/>")
        return
    end
    
    --选择的桶
    red:select(8)
    
    --获取请求ip
    local local_ip = ngx.req.get_headers()["X-Real-IP"];
    if local_ip == nil then
       local_ip = ngx.req.get_headers()["x_forwarded_for"];
    end
    if local_ip == nil then
       local_ip = ngx.var.remote_addr;
    end
    
    local_ip=ngx.var.remote_addr;
    --redis中获取白名单
    local ip_lists=red:get("gray");
    
    --ngx.say("ip_list" .. ip_lists);
    
    --判断是否在白名单然后转到对应服务
    if string.find(ip_lists,local_ip) == nil then
      --一定要注意,当这里ngx.exec()的时候,前面一定不能用任何的输出
      ngx.exec("@prod1");
      --ngx.say("prod1");
    else
      --一定要注意,当这里ngx.exec()的时候,前面一定不能用任何的输出
      ngx.exec("@prod2");
      --ngx.say("prod2");
    end
    
    local ok,err=red:close();

启动

    /usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/gray/conf/nginx.conf

停止

    /usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/gray/conf/nginx.conf -s stop

备注

脚本还在根据实际项目调整中,还未更加cookie中的access_token 来进行灰度。

local cjson = require("cjson")
local redis=require "resty.redis";

local red=redis:new();

--ngx.log(ngx.INFO, " msg:", "helloworld")

red:set_timeout(1000);
--redis连接
local ok,err=red:connect("39.100.254.140", 6379);

if not ok then
  ngx.say("failed to connect redis ",err);
  return;
end


-- 请注意这里 auth 的调用过程 这是redis设置密码的
local count
count, err = red:get_reused_times()
if 0 == count then
    ok, err = red:auth("portal2019")
    if not ok then
         ngx.say(cjson.encode({code = 500,message = err}))
        return
    end
elseif err then
    	ngx.say("failed to get reused times: ", err, "<br/>")
    return
end

--密码和选择的桶
red:select(8)

--accessToken 

function get_cookie(s_cookie, key)
    local value = nil
    -- string.gfind is renamed to string.gmatch
    for item in string.gmatch(s_cookie, "[^;]+") do
        local _, _, k, v = string.find(item, "^%s*(%S+)%s*=%s*(%S+)%s*")
        if k ~= nil and v~= nil and key ==k then
            return v
        end
    end
    return value
end

local isNULL = function(v)
    return not v or v == ngx.null
end

local raw_cookie = ngx.req.get_headers()["Cookie"]
local accessToken = get_cookie(raw_cookie, "accessToken");
--ngx.say("accessToken :" .. accessToken)

-- 当accessToken不存在时候默认路由
if accessToken == nil then
 --ngx.say("prod1");
 ngx.exec("@prod1");
 local ok,err=red:close();
 return;
end

--redis中获取白名单
local rdsAccessToken=red:get("gray:token:" .. accessToken);
--ngx.say("rdsAccessToken:", cjson.encode(rdsAccessToken));

--判断是否在白名单然后转到对应服务
if isNULL(rdsAccessToken) then
  --一定要注意,当这里ngx.exec()的时候,前面一定不能用任何的输出
  ngx.exec("@prod1");
  --ngx.say("prod1");
else
  --一定要注意,当这里ngx.exec()的时候,前面一定不能用任何的输出
  ngx.exec("@prod2");
  --ngx.say("prod2");
end

local ok,err=red:close();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值