AbtestingGateway 复制请求到其他服务上

perface

最近有需求,需要把服务a发给服务b的请求复制给服务c,服务a发给服务b的时候会经过nginx,这个nginx是有lua脚本来辅助工作的。说白了,这个nginx+lua就是abtestingGateway。
架构图如下:
866977-20180703181219380-936483355.png

下面看看怎么实现abtestingGateway来复制流量。

分流条件

customercode和user字段的数据都在请求里的json数据里面。

  1. 匹配到customercode等于我们指定的值后把流量分到服务B。
  2. 在条件1的基础上,判断是否有users这个字段,有的话服务b,服务C同时发,否则只发服务B。
  3. 如果没有匹配到customercode,那么就转发到服务C上。
分流策略添加

这个请参考我们的另一篇博文 abtestingGateway 分流策略添加

编写复制请求的代码

我在abtestingGateway下,在customercode.lua(lib/abtesting/diversion目录下)文件里面添加代码

逻辑是这样的:
当abtestingGateway拿到customercode后,从redis里面拿取对应的upstream,如果upstream是服务b的upstream,那么就需要复制流量咯,反之。
所以我们在getUpstream里添加一段代码

_M.getUpstream = function(self, customercode)
    if not tostring(customercode) then
        return nil
    end
    
    local database, key = self.database, self.policyLib
    
    local backend, err = database:hget(key, customercode)
    if not backend then error{ERRORINFO.REDIS_ERROR, err} end
    
    if backend == ngx.null then backend = nil end
    
    -- 下面就是新添加的代码 begin
    if backend == "newacm" then   -- newacm就是我们配置的upstream名字
        copyRequest()
    end     
    -- end

    local new_acm_uri = ngx.var.new_acm_uri
    if new_acm_uri and backend then
        backend = backend..new_acm_uri
    end   
    return backend
end

匹配到指定的upstream后,那么就走copyRequest的方法了,代码如下:

_M.copyRequestToOldAcm = function(self)
    if  ngx.var.copy_switch == "on" then -- 分流开关,on为打开
        copyRequest()
    end
end

function copyRequest() -- 匹配到我们指定的customerCode以后,那么就复制一份请求发送到老的ACM上,原因是因为发送给老的acm就可新老一块同步数据了
        users_exist = false  -- 有这个users那么这个标志位为true,没有的话就是false
        action = ngx.var.request_method

        local postData = ngx.req.get_post_args()  --如果post请求里面没有这个customercode参数,
        if postData then  -- post 请求
            local errinfo =  ERRORINFO.UNKNOWN_ERROR
            if postData then
                for k, v in pairs(postData) do  --这个k为未json反序列化的数据,v为布尔值
                    local after_json_k = cjson.decode(k)  --提交上来的数据为json格式的,需要反序列化一下
                    if after_json_k then
                        for k1 ,v1 in pairs(after_json_k) do
                            if k1 == "customer" then
                               if v1.users then
                                   users_exist = true
                               end
                            end
                        end
                    end
                end
            end
        end

        if users_exist == true then   --没有匹配到users在post请求数据里,那么就不需要复制一份请求到老的的acm上了,直接return就行了。否则需要复制
            log:info("the request has users arguments ,so  needn't copy the request to the old acm!")
            return false
        else
            log:info("the request doesn't have users arguments, so need to copy the request to  the old acm!")           
        end

        if action == "POST" then
                --ngx.req.read_body() -- 解析 body 参数之前一定要先读取 body
                local arg = ngx.req.get_post_args() -- post需要参数传递
                arry = {method = ngx.HTTP_POST, body = ngx.var.request_body, args=arg}
        else
                arry = {method = ngx.HTTP_GET}
        end

        oldacm = ngx.location.capture_multi {
            { "/oldacm" .. ngx.var.request_uri, arry},
        }
        if oldacm.status == ngx.HTTP_OK then -- 只返回online server的结果
            -- ngx.say(online.body)
            log:info("copy request result is ok!!")
            return true    
        else
            -- ngx.status = ngx.HTTP_NOT_FOUND
            --for i,r in pairs(oldacm) do   end
            log:info("copy request result is failed!! the response body is -->"..oldacm.body)
            return false
        end     
                                                
end

然后我们在diversion/diversion.lua里面添加下这段代码,添加的这段代码起到这个作用的,abtestingGateway在运行的时候,只要匹配到了我们的规则,比如customercode=123456,匹配到了customercode=123456,那么abtestingGateway会从redis获取这个规则对应的upstream,然后把这个upstream放在nginx的内存里面,在接下来的60秒以内,只要匹配到规则就直接从内存里面拿取,不走getUpstream这个函数了,所以也触发不了复制请求的代码块了。这就是为什么我们还要添加下面这些代码:

 95 local copyRequest = function()
 96     local cm =    require("abtesting.diversion.customercode")
 97     local cr   = cm:copyRequestToOldAcm()
 98     return cr
 99 end
264                         local info = "get upstream ["..ups.."] according to ["
265                                                         ..idx.."] userinfo ["..usertable[idx].."] in cache 1"
266                         log:info(info)
267                         if string.find(ups,"newacm") == 1 then
268                             copyRequest()
269                         end

左边的数字是代码所处的行数,可以参考行数来添加代码。

最后我们需要在nginx的配置文件里面添加oldacm这个location,不然会提示404咯

    location /oldacm/ {
        proxy_pass http://stable/;  # 服务B ip
    }

然后重启nginx即可咯。测试,没啥问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值