Re: 请教一个 nginx lua模块的问题

842 views
Skip to first unread message

agentzh

unread,
Jun 8, 2012, 2:33:56 AM6/8/12
to 刘鑫, openresty
Hello!

2012/6/8 刘鑫 <marc...@gmail.com>:
> 但是下面的代码会有问题:
>

你的 Lua 代码中有几处明显的问题,请见下面的评注:

>
> server {
> server_name _;
> location / {
> # proxy_pass http://localhost:8080;
> set $is_dynamic '';
> set_by_lua $is_dynamic '
> method = ngx.req.get_uri_args()["method"];
> if ngx.var.request_method == "GET" and method ~= nil then
> return "yes";
> elseif ngx.var.request_method == "PUT" then {

首先,您这里的 if /elseif 的语法有问题,then 后面不应跟 { ... } 花括号,这不是合法的 Lua 语法。我这里可以得到这样的错误消息:

[error] ... failed to load Lua inlined code: [string
\"set_by_lua\"]:5: unexpected symbol near '{'

> local m = ngx.re.match(ngx.var.remote_addr,
> "^10\\.0\\.0\\.[0-9]+$");

其次,这里的 \ 转义序列是有问题的,因为你使用的是 set_by_lua,所以 Lua 代码本身是一个 nginx 字符串,所以 \
需要转义一次,而 Lua 代码内你又使用了 "..." 这个 Lua 字符串常量,所以 \ 还需要再转义一次,因此正确的写法是:

local m = ngx.re.match(ngx.var.remote_addr, "^10\\\\.0\\\\.0\\\\.[0-9]+$");

如果这个看得过于复杂,也可以换用 [[...]] 的形式,这样只需要为 nginx 字符串的语法转义一次 \:

local m = ngx.re.match(ngx.var.remote_addr, [[^10\\.0\\.0\\.[0-9]+$]]);

如果你的 Lua 代码是放在外部 .lua 文件中的话,就不再需要为 nginx 字符串语法对 \ 进行额外转义了。关于此问题的更多讨论,可以见:

http://wiki.nginx.org/HttpLuaModule#Special_PCRE_Sequences

一般地,对于这种简单的模式匹配,使用 Lua 标准的 string.find 或者 string.match 会更加高效和简便一些 :)

> if m == nil then
> ngx.exit(401);

再次,ngx.exit 在 set_by_lua 的上下文中是不可用的,见它的文档中的"context"列表:

http://wiki.nginx.org/HttpLuaModule#ngx.exit

建议换用 rewrite_by_lua 或者 access_by_lua :)

Best regards,
-agentzh

P.S. 同时抄送给了 openresty 邮件列表 https://groups.google.com/group/openresty
,以便其他用户也可以参考一下,呵呵 :)

刘鑫

unread,
Jun 8, 2012, 2:49:50 AM6/8/12
to agentzh, openresty
啊,感谢你的详细讲解,那个语法错误刚刚我也发现了,而且我测试了一下,用allow/deny不足以区分来自proxy的访问,目前我的测试代码是这样的:

    location / {
        content_by_lua '
            local remote_ip = ngx.req.get_headers()["X-Real-IP"];
            if remote_ip == nil then
                remote_ip = ngx.var.remote_addr;
            end;
            local m = ngx.re.match(remote_ip, "^192\\.168\\.56\\.[0-9]+$");
            if m ~= nil then
                ngx.say(remote_ip);
            else
                ngx.exit(403);
            end;
        ';
    }

我现在把测试代码改一下,让它更接近实际场景:)
--
国难思良将

智拙实验室: http://zerolabrary.appspot.com/
……

劉鑫
March.Liu

smallfish

unread,
Jun 8, 2012, 2:55:21 AM6/8/12
to open...@googlegroups.com, agentzh
@刘老师,针对类似判断的推荐采用access_by_lua*。
我这边也有类似的,是子请求数据库校验,然后返回proxy。
以前写过401认证的,意思差不多:http://chenxiaoyu.org/2012/02/08/nginx-lua-401-auth.html
--


2012/6/8 刘鑫 <marc...@gmail.com>

刘鑫

unread,
Jun 8, 2012, 2:56:14 AM6/8/12
to agentzh, openresty
PS,原本我想用access_by_lua,但是这里我需要把它proxy到另一个server{...},rewrite好像只能转向到同一个http://host:port下面?


