本文不是关于新版 OpenResty 如何支持 ARM64 的,而是关于如何应对这一过程引入的 break change。服务器
另外,若是你没用 OpenResty 本身的 LuaJIT 分支,那么能够直接关掉这个页面了,由于这些 break change 只有在使用了 OpenResty 本身的 LuaJIT 分支才会出现。架构
一切的根源在于,新版本的 OpenResty 把当前请求的 ngx_http_request_t
放到了 luaState
的 exdata
属性里面,再也不使用 getfenv(0).__ngx_req
这种方式了。exdata
是 OpenResty 本身的 LuaJIT 分支加的属性,因此若是不用 OpenResty 本身的 LuaJIT 分支,依旧仍是得走 getfenv(0).__ngx_req
这种方式。一样被移除的还有 getfenv(0).__ngx_cycle
和给每一个 main thread 准备的全局环境表。接下来咱们谈谈如何应对这几个变化。函数
新版 OpenResty 使用 resty.core.base
里面的 get_request()
代替了 getfenv(0).__ngx_req
。在你的代码里,能够这么写:性能
local base = require "resty.core.base" local get_request = base.get_request if not get_request then get_request = function() return getfenv(0).__ngx_req end end ... local r = get_request()
可是! get_request()
并不是 100% 兼容 getfenv(0).__ngx_req
。前者返回的是一个 cdata,然后者返回的是一个 lightuserdata。cdata 和 lightuserdata 在语义上有些微妙的不一样。当你用 lightuserdata 做为 table 的 hash key 时,若是 lightuserdata 指向的地址相同,那么 hash 值会相同。但若是是 cdata,即便是指向同一地址的指针类型的 cdata,因为计算 hash 时用的是 cdata 的地址,而非其内部的值,因此不一样的 cdata 的 hash 值会不同。举个例子:优化
local r = get_request() local h = {} h[r] = 1 ngx.say(h[get_request()])
在以前的版本里,两次 get_request()
会返回同一个地址(都是同一个请求嘛),因此会输出 1。而新的 OpenResty 里,你会发现输出结果是 nil。这是由于两次 get_request()
会创造两个 cdata 对象,这两个对象虽然值同样,可是内存地址不同,因此 hash 值不同。ui
那怎么解决呢?咱们能够实现一个转换函数,把 cdata 的值变成某种可用做 hash key 的类型。一个简单的解决方法是加上 tostring。tostring(cdata)
的输出中会包含 cdata 指向的地址,这样同一个请求对应的 key 就会相同。lua
考虑到 LuaJIT 建立字符串的开销比较大,做为一种优化手段,在某些架构下咱们能够用 tonumber(ffi_cast("intptr_t", cdata))
代替。之因此限定在某些架构,是由于 LuaJIT 的 Number 实际上是 double,而不是 int64,因此对于某些 64 位的架构,不必定能获得正确的输出。好在 x64 的用户态空间地址不会超过 48 位,因此咱们能够在主流的 x64 服务器上采用该优化。固然前提是你没有启用 5 级页表。考虑到只有数百 TB 内存的机器才会有开启 5 级页表的须要,大致上你能够放心地认为你的 x64 环境不会遇到这样的问题。即便开启了 5 级页表,现阶段 48 位以上的内存地址也不是默承认用的。关于 5 级页表的更多上下文,能够看下这两个连接:.net
https://lwn.net/Articles/717293/
https://www.kernel.org/doc/Do...指针
有些 Lua 代码会经过 getfenv(0).__ngx_cycle
获取 ngx_cycle
, 而后经过 FFI 调用传给 C 函数。其实直接在 C 函数里面访问 ngx_cycle
就能够了,不须要通过 Lua 这一层。rest
你可能会问,reload 的时候,init
阶段下 ngx_cylce
应该会指向旧的 ngx_cycle
吧?这里 OpenResty 作了点手脚。它会把旧的 ngx_cycle
放到 saved_ngx_cycle
里面来,让 ngx_cycle
指向新构建的 ngx_cycle_t *cycle
。因此并不须要特殊的对待。
为了放下 getfenv(0).__ngx_req
,过去的 OpenResty 须要给每一个 main thread 准备独立的全局环境表,这样每一个请求的 getfenv(0)
才会返回不一样的 table。既然新的 OpenResty 已经不须要 getfenv(0).__ngx_req
,这些全局环境表就能干掉了。
不过让它们下岗,还有点反作用。过去在 rewrite
/access
/content
等阶段里定义了全局变量(一般是手误引入的),不会污染到其余的请求。那是由于 OpenResty 设置了全局环境表,这些全局变量只会影响到它们所在的全局环境表。可是移除了全局环境表的保护后,这些全局变量就能肆无忌惮地跑来跑去。为此新版 OpenResty 加了个 guard,若是在这些阶段里遇到全局变量的定义,会打印这样的错误信息:
2019/04/30 11:01:18 [warn] 26843#26843: *240 [lua] _G write guard:12: __newindex(): writing a global lua variable ('xxx') which may lead to race conditions between concurrent requests, so prefer the use of 'local' variables stack traceback: ...
这种错误信息对程序的流程没有影响,但对性能有影响。解决办法?把全局变量一个个都揪出来解决掉。
固然若是你是在 init
或 init_worker
阶段定义全局变量,并不会触发这个 guard。毕竟这么作的人通常是故意的,在过去的 OpenResty 里这也是标准的“使用”全局变量的方式。虽然我我的不推荐这么作。用全局变量,早晚都要还的。