如何编写这个特殊的测试用例

133 views
Skip to first unread message

YuanSheng

unread,
Mar 26, 2016, 11:10:15 PM3/26/16
to openresty
hi,大家好:

我准备对 OpenResty 添加一个新的 API,名字是 ngx.worker.close_listen() ,函数名能看得出来是为了关闭当前 worker 的 listen 端口。这样可以让某个 nginx worker 专心用于非 HTTP 请求的处理,比如完成一些后台定时任务等。代码本身不难,可是在测试用例实现上卡住了。

API 源码:

static int
ngx_http_lua_ngx_worker_close_listen(lua_State *L)
{
    ngx_close_listening_sockets((ngx_cycle_t *)ngx_cycle);

    return 0;
}

测试用例:
=== TEST 4: ngx.worker.close_listen
--- config
    location /lua {
        content_by_lua_block {
            local sock = ngx.socket.tcp()
            sock:settimeout(100)
            local ok, err = sock:connect(ngx.var.server_addr, ngx.var.server_port)
            ngx.say("first connect: ", ok)
            sock:close()

            ngx.worker.close_listen()

            local sock = ngx.socket.tcp()
            sock:settimeout(100)
            local ok, err = sock:connect(ngx.var.server_addr, ngx.var.server_port)
            ngx.say("second connect: ", ok)
            sock:close()
        }
    }
--- request
GET /lua
--- response_body
first connect: 1
second connect: nil
--- error_log
reported that connect() failed

执行结果:
$ prove t/122-worker.t
t/122-worker.t .. 1/6 TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 0.675 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 0.825 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 0.99 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
Retry connecting after 1 sec
TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused
t/122-worker.t .. 4/6
#   Failed test 'TEST 4: ngx.worker.close_listen - Can't connect to 127.0.0.1:1984: Connection refused (aborted)
# '
#   at /Users/yuansheng/perl5/lib/perl5/Test/Nginx/Socket.pm line 1521.

#   Failed test 'TEST 4: ngx.worker.close_listen - status code ok'
#   at /Users/yuansheng/perl5/lib/perl5/Test/Nginx/Socket.pm line 891.
#          got: ''
#     expected: '200'

#   Failed test 'TEST 4: ngx.worker.close_listen - response_body - response is expected (req 1)'
#   at /Users/yuansheng/perl5/lib/perl5/Test/Nginx/Socket.pm line 1277.
# @@ -1,2 +0,0 @@
# -first connect: 1
# -second connect: nil

#   Failed test 'TEST 4: ngx.worker.close_listen - pattern "reported that connect() failed" should match a line in error.log (req 1)'
#   at /Users/yuansheng/perl5/lib/perl5/Test/Nginx/Socket.pm line 1102.
t/122-worker.t .. Failed 3/6 subtests

Test Summary Report
-------------------
t/122-worker.t (Wstat: 0 Tests: 7 Failed: 4)
  Failed tests:  4-7
  Parse errors: Bad plan.  You planned 6 tests but ran 7.
Files=1, Tests=7, 44 wallclock secs ( 0.02 usr  0.00 sys +  0.15 cusr  0.05 csys =  0.22 CPU)
Result: FAIL

Yichun Zhang (agentzh)

unread,
Mar 27, 2016, 2:52:18 PM3/27/16
to openresty
Hello!

On Sat, Mar 26, 2016 at 8:10 PM, YuanSheng wrote:
> 我准备对 OpenResty 添加一个新的 API,名字是 ngx.worker.close_listen() ,函数名能看得出来是为了关闭当前
> worker 的 listen 端口。这样可以让某个 nginx worker 专心用于非 HTTP
> 请求的处理,比如完成一些后台定时任务等。代码本身不难,可是在测试用例实现上卡住了。
>
> API 源码:
>
> static int
> ngx_http_lua_ngx_worker_close_listen(lua_State *L)
> {
> ngx_close_listening_sockets((ngx_cycle_t *)ngx_cycle);
>
> return 0;
> }