在 2012年6月8日 下午2:49,刘鑫 <marc...@gmail.com>写道:
啊,感谢你的详细讲解,那个语法错误刚刚我也发现了,而且我测试了一下,用allow/deny不足以区分来自proxy的访问,目前我的测试代码是这样的:

    location / {
        content_by_lua '
            local remote_ip = ngx.req.get_headers()["X-Real-IP"];
            if remote_ip == nil then
                remote_ip = ngx.var.remote_addr;
            end;
            local m = ngx.re.match(remote_ip, "^192\\.168\\.56\\.[0-9]+$");
            if m ~= nil then
                ngx.say(remote_ip);
            else
                ngx.exit(403);
            end;
        ';
    }

我现在把测试代码改一下,让它更接近实际场景:)





Zoom.Quiet

unread,
Jun 8, 2012, 2:58:57 AM6/8/12
to open...@googlegroups.com, agentzh
在 2012年6月8日 下午2:56,刘鑫 <marc...@gmail.com> 写道:
> PS,原本我想用access_by_lua,但是这里我需要把它proxy到另一个server{...},rewrite好像只能转向到同一个http://host:port下面?
>

- 都可以的, openresty 里有 interl 模式的完全跳转,,纯内部数据流通,很爽脆的!
- 嗯嗯嗯,俺对内忽悠没有成功,怎么外边儿的旧金山人都开始研究了?!
- 推荐使用 tengine+openrsty 进行多重优化!

> 在 2012年6月8日 下午2:49,刘鑫 <marc...@gmail.com>写道:
>
>> 啊,感谢你的详细讲解,那个语法错误刚刚我也发现了,而且我测试了一下,用allow/deny不足以区分来自proxy的访问,目前我的测试代码是这样的:
>>
>>     location / {
>>         content_by_lua '
>>             local remote_ip = ngx.req.get_headers()["X-Real-IP"];
>>             if remote_ip == nil then
>>                 remote_ip = ngx.var.remote_addr;
>>             end;
>>             local m = ngx.re.match(remote_ip,
>> "^192\\.168\\.56\\.[0-9]+$");
>>             if m ~= nil then
>>                 ngx.say(remote_ip);
>>             else
>>                 ngx.exit(403);
>>             end;
>>         ';
>>     }
>>
>> 我现在把测试代码改一下,让它更接近实际场景:)
>>
>>
>
>
>
> --
> 国难思良将
>
> 智拙实验室: http://zerolabrary.appspot.com/
> ……
>
> 劉鑫
> March.Liu
>

> --
> 邮件自: 列表“openresty”,专用于技术讨论!
> 发言: 请发邮件到 open...@googlegroups.com
> 退订: 请发邮件至 openresty+...@googlegroups.com
> 详情: http://groups.google.com/group/openresty
> 官网: http://openresty.org/
> 仓库: https://github.com/agentzh/ngx_openresty
> 建议: 提问的智慧 http://wiki.woodpecker.org.cn/moin/AskForHelp
> 教程: http://agentzh.org/misc/nginx/agentzh-nginx-tutorials-zhcn.html

--
人生苦短, Pythonic! 冗余不做,日子甭过!备份不做,十恶不赦!
俺: http://about.me/zoom.quiet
文字协议: http://creativecommons.org/licenses/by-sa/2.5/cn/

smallfish

unread,
Jun 8, 2012, 3:01:36 AM6/8/12
to open...@googlegroups.com
跟tengine+openrestry多重优化有半毛钱关系啊....

我现在的做法是:
1. 搞多个upstream
2. 在access_by_lua里判断校验的结果,然后拼出upstream的名字
3. 最后proxy_pass到上面的变量结果中去
--


2012/6/8 Zoom.Quiet <zoom....@gmail.com>
在 2012年6月8日 下午2:56,刘鑫 <marc...@gmail.com> 写道:
> PS,原本我想用access_by_lua,但是这里我需要把它proxy到另一个server{...},rewrite好像只能转向到同一个http://host:port下面?
>

- 都可以的, openresty 里有 interl 模式的完全跳转,,纯内部数据流通,很爽脆的!
- 嗯嗯嗯,俺对内忽悠没有成功,怎么外边儿的旧金山人都开始研究了?!
- 推荐使用 tengine+openrsty 进行多重优化!

> 在 2012年6月8日 下午2:49,刘鑫 <marc...@gmail.com>写道:
>
>> 啊,感谢你的详细讲解,那个语法错误刚刚我也发现了,而且我测试了一下,用allow/deny不足以区分来自proxy的访问,目前我的测试代码是这样的:
>>
>>     location / {
>>         content_by_lua '
>>             local remote_ip = ngx.req.get_headers()["X-Real-IP"];
>>             if remote_ip == nil then
>>                 remote_ip = ngx.var.remote_addr;
>>             end;
>>             local m = ngx.re.match(remote_ip,
>> "^192\\.168\\.56\\.[0-9]+$");
>>             if m ~= nil then
>>                 ngx.say(remote_ip);
>>             else
>>                 ngx.exit(403);
>>             end;
>>         ';
>>     }
>>
>> 我现在把测试代码改一下,让它更接近实际场景:)
>>
>>
>
>
>
> --
> 国难思良将
>
> 智拙实验室: http://zerolabrary.appspot.com/
> ......

