Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何在网站里加入自定义的'X-Uid:30'请求头? #3

Closed
Michael2008S opened this issue Aug 31, 2015 · 16 comments
Closed

如何在网站里加入自定义的'X-Uid:30'请求头? #3

Michael2008S opened this issue Aug 31, 2015 · 16 comments

Comments

@Michael2008S
Copy link
Contributor

我用的是golang开发的后端服务器,现在实例里面都是通过 curl 请求的测试是可以通过的,但是不知道怎么添加自定义的请求头?
有什么方法可以为网页添加自定义的请求头?

@BG2BKK
Copy link
Contributor

BG2BKK commented Aug 31, 2015

这里golang开发的后端服务器指的是否是upstream server,而proxy server依然使用的是nginx?能否具体说下,为网页添加自定义请求头是什么意思呢?

@Michael2008S
Copy link
Contributor Author

proxy server 就是你提供的ABTestingGateway .upstream server 就是我这边用golang开发的app server.
现在我的情况是 我想用灰度发布来做内部的测试,访问的都是www.子域名下面的网址.
现在已经设置了这个规则:

curl http://127.0.0.1:8030/admin/policy/set -d '{"divtype":"uidappoint","divdata":[{"uidset":["1024"], "upstream":"beta1"}]}'

运行:

curl 127.0.0.1:8030/  -H 'X-Uid:1024'

可以分流到beta1 的app server.

现在我的问题是: 这里是通过 curl -H 'X-Uid:1024' 这个请求头进行分流的. 但是我有什么方法设置浏览器里网页的请求头? 来达到分流的目的?

@BG2BKK
Copy link
Contributor

BG2BKK commented Sep 1, 2015

你好。默认情况下浏览器不会发送多余的header,除非是cookie。如果想通过浏览器网页发送请求头,可以通过javascript实现,其他定制的客户端,比如curl、自己写的client或者手机客户端,都可以编程实现。

如果你是想压测或者什么的,可以使用wrk,wrk可以通过内嵌的lua脚本向请求添加 header。

@Michael2008S
Copy link
Contributor Author

好的,谢谢,我昨晚稍改了下lua的代码,现在加了个uid的cookie.这样应该可以去实现分流.
不过就是不知道使用cookie做分流不知道会不会对性能有影响.

  _M.get = function()
    -- local u = ngx.req.get_headers()["X-Uid"]
    ngx.log(ngx.ERR,"X-Uid")
    ngx.log(ngx.ERR,ngx.req.get_headers()["X-Uid"])
    ngx.log(ngx.ERR,"cookie-uid")
    ngx.log(ngx.ERR,ngx.var.cookie_uid)
    u = ngx.var.cookie_uid
    return u
 end

@BG2BKK
Copy link
Contributor

BG2BKK commented Sep 1, 2015

cookie可以被认为一种特殊的header。
从header和cookie在ngx_lua的实现来看,没有太大区别。不过具体的性能差异我们没有测试。
另说一句,这种ngx.var.VARIABLE的写法,每次调用都会有内存分配,最好的办法是先用lua变量缓存起来,比如local foo = ngx.var.foo,对性能有一定好处。
https://groups.google.com/forum/#!topic/openresty/8CmuW5LTqTo

@Michael2008S
Copy link
Contributor Author

不过我修改了代码之后遇到另外一个问题:
设置的策略:

curl http://www.xxxx.com/admin/policy/set -d '{"divtype":"uidappoint","divdata":[{"uidset":["1"], "upstream":"beta1"}]}'

修改的代码:

  _M.get = function()
    -- local u = ngx.req.get_headers()["X-Uid"]
    -- ngx.log(ngx.ERR,"X-Uid")
    -- ngx.log(ngx.ERR,ngx.req.get_headers()["X-Uid"])
    -- ngx.log(ngx.ERR,"cookie-uid")
    -- ngx.log(ngx.ERR,ngx.var.cookie_uid)
    local  u = ngx.var.cookie_uid         ------`还有就是local 是加到这里可以避免你上面说的内存问题吗?`     
    return u
 end

遇到的问题:

2015/09/01 17:45:08 [error] 23001#0: *33262 lua entry thread aborted: runtime error: ...t/soft/ABTestingGateway/utils/../diversion/diversion.lua:238: bad argument #1 to '__newindex' (string, number, or nil expected, but got user data)
stack traceback:
coroutine 0:
        [C]: in function '__newindex'
        ...t/soft/ABTestingGateway/utils/../diversion/diversion.lua:238: in function <...t/soft/ABTestingGateway/utils/../diversion/diversion.lua:1>, client: xxx.xxx.xxx.xxx, server: www.XXXXX.com, request: "GET / HTTP/1.1", host: "www.XXXXX.com"