咱先不说测试的问题,先来谈 ngx.worker.close_listen() 函数本身哈。如果我们允许在 request handler
里面调用,则这会存在 race condition,即当前 worker 已经 accept
的并且正在处理中的那些请求很可能会陷入超时(因为 ngx_close_listening_sockets 函数会删除所有
ngx_connection_t 上的读事件。貌似你这个函数应该只允许在 init_worker_by_lua* 上下文中调用(不含
ngx.timer.* 上下文)。

另外,ngx.worker.no_listen() 这个名字貌似更好一些。

我其实希望咱在这里可以更加灵活一些。把正常的 worker 进程转换为特殊的 helper 进程有一些限制,比如我们不好改变运行这个
helper 进程的系统帐户名。有的时候,我们希望使用另一个有某种特权的 uid (不一定是 root)来运行这种特殊的 helper
进程。所以感觉得在 nginx master 进程里面做,而不是在 worker 里面。

最后,我不太想在 ngx.worker. 下面再无限制地添加新的 API 函数了。咱们引入一个新的 ngx.worker Lua
模块吧,就放在 lua-resty-core 里面。使用时 require() 就好:

local ngx_worker = require "ngx.worker"
ngx_worker.some_func()

我计划把现有的 ngx.worker.* API 也移动到 ngx.worker 模块中来,同时引入一个新的 nginx
配置指令可以禁用原先的 ngx.worker.* API. 类似地更动也可以应用到 ngx.req, ngx.config,
ngx.thread, ngx.socket 和 ngx.location 上面来。这样新的 API 布局有利于按需加载 API
函数,有效控制 ngx_lua 的 memory footprint 和 Lua GC 的压力。当然,我们默认仍会保持对老 API
的向后兼容,但我们会 deprecate 老的 API,同时提供配置指令显式地禁用老的 API.

ngx_lua 的标准 Lua API 也得向模块化方向发展。

最后来说测试的问题。默认情况下,Test::Nginx 并不启用 master 进程,这意味着你的 worker 其实就是
master,所以要小心。你可以使用 master_on() 函数让测试台启动的 nginx 使用 master 进程。另外,要小心
repeat_each() 会对同一个 nginx 实例重复发测试请求,所以如果你只有一个 worker
进程,第二次请求的时候肯定就连不上了,因为没有 worker 在监听了。

Regards,
-agentzh

YuanSheng Wang

unread,
Mar 28, 2016, 10:15:53 AM3/28/16
to open...@googlegroups.com
2016-03-28 2:52 GMT+08:00 Yichun Zhang (agentzh) <age...@gmail.com>:
Hello!

On Sat, Mar 26, 2016 at 8:10 PM, YuanSheng wrote:
> 我准备对 OpenResty 添加一个新的 API,名字是 ngx.worker.close_listen() ,函数名能看得出来是为了关闭当前
> worker 的 listen 端口。这样可以让某个 nginx worker 专心用于非 HTTP
> 请求的处理,比如完成一些后台定时任务等。代码本身不难,可是在测试用例实现上卡住了。
>
> API 源码:
>
> static int
> ngx_http_lua_ngx_worker_close_listen(lua_State *L)
> {
>     ngx_close_listening_sockets((ngx_cycle_t *)ngx_cycle);
>
>     return 0;
> }

咱先不说测试的问题,先来谈 ngx.worker.close_listen() 函数本身哈。如果我们允许在 request handler
里面调用,则这会存在 race condition,即当前 worker 已经 accept
的并且正在处理中的那些请求很可能会陷入超时(因为 ngx_close_listening_sockets 函数会删除所有
ngx_connection_t 上的读事件。貌似你这个函数应该只允许在 init_worker_by_lua* 上下文中调用(不含
ngx.timer.* 上下文)。

另外,ngx.worker.no_listen() 这个名字貌似更好一些。

好好回味了一下,有种被点醒的感觉,细节还是春哥考虑的周到。
 

我其实希望咱在这里可以更加灵活一些。把正常的 worker 进程转换为特殊的 helper 进程有一些限制,比如我们不好改变运行这个
helper 进程的系统帐户名。有的时候,我们希望使用另一个有某种特权的 uid (不一定是 root)来运行这种特殊的 helper
进程。所以感觉得在 nginx master 进程里面做,而不是在 worker 里面。

这段我没有太读懂,说说我的理解。

把当前这个关闭 listen 的 worker 重命名一下,进程名不在是 "nginx worker process",换成其他更有辨识度的名字例如 “nginx back process”,这个功能我这次是和 close_listen 一起做的,允许用户自定义当前 worker 进程的名字,更加方便管理使用。

不过当前卡在了测试用例上,所以就先发邮件询问测试用例的事情。
 

最后,我不太想在 ngx.worker. 下面再无限制地添加新的 API 函数了。咱们引入一个新的 ngx.worker Lua
模块吧,就放在 lua-resty-core 里面。使用时 require() 就好:

    local ngx_worker = require "ngx.worker"
    ngx_worker.some_func()

我计划把现有的 ngx.worker.* API 也移动到 ngx.worker 模块中来,同时引入一个新的 nginx
配置指令可以禁用原先的 ngx.worker.* API. 类似地更动也可以应用到 ngx.req, ngx.config,
ngx.thread, ngx.socket 和 ngx.location 上面来。这样新的 API 布局有利于按需加载 API
函数,有效控制 ngx_lua 的 memory footprint 和 Lua GC 的压力。当然,我们默认仍会保持对老 API
的向后兼容,但我们会 deprecate 老的 API,同时提供配置指令显式地禁用老的 API.

ngx_lua 的标准 Lua API 也得向模块化方向发展。

那我有空就帮忙春哥先把 worker 整理出来,往这个方向靠拢(如果我动作慢了,春哥别嫌弃哈)。
 

最后来说测试的问题。默认情况下,Test::Nginx 并不启用 master 进程,这意味着你的 worker 其实就是
master,所以要小心。你可以使用 master_on() 函数让测试台启动的 nginx 使用 master 进程。另外,要小心
repeat_each() 会对同一个 nginx 实例重复发测试请求,所以如果你只有一个 worker
进程,第二次请求的时候肯定就连不上了,因为没有 worker 在监听了。

master_on() 函数我还真没注意,我尝试了 workers(2) 可是发现没有达到需要。看来我后面尝试规避 repeat_each() 是卡在了 master_on() 上。后面我尝试一下。

非常感谢春哥这么详细的答复,又占用您时间了 ^_^。
 

Regards,
-agentzh

--
--
邮件来自列表“openresty”,专用于技术讨论!
订阅: 请发空白邮件到 openresty...@googlegroups.com
发言: 请发邮件到 open...@googlegroups.com
退订: 请发邮件至 openresty+...@googlegroups.com
归档: http://groups.google.com/group/openresty
官网: http://openresty.org/
仓库: https://github.com/agentzh/ngx_openresty
教程: http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html



--

YuanSheng Wang
---------------------------------------
OpenResty lover ^_^

Yichun Zhang (agentzh)

unread,
Mar 31, 2016, 2:41:27 PM3/31/16
to openresty
Hello!

2016-03-28 7:15 GMT-07:00 YuanSheng Wang:
> 这段我没有太读懂,说说我的理解。
>

其实就是我希望用不同的系统帐户来运行这种特殊的不(直接)服务下游请求的 nginx worker.

> 把当前这个关闭 listen 的 worker 重命名一下,进程名不在是 "nginx worker process",换成其他更有辨识度的名字例如
> “nginx back process”,这个功能我这次是和 close_listen 一起做的,允许用户自定义当前 worker
> 进程的名字,更加方便管理使用。

感觉进程的 title 可以做成自定义的比较好 :)

>
> 不过当前卡在了测试用例上,所以就先发邮件询问测试用例的事情。
>

嗯嗯。你可以同时检查一下 t/servroot/logs/error.log 文件里的调试日志,以帮助分析。

>
> 那我有空就帮忙春哥先把 worker 整理出来,往这个方向靠拢(如果我动作慢了,春哥别嫌弃哈)。
>

太客气啦。非常感谢你在这一块进行调研和尝试。

Best regards,
-agentzh
Reply all
Reply to author
Forward
0 new messages