agentzh

unread,
Jun 8, 2012, 3:11:47 AM6/8/12
to 刘鑫, openresty
Hello!

2012/6/8 刘鑫 <marc...@gmail.com>:


> 啊,感谢你的详细讲解,那个语法错误刚刚我也发现了,而且我测试了一下,用allow/deny不足以区分来自proxy的访问,目前我的测试代码是这样的:
>
> location / {
> content_by_lua '
> local remote_ip = ngx.req.get_headers()["X-Real-IP"];
> if remote_ip == nil then
> remote_ip = ngx.var.remote_addr;
> end;
> local m = ngx.re.match(remote_ip, "^192\\.168\\.56\\.[0-9]+$");

这一行代码中的正则表达式仍然存在转义的问题。如果你使用 LuaJIT 2.0.0 beta9 以上版本运行你的 Lua 代码的话,会得到下面这一行错误:

[error] 7829\#0: *1 Failed to load Lua inlined code: [string
\"content_by_lua\"]:6: invalid escape sequence near '\"^192'

如果你使用标准 Lua 5.1 是会把 \. 默默地解释为 . 的,而这显然不是你想要的,呵呵。

对非法转义序列的错误检查是 Lua 5.2 和 LuaJIT 2.0.0 beta9+ 引入的新特性。

正如我早先那封邮件中指出的,在这个内联 Lua 代码到 nginx.conf 的上下文中的正确的写法应当是:

local m = ngx.re.match(remote_ip, [[^192\\.168\\.56\\.[0-9]+$]]);

Regards,
-agentzh

agentzh

unread,
Jun 8, 2012, 3:15:05 AM6/8/12
to open...@googlegroups.com
Hello!

2012/6/8 smallfish <smallf...@gmail.com>:


> 我现在的做法是:
> 1. 搞多个upstream
> 2. 在access_by_lua里判断校验的结果,然后拼出upstream的名字
> 3. 最后proxy_pass到上面的变量结果中去

是的,这是一种推荐的做法。当然,另外一种做法是在 access_by_lua 中使用 ngx.exec()
进行内部跳转,这样在那些使用有限域名的情况下可以避免请求时运态请求 DNS 服务器(当然,这个对性能的影响也有限,毕竟 nginx 自己的
resolver 也是带缓存的) :)

Best regards,
-agentzh

agentzh

unread,
Jun 8, 2012, 3:18:28 AM6/8/12
to Zoom.Quiet, open...@googlegroups.com
Hello!

2012/6/8 Zoom.Quiet <zoom....@gmail.com>:
> - 推荐使用 tengine+openrsty 进行多重优化!
>

Tengine 的一个好处是可以自动进行 CPU 亲缘性配置。如果使用标准 Nginx 核心同时自己配置好 CPU affinity
的话,在使用 ngx_lua 等 OpenResty 组件时,应该在性能上不会有什么区别。(欢迎 Tengine 团队的朋友们指正,呵呵)

另外,我个人目前只支持 OpenResty 组件在标准 Nginx 核心上的技术问题 :) 特定于 Tengine 的问题需要
Tengine 团队来支持了,呵呵。

Regards,
-agentzh

Zoom.Quiet

unread,
Jun 8, 2012, 3:21:09 AM6/8/12
to agentzh, open...@googlegroups.com

- 是也乎,是也乎
- Tengine 提供对 Nginx 的原生增强
- openresty 提供对 Nginx 的非入侵式功能扩展
相得益彰哪,,,

> Regards,
> -agentzh

Joshua Zhu

unread,
Jun 8, 2012, 4:25:08 AM6/8/12
to open...@googlegroups.com
Hi,

2012/6/8 agentzh <age...@gmail.com>
目前的Tengine在Nginx性能方面的优化很有限。较大的性能优化正在开发中,不久会release出来。
另外我们也会尝试让OpenResty在选择Nginx的时候把Tengine支持作为可选项。当然这部分的工作如果做了会由我们团队来支持——维护OpenResty对标准Nginx的支持已经让春儿老师够费心的了,我们不能添乱,哈哈。

Regards,

--
Joshua Zhu
Senior Software Engineer
Server Platforms Team at Taobao

刘鑫