不知道什么原因造成的?

这个问题导致只有设置的uid才能访问,其他都返回 500 Internal Server Error .

@BG2BKK
Copy link
Contributor

BG2BKK commented Sep 1, 2015

你好。
首先你采用的策略是uidappoint的,我加入策略并设置为运行时策略后

$ curl http://127.0.0.1:8030/admin/policy/set -d '{"divtype":"uidappoint","divdata":[{"uidset":["1"], "upstream":"beta1"}]}'
{"errcode":200,"errinfo":"success  the id of new policy is 1"}

$ curl http://127.0.0.1:8030/admin/runtime/set?policyid=1
{"errcode":200,"errinfo":"success "}

按照你提供的代码,修改lib/abtesting/userinfo/uidParser.lua内容为:

local _M = {
    _VERSION = '0.01'
}

--_M.get = function()
--  local u = ngx.req.get_headers()["X-Uid"]
--  return u
--end
--
_M.get = function()
    -- local u = ngx.req.get_headers()["X-Uid"]
    -- ngx.log(ngx.ERR,"X-Uid")
    -- ngx.log(ngx.ERR,ngx.req.get_headers()["X-Uid"])
    -- ngx.log(ngx.ERR,"cookie-uid")
    -- ngx.log(ngx.ERR,ngx.var.cookie_uid)
    local  u = ngx.var.cookie_uid         ------`还有就是local 是加到这里可以避免你上面说的内存问题吗?`     
    return u
end
return _M

检验结果

$ curl 127.0.0.1:8030/ --cookie 'uid=1'
this is beta1 server

说明是正常运行的。

然后,根据你的错误提示,

2015/09/01 17:45:08 [error] 23001#0: *33262 lua entry thread aborted: runtime error: ...t/soft/ABTestingGateway/utils/../diversion/diversion.lua:238: bad argument #1 to '__newindex' (string, number, or nil expected, but got user data)

diversion/diversion.lua的第238行左右代码是

if upstream then
    ngx.var.backend = upstream    --line 238
else
    upstream = default_backend
end

再结合是__newindex方法错误(__newindex是修改table内容的lua metamethod,__index是访问table内容的lua metamethod,这里ngx.var可以看做是一个table,ngx.var.backend和ngx.var['backend']是一个东西),你应该是将一个这个upstream变量获取错了,你获取成了user data类型,而ngx.var.backend需要一个普通类型,比如number、string什么的。所以首先应该检查的是在238行之前,就是getUpstream函数的返回值,可能是user data类型的(提示:lua-resty-redis读取到的ngx.null就是一个user data,极有可能是他。ngx.null)。

从根源上来说,用user data类型对ngx.var.backend进行赋值的错误原因可以在ngx_lua源码看到,在ngx_lua/src/ngx_http_lua_variable.c中

static int
ngx_http_lua_var_set(lua_State *L)
{
...
    /* we read the variable new value */

    value_type = lua_type(L, 3);
    switch (value_type) {
    case LUA_TNUMBER:
    case LUA_TSTRING:
        p = (u_char *) luaL_checklstring(L, 3, &len);

        val = ngx_palloc(r->pool, len);
        if (val == NULL) {
            return luaL_error(L, "memory allocation erorr");
        }

        ngx_memcpy(val, p, len);

        break;

    case LUA_TNIL:
        /* undef the variable */

        val = NULL;
        len = 0;

        break;

    default:
        msg = lua_pushfstring(L, "string, number, or nil expected, "
                              "but got %s", lua_typename(L, value_type));
        return luaL_argerror(L, 1, msg);
    }
...
}

当右值不是number、string或nil时,比如是table或者user data,会报错:

string, number, or nil expected, but got %s    / type

而且错误信息就是luaL_argerror函数打印的。

2015/09/01 17:45:08 [error] 23001#0: *33262 lua entry thread aborted: runtime error: ...t/soft/ABTestingGateway/utils/../diversion/diversion.lua:238: bad argument #1 to '__newindex' (string, number, or nil expected, but got user data)

最后

local  u = ngx.var.cookie_uid         ------`还有就是local 是加到这里可以避免你上面说的内存问题吗?` 

答案是肯定的,这相当于只读取cookie_uid一次,缓存在lua虚拟机中,下次就不用再重新分配内容并读取数据了,直接从lua虚拟机读就好。

