AbtestingGateway 分流策略添加

马哲
2023-12-01

目录结构分布

我们从GitHub上把它下载后解压出来,有以下5个目录,分别是:

  1. admin 管理模块,对策略增删改查等功能
  2. diversion 主模块吧,看源码是匹配redis存储的key
  3. doc 文档
  4. lib 各个操作的子模块
  5. utils 专门存放nginx相关文件的

我这次更改的需求是 添加一个分流策略

从哪里开始入手

我们了解每个目录是干啥的后,我们开始从lib这个模块开始入手

我们切换到lib/abtesting/utils/ 下面,修改init.lua模块以下代码

init.lua

添加分流策略

_M.divtypes = {
    ["iprange"]     = 'ipParser',  
    ["uidrange"]    = 'uidParser',
    ["uidsuffix"]   = 'uidParser',
    ["uidappoint"]  = 'uidParser',
    ["arg_city"]    = 'cityParser', 
    ["url"]         = 'urlParser',
    ["domainname"]  = 'domainname',  --新添加域名策略  
} 

我在上面的代码divtypes里面添加了 ["domainname"] = 'domainname'这一行,这个主要是对分流策略做添加一个策略作用,因为当我们对abtestinggateway添加策略的时候,它会从这里匹配。

userinfo目录下面添加lua文件

获取我们需要的参数在分流的时候。

添加完策略名后,我们还需要添加一个模块,先切换到 lib/abtesting/userinfo/ 下面,然后添加一个和刚才策略名一样的lua文件,比如我刚才设置的是domainname,那么我在这下面也添加一个 domainname.lua 文件,文件名不一样的话,会报错,提示导入模块失败的。代码如下:

[root@LeoDevops gray_release_newtang]# cat lib/abtesting/userinfo/domainname.lua 
local _M = {
    _VERSION = '0.01'
}

_M.get = function()
    local h = ngx.var.host
    return h
end
return _M

上面这段代码主要是提取http的请求域名的功能,然后返回给调用者。

diversion目录下添加lua文件

编写分流逻辑。
我们上面两步做完后,还有一步是自己写一个分流规则,我们可以参考这个/lib/abtesting/diversion下面的其他策略文件,照着写就是了,文件名需要和策略名字一致。

[root@LeoDevops gray_release_newtang]# cat lib/abtesting/diversion/domainname.lua 
local modulename = "abtestingDiversionCustomercode"   -- 模块名记得更改

local _M    = {}
local mt    = { __index = _M }
_M._VERSION = "0.0.1"

local ERRORINFO = require('abtesting.error.errcode').info

local k_domainname      = 'domainname'
local k_domainname_set  = 'domainname_set'   --这个是key,后面对应的value就是我们要匹配的域名 比如{domainname_set:['t1.com','t2.com']}
local k_upstream        = 'upstream'

_M.new = function(self, database, policyLib)
    if not database then
        error{ERRORINFO.PARAMETER_NONE, 'need avaliable redis db'}
    end if not policyLib then
        error{ERRORINFO.PARAMETER_NONE, 'need avaliable policy lib'}
    end
    
    self.database = database
    self.policyLib = policyLib
    return setmetatable(self, mt)
end

local isNULL = function(v)
    return v and v ~= ngx.null
end

--  policy is in format as {{upstream = '192.132.23.125', domainname_set ={ "t1", "t2","t3"} }, {}}
_M.check = function(self, policy)
    for _, v in pairs(policy) do
        local domainname_set    = v[k_domainname_set]
        local upstream  = v[k_upstream]
        
        local v_domainname_set    = domainname_set and (type(domainname_set) == 'table')
        local v_upstream  = upstream and upstream ~= ngx.null
        
        if not v_domainname_set or not v_upstream then
            local info = ERRORINFO.POLICY_INVALID_ERROR 
            local desc = ' k_domainname_set or k_upstream error'
            return {false, info, desc}
        end
        
        for _, domainname in pairs(domainname_set) do 
            if not tostring(uid) then
                local info = ERRORINFO.POLICY_INVALID_ERROR 
                local desc = 'domainname invalid ,can not convert to string'
                return {false, info, desc}
            end
        end
        --TODO: need to check upstream alive
    end
    
    return {true}
end

--  policyData will be in hash table  domainname:upstream
_M.set = function(self, policy)
    local database  = self.database 
    local policyLib = self.policyLib
    
    database:init_pipeline()
    for _, v in pairs(policy) do
        local domainname_set   = v[k_domainname_set]
        local upstream = v[k_upstream] 
        for _, domainname in pairs(domainname_set) do
            database:hset(policyLib, domainname, upstream)
        end
    end
    local ok, err = database:commit_pipeline()
    if not ok then 
        error{ERRORINFO.REDIS_ERROR, err} 
    end
end

_M.get = function(self)
    local database  = self.database 
    local policyLib = self.policyLib
    
    local data, err = database:hgetall(policyLib)
    if not data then 
        error{ERRORINFO.REDIS_ERROR, err} 
    end

    return data
end

_M.getUpstream = function(self, domainname)
    if not tostring(domainname) then
        return nil
    end
    
    local database, key = self.database, self.policyLib
    
    local backend, err = database:hget(key, domainname)
    if not backend then error{ERRORINFO.REDIS_ERROR, err} end
    
    if backend == ngx.null then backend = nil end
    
    return backend
end

return _M

上面的就是我的域名分流 逻辑,如果我加入了其他分流策略,那么只需要把domainname,k_domainname,k_domainname_set这三个变量名更改成自己的就可以了。

测试

完成上面三步后,我们就可以测试了,

policy_check

[root@LeoDevops ~]# curl 'http://127.0.0.1:8080/ab_admin?action=policy_check' -d '{"divtype": "domainname","divdata": [{"domainname_set": ["t1.quanshi.com", "t2.quanshi.com"],"upstream": "beta1"}]}'
{"code":200,"desc":"success "}

语法检测OK,通过,那么就添加策略了吧。

policy_add

[root@LeoDevops ~]# curl 'http://127.0.0.1:8080/ab_admin?action=policy_set' -d '{"divtype": "domainname","divdata": [{"domainname_set":["t1.quanshi.com", "t2.quanshi.com"],"upstream": "beta1"}]}'
{"code":200,"desc":"success  the id of new policy is 0"}

策略添加成功,没有问题。

错误日志记录

这里使用的是nginx日志来记录日志,我们这里的也借助abtestingGateway来做日志记录。
我们切换到lib/abtesting/error/ 下面,编辑errorcode.lua 里面的代码

local modulename = 'abtestingErrorInfo'
local _M = {}

_M._VERSION = '0.0.1'

_M.info = {
    ... 省略N行
    --  show some args when posting
    ['SHOW_REQUEST_ARGS']       = { 50701, 'requst arguments --> '},
    ['SHOW_POST_ARGS']       = { 50702, 'POST arguments --> '},
}

return _M

上面两行代码是我添加的错误代码。用于显示参数信息等。临时打下这些信息方便编写代码。

打印日志
local args = ngx.req.get_uri_args()
local action = args.action
local do_action = ab_action[action]
local info = ERRORINFO.SHOW_REQUEST_ARGS
log:errlog(dolog(info,action ))   --记录http请求的参数,临时打开下,记录为error也可以,反正是临时打开下。
 类似资料: