文档章节

nil、null与ngx.null

epiclight
 epiclight
发布于 2015/06/07 12:00
字数 1588
阅读 91
收藏 1

nil、null与ngx.null

问题概述

今天第一次在nginx+lua架构下,写了个需要操作Redis的后台接口,该接口的功能主要是接受客户端的json格式的post请求,实现对保存在redis中的任务插入、删除、查询等。虽然nginx,lua等都是刚接触,但这几个接口还是顺风顺水的坐下来了,不能忘了感谢春哥章亦春。

在Redis中记录的任务其实很简单,每插入一个任务,就在redis中增加一个HASH结构,每次查询返回该SET的各个Field和对应的Value值,例如md5,filesize等。由于任务类型的不同,有的Field可能在该任务中不存在,此时在以json格式将查询结果返回时不应显示该Field。

以md5域为例,在对当前任务以md5域执行hget后,应该对返回结果做一个判断,如果该HASH结构并没有设置md5这个域,则跳过,继续执行后面的逻辑,如果设置了md5域,则把该域的Value取出来,插入到结果table中,后续再作为json格式返回结果的一部分,返回给后台。

测试时,却发现在某些域未设置时,查询结果中却仍然会把该域返回给查询调用者,但其Value部分是null。例如,执行下面的测试用例:

curl -d "{\"queryfile\":[{\"url\":\"/www.baidu.com/img/bdlogo.gif\" }]}" "127.0.0.1/cjson"

尽管对该任务而言,在插入时并没有设置md5域,但返回结果包含了md5域:

{"result":[{"url":"\/www.baidu.com\/img\/bdlogo.gif","result":0,"md5":null,"putflag":"remote"}]}

问题分析

看到这个现象,首先想到的当然是lua脚本中对执行hget md5操作的返回值判断失效了,我第一次是这么写的:

local md5,err=red:hget(tasklist,"md5")if md5 and  md5 ~= ""  then
    tb.md5=md5end

从后面的结果看,当md5值为空时,该判断条件并没有将其过滤掉,依然执行了tb.md5=md5。由于redis模块也是调用春哥的lua-resty-redis,因此猜测是否春哥把redis查询结果中的空值用“null”字符串返回了,于是将上面的几行代码改为:

local md5,err=red:hget(tasklist,"md5")if md5 and  md5 ~= “null”  then
    tb.md5=md5end

仍然过滤失败,忽然眼前一亮,发现查询结果中显示的是"md5":null,而非"md5":"null",上面这种猜测不攻自破。

red:hget(tasklist,"md5")肯定是返回了一个跟null相关的结果,但这个结果既不是nil,又不是空字符串,也不是"null"。再次猜测,该值类型可能不是string,虽然这个猜测看上去很奇怪,因为在设置了md5的情况下,其类型的确是string。于是在判断语句前面加了一句打印信息:

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

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

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

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

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

local md5,err=red:hget(tasklist,"md5")if md5 and md5 ~= null and md5 ~= ngx.null  then
    tb.md5=md5end

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

眼高手低

回头看了一下lua-resty-redis的文档,发现关于上面的内容,在Readme里已经写的清清楚楚了,在https://github.com/agentzh/lua-resty-redis/blob/master/README.markdown中,有这么一句:
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.

首先不应该是自责,而是再次赞一下agentzh的态度,业界标杆。

ngx.null是什么?

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

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等函数中,有如下特点:

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,或者是一个空字符串。


本文转载自:www.baidu.com

epiclight
粉丝 3
博文 41
码字总数 79
作品 0
深圳
架构师
私信 提问
lua读取redis中的数据部分丢失

用lua从redis中取数据,结果部分数据丢失。 redis中的元数据是{"content":"保税科技<\/a>(3.79%)","title":"1","weight":0,"rate":0,"timeStamp":1450679400037,"name":"","code":""} 但是l......

123jxl123
2015/12/21
175
0
Objective-C 中 NULL、nil、Nil、NSNull

NULL NULL 定义在 usr/include/sys/types/null.h 文件里: #ifndef NULL #define NULL DARWIN_NULL #endif / NULL / 其中 DARWINNULL 的定义在 usr/include/sys/_types.h 文件里,如下: #i......

tb_engineer
2016/12/20
1
0
iOS nil、Nil、NULL和NSNull 的使用

nil用来给对象赋值(Objective-C中的任何对象都属于id类型),NULL则给任何指针赋值,NULL和nil不能互换,nil用于类指针赋值(在Objective-C中类是一个对象,是类的meta-class的实例),而N...

江山风雨
2018/07/04
0
0
nil,NULL,NSNull的区别

nil,NULL,NSNull的区别 主要就以下几点: 1、nil:一般赋值给空对象; 2、NULL:一般赋值给nil之外的其他空值。如SEL等;   举个栗子(好重啊~):     [NSApp beginSheet:sheet    ...

Daniel_s
2015/12/25
25
0
iOS中nil、NSNULL、Nil

一、起因 参与一个比较成熟的项目的开发,刚好项目新功能上线。结果上线4天奔溃2千个,我看了下倒吸一口凉气。当时,最后排查出来是很简单的原因新用户注册时H5返回给iOS壳中<NULL>类型数据的...

gitzhengjianhua
2016/11/15
12
0

没有更多内容

加载失败,请刷新页面

加载更多

idea下springboot 项目在static目录下添加文件不生效

idea下springboot 项目在static目录下添加文件不生效 问题描述 是这样子的,我的项目目录结构如下: 我在static目录下,创建了index.html和aaaa.jpg这两个文件。然后,启动服务访问 http://l...

wotrd
昨天
5
0
k8s1.14 一、环境

1. 4台虚拟机 (CentOS Linux release 7.2.1511 (Core) ) 192.168.130.211 master 192.168.130.212 node1 192.168.130.213 node2 192.168.130.214 node3 2. 设置服务器hostname 2.1 设置本机......

ThomasCheng
昨天
3
0
盖茨:如果我现在开创一家公司 将会专注于AI

新浪科技讯,北京时间 6 月 26 日凌晨消息,微软联合创始人比尔·盖茨(Bill Gates)在周一接受采访时表示,如果他今天从哈佛大学辍学并开创一家新公司,那么这家公司将会专注于人工智能(A...

linuxCool
昨天
1
0
聊聊feign的Retryer

序 本文主要研究一下feign的Retryer Retryer feign-core-10.2.3-sources.jar!/feign/Retryer.java public interface Retryer extends Cloneable { /** * if retry is permitted, retur......

go4it
昨天
10
0
HyperLogLog简介

  (1)HyperLogLog简介      在Redis 在 2.8.9 版本才添加了 HyperLogLog,HyperLogLog算法是用于基数统计的算法,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个...

SEOwhywhy
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部