@Michael2008S
Copy link
Contributor Author

谢谢您这么详细的分析和解答.
其实原来的代码我没怎么改过,就只是判断head 改成cookie.就出现上面的情况.
经过你的提示 ,我做了一下判断修改就可以了. 判断 upstream 是不是 ngx.null

原来diversion/diversion.lua的第238行

if upstream then                                          --238
    ngx.var.backend = upstream
else
    upstream = default_backend
end

修改后的diversion/diversion.lua的第238行

if upstream ~= ngx.null and upstream then   --238
    ngx.var.backend = upstream
else
    upstream = default_backend
end

@BG2BKK
Copy link
Contributor

BG2BKK commented Sep 3, 2015

不客气,最应该感谢你的使用和反馈,一个活的project必须是经过多人使用和检验的。
然后仍需指出的是,使用uidappoint分流时,getUpstream的代码为:

_M.getUpstream = function(self, uid)
    if not tonumber(uid) then
        return nil
    end

    local database, key = self.database, self.policyLib

    local backend, err = database:hget(key, uid)
    if not backend then error{ERRORINFO.REDIS_ERROR, err} end

    if backend == ngx.nul then backend = nil end

    return backend
end

请注意返回backend时我已经判断过是否为ngx.null的问题,所以我现在很好奇你的问题根源,能提供更多的信息或者最小实现吗?

@Michael2008S
Copy link
Contributor Author

看了这段代码,我好像发现问题在哪里了:

_M.getUpstream = function(self, uid)
    if not tonumber(uid) then
        return nil
    end

    local database, key = self.database, self.policyLib

    local backend, err = database:hget(key, uid)
    if not backend then error{ERRORINFO.REDIS_ERROR, err} end

    if backend == ngx.nul then backend = nil end                  -- 这里的ngx.nul  是不是应该是ngx.null

    return backend
end

因为我改的代码(diversion/diversion.lua)是ngx.null :

if upstream ~= ngx.null and upstream then           --238  ,这里的ngx.null
    ngx.var.backend = upstream
else
    upstream = default_backend
end

不过我奇怪最开始我测 获取head的"X-Uid"好像没有报错.
你可以看我fork你的代码,我把修改了的地方提交到我fork的代码里了.

@BG2BKK
Copy link
Contributor

BG2BKK commented Sep 4, 2015

好的吧,是我写错了,不好意思哈哈。我立刻改改。

@BG2BKK BG2BKK closed this as completed Sep 4, 2015
@yanchaoguo
Copy link

@Michael2008S @BG2BKK
按照例子配置策略后 运行:
curl 127.0.0.1:8030/ -H 'X-Uid:1024'
可以分流到beta1 的app server.
但如果我有个http://127.0.0.1:8030/abc?q=sina 的request 该如何配置,我这边404了

@BG2BKK
Copy link
Contributor

BG2BKK commented Jan 9, 2016

你是想在location abc{}中配置分流功能是吧,之前我们的考虑都是在/下分流的。你的目的应该是想要访问/abc?q=sina的时候转发到upstream的请求是/?q=sina是吗?

@yanchaoguo
Copy link

是的,由于上游业务不同url是不同的,且随着业务变动后期还会有新的url ,我想是不是会有一个url的模糊匹配方式,凡事满足127.0.0.1:8030/* 的请求 统统走你们提出的几种策略(这里假设是uid后缀)
回到上面的问题 ‘http://127.0.0.1:8030/abc?q=sina ’ 这个url满足了127.0.0.1:8030/* 然后策略适配器根据从策略中识别出是uid后缀匹配 对'X-Uid:1024' 进行处理分流 这样更好些吧

@yanchaoguo
Copy link

@Michael2008S @BG2BKK
假设我有一个这样的‘http://127.0.0.1:8030/abc?q=sina ’ request 按照指定uid方式分流
curl 127.0.0.1:8030//abc?q=sina -H 'X-Uid:1024' 分流到beta1
该如何配置,谢谢

@fankeke
Copy link

fankeke commented Jun 15, 2016

local u = ngx.var.cookie_uid ------还有就是local 是加到这里可以避免你上面说的内存问题吗?
答案是肯定的,这相当于只读取cookie_uid一次,缓存在lua虚拟机中,下次就不用再重新分配内容并读取数据了,直接从lua虚拟机读就好。


请问下为何加了local之后就缓存到lua里面了?这是lua自己的特征还是openresty提供的特质??
谢谢:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants