在 OpenResty 中使用正则表达式,社区中推荐的做法是使用ngx.re api。比如匹配一个字符串是否为 http(s) 的链接,可以这么写:
local function is_http_url(s)
return ngx.re.find(s,[[^https?://[w-_?.:/+=&#%]+$]])
end
压测一下:
local max = 1000000
local t = os.clock()
for _ = 1,max do
is_http_url("http://blog.stackoverflow.com/2016/10/Stack-Overflow-92-Podcast-The-Guerilla-Guide-to-Interviewing/?cb=1")
end
print("Time cost: ",os.clock() - t," s")
结果: Time cost: 2.663408 s
另一种做法是使用 lua 的正则语法:
local function is_http_url(s)
return s:find("^https?://[%w-_%.%?:/%+=&#%%]+$")
end
结果: Time cost: 0.652221 s
呃,怎么前者耗时是后者的四倍?lua 内置的小小状态机实现,居然打败了大名鼎鼎的 PCRE 库!说好的社区推荐呢!
仔细一瞧,前者的确漏了点东西。ngx.re 默认不会缓存正则表达式编译后的结果。一般在其它编程平台上,我们都会先把字符串编译成正则表达式,再用到正则函数中。比如在 Python 里使用 re.compile 。所以赶紧补上:
return ngx.re.find(s,[[^https?://[w-_?.:/+=&#%]+$]],"o")
好,这次性能有了明显提升: Time cost: 0.646518 s
不错不错,虽然还是跟 lua 的实现不分上下,考虑到 lua 本身的正则支持非常弱(比如连 (foo|bar) 这种形式都不行),而且语法离经叛道,改用 ngx.re 还是挺有必要的。毕竟 PCRE 可是 Perl Compatibility Regex Expression库,我最喜欢它支持的(?name:pattern) 形式的命名捕获功能。
其实 ngx.re 实现尚未用尽全力呢。开启了 JIT 之后,PCRE 库的性能会更上一层楼:
return ngx.re.find(s,"jo")
结果: Time cost: 0.421824 s
此时,lua 正则已经被甩到后面去了。
还能更快吗?
当然,OpenResty 军火库里还有另外一个武器:lua-resty-core
require 'resty.core.regex'
local function is_http_url(s)
return ngx.re.find(s,"jo")
end
结果: Time cost: 0.175346 s
Boom!最终用时是 lua 正则的四分之一。lua 正则已经望尘莫及了。有趣的是,这个结果正好是第一次比较的结果倒过来。
resty.core.regex 里面的 ngx.re.* API 是通过 luajit FFI 实现的。跟原来的 lua C/API 相比,这里有两点不同:
FFI 版本内部实现不同。即使我在代码里加入不能被 JIT 的语句,FFI 版本的 ngx.re.find 依然比 C/API 版本要快。当然可能是因为这个 api 是个特例。
有一点是必然的:FFI 版本可以被 JIT,而 C/API 不能。经过 JIT 之后的代码性能会更高。 如果代码执行路径中存在不能 JIT 的语句,luajit 会放弃 JIT。要想保障热路径上的代码都能被 JIT 掉,就需要用 FFI 版本的 api。
需要注明一下,resty.core.regex 并非银弹。在我们自己的应用上,我尝试引入resty.core.regex ,发现对性能无可见的提升。当然,我们的应用的功能不是匹配 url,正则处理亦非瓶颈。不过 resty.core.regex 对自己的项目是否有效,还需要诸君自己测试一番。
结论:
如果在 OpenResty 项目中需要使用正则表达式,请使用 ngx.re api,并开启 jo 选项。
resty.core.regex 值得一试。
更新一: 大多数人似乎没有在 ngx.re api 里使用 j 选项的习惯?随便在 GitHub 上找个基于 OpenResty 开发的项目,然后搜索 ngx.re ,你会发现很多时候都没有加 j 选项。 加 j 了的表达式,如果运行在不支持 PCRE JIT 的机器上,跟不加 j 的表达式是一样的,并没有什么副作用。加了你不会吃亏,写了你不会上当。 再说,OpenResty 的 j 优化,从 pcre 8.21 起就支持了。如果部署的机器是 Ubuntu 14.04 或 Centos 7,从包管理下载的 pcre 库就支持 JIT。即使用的是上古服务器 Centos 6,也可以选择自己提供编译用的 pcre(OpenResty 官方的 yum 包就是这么做的)。仅需配置一下,并养成多写一个字符的习惯,即可获得可见的性能提升,何乐而不为? 随便一提,记得在编译 OpenResty 时指定 --with-pcre-jit 选项,并在配置文件(main context)中添加 pcre_jit on ,这么做可以有效提升路由匹配的性能。 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|