Openresty中nil、null和ngx.null的区别

由于没有保留现场,本文直接转载另一篇文章做参考:https://pureage.info/2013/09/02/125.html

问题概述
 今天第一次在nginx+lua架构下,写了个需要操作Redis的后台接口,但是在判断空值时,结果和预期不一样

主要代码如下

1
2
3
4
local md5,err=red:hget(tasklist,"md5")  
if md5 and md5 ~= "" then
tb.md5=md5
end

当md5不为空时才赋值,如果为空就过滤掉,可实际结果是md5为空,但是仍然执行了tb.md5=md5,然后猜测是不是null字符串导致的,于是将上面的几行代码改为:

1
2
3
4
local md5,err=red:hget(tasklist,"md5")  
if md5 and md5 ~= “null” then
tb.md5=md5
end

仍然过滤失败,再次猜测可能是类型不对导致的

1
ngx.say("type of null is "..type(md5))

果然,这个”空值“并不是string类型,而是userdata类型,userdata类型当然跟字符串类型不会相等,所以上面的过滤条件不管设置成什么样子,都不会生效,永远会执行tb.md5=md5。

这样是找到原因了,但还未最终解决。既然当hget操作返回一个空值时,lua-resty-redis将其设置为一个userdata类型,那我们在代码里该如何过滤这种情况呢?本质问题就是,red:hget当查询resdis结果为空时,到底返回了什么?(不为空时,是string)

这时候开源的好处就体现出来了,在lua-resty-redis里扫了下redis.lua文件,发现返回的是ngx.null。

恩,问题到这就解决了,将上面的过滤代码改为:

1
2
3
4
local md5,err=red:hget(tasklist,"md5")  
if md5 and md5 ~= null and md5 ~= ngx.null then
tb.md5=md5
end

就能保证返回结果里不会包含值为null的域了。

回头看了一下lua-resty-redis的文档,发现关于上面的内容,在Readme里已经写的清清楚楚了,在lua-resty-redis中,有这么一句:

1
A non-nil Redis “bulk reply” results in a Lua string as the return value. A nil bulk reply results in a ngx.null return value.

ngx.null是什么?
那么ngx.null到底是什么东西呢? 在HttpLuaModule有如下说明:

1
The ngx.null constant is a NULL light userdata usually used to represent nil values in Lua tables etc and is similar to the lua-cjson library’s cjson.null constant. This constant was first introduced in the v0.5.0rc5 release.

ngx.null在print、ngx.print、ngx.log、ngx.say等函数中,有如下特点:

1
Lua nil arguments are accepted and result in literal “nil” strings while Lua booleans result in literal “true” or “false” strings. And the ngx.null constant will yield the “null” string output.

为什么要这么设计?
lua-resty-redis中,为什么要把redis查询为空的情况返回一个userdata类型的ngx.null?直接返回nil不行吗?
  答案是不行,因为nil在lua中有其特殊意义,如果一个变量被设置为nil,就等于说该变量未定义,与无穷无尽的其他未定义的变量一样。那么,如果把redis查询为空的结果设置为nil,就无法把”查询为空”和“未定义”区分开来了,例如在一个table中,一个key对应一个value,如果将该value设置为nil,则相当让key凭空消失,这显然是不合理的。所以必须用一个userdata类型的独特的值来表示这种查询为空,但又不等同于未定义的变量,例如ngx.null。同样的情况想必在sql的lua模块中也会出现,用来处理记录中键值查询为空的情况。

幽灵般的nil
  这就要说道lua中神奇的nil了。nil是一种类型,该类型只有一个值,这个值也叫nil。改值的作用只有一个,表示一个变量不存在。跟C\C++等常规语言不同,”不存在“跟空、0完全是两个概念。在C语言中,一个字符串如果为空,那么它就只有一个为0的\nul结束符,如果对齐进行逻辑判断,则是假。
但lua中,只要一个变量不是nil类型或者是boolean类型中的false,则对它进行逻辑判断,结果是真,即使该值是一个数字0,或者是一个空字符串。

JouyPub wechat
欢迎订阅「K叔区块链」 - 专注于区块链技术学习