unread,
Jun 8, 2012, 4:55:03 AM6/8/12
to agentzh, openresty
刚才网络有问题,我跟鱼哥讨论了一下,初步做了一个实现,就是觉得有点囧⋯⋯
server {
    listen 9080;

    location / {
        content_by_lua '
            local remote_ip = ngx.req.get_headers()["X-Real-IP"];
            if remote_ip == nil then
                remote_ip = ngx.var.remote_addr;
            end;
            ngx.say(remote_ip);
        ';
    }
}

server {
    listen 9403;
    location / {
        return 403;
    }
}

server{
    listen 8080;
    location / {
    set $proxy_to '';
        access_by_lua '
            local client_ip = ngx.req.get_headers()["X-Real-IP"];
            if client_ip == nil then
                client_ip = ngx.var.remote_addr;
           end;

            local m = string.match(client_ip, "^192%.168%.56%.%d+$");

            if m ~= nil then
                ngx.var.proxy_to = "http://127.0.0.1:9080";
            else
                ngx.var.proxy_to = "http://127.0.0.1:9403";
            end;
        ';

        proxy_pass $proxy_to;
        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP   $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

我这个需求跟鱼哥的有点不一样,对于非匹配网段,其实我希望把它阻挡到外面,所以我原本想把某些匹配的直接return一个403,但是测试发现执行完return 403后,nginx没有结束request,而是继续去执行下面的代码。即使break也不行。所以我现在做了一个比较囧的实现,专门写一个只返回403的server节点。
不知道大家有什么比较好的方案?
PS,我在金山也算比较早开始用C做nginx定制开发的吧⋯⋯只是除了老彭和钟东,未必有人知道。

agentzh

unread,
Jun 8, 2012, 5:01:11 AM6/8/12
to 刘鑫, openresty
Hello!

2012/6/8 刘鑫 <marc...@gmail.com>:


> 我这个需求跟鱼哥的有点不一样,对于非匹配网段,其实我希望把它阻挡到外面,所以我原本想把某些匹配的直接return一个403,但是测试发现执行完return
> 403后,nginx没有结束request,而是继续去执行下面的代码。即使break也不行。所以我现在做了一个比较囧的实现,专门写一个只返回403的server节点。
> 不知道大家有什么比较好的方案?

如果需要在 access_by_lua 中提前结束当前请求,可以使用 ngx.exit() 函数:

http://wiki.nginx.org/HttpLuaModule#ngx.exit

特别地,在你的场景中,直接写 ngx.exit(403) 就好了 :)

请注意,access_by_lua 总是运行在 ngx_rewrite 模块之后,所以 ngx_rewrite 模块的 return 和
break 自然都会在 access_by_lua 之前执行。

关于这里的执行顺序问题,可以参见我的 Nginx 连载教程:

http://agentzh.org/misc/nginx/agentzh-nginx-tutorials-zhcn.html#02-NginxDirectiveExecOrder01

Best regards,
-agentzh

刘鑫

unread,
Jun 8, 2012, 5:13:12 AM6/8/12
to agentzh, openresty
在 2012年6月8日 下午5:01,agentzh <age...@gmail.com>写道:
Hello!

2012/6/8 刘鑫 <marc...@gmail.com>:
> 我这个需求跟鱼哥的有点不一样,对于非匹配网段,其实我希望把它阻挡到外面,所以我原本想把某些匹配的直接return一个403,但是测试发现执行完return
> 403后,nginx没有结束request,而是继续去执行下面的代码。即使break也不行。所以我现在做了一个比较囧的实现,专门写一个只返回403的server节点。
> 不知道大家有什么比较好的方案?

如果需要在 access_by_lua 中提前结束当前请求,可以使用 ngx.exit() 函数:

   http://wiki.nginx.org/HttpLuaModule#ngx.exit

特别地,在你的场景中,直接写 ngx.exit(403) 就好了 :)

测试了一下,确实用ngx.exit就对了,最终简化成:


server{
    listen 8080;
    location / {
        access_by_lua '
            local client_ip = ngx.req.get_headers()["X-Real-IP"];
            if client_ip == nil then
                client_ip = ngx.var.remote_addr;
            end;

            local m = string.match(client_ip, "^192%.168%.56%.%d+$");
            if m == nil then
                ngx.exit(403);
            end;
        ';

        proxy_pass http://127.0.0.1:9080;

        proxy_set_header        Host            $host;
        proxy_set_header        X-Real-IP   $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
 
谢谢章老师和鱼哥!

请注意,access_by_lua 总是运行在 ngx_rewrite 模块之后,所以 ngx_rewrite 模块的 return 和
break 自然都会在 access_by_lua 之前执行。

关于这里的执行顺序问题,可以参见我的 Nginx 连载教程:

   http://agentzh.org/misc/nginx/agentzh-nginx-tutorials-zhcn.html#02-NginxDirectiveExecOrder01

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