我们的服务本来使用一组redis,以一致性哈希的方式来使用,现在打算替换为云平台的redis集群服务,因此需要设计一个平滑过渡的方案。解决方案并不难,两种方式可以使用不同的key,对于一种key使用ring_redis,另一种使用redis集群,这样只需要在redis库的接口内部进行key的解析,就无需对其他代码做过多改动了。
实现并不难,但在测试时,遇到个很诡异的问题:从redis中取一个不存在的key时,返回的值(正常情况是json格式的字符串)在不存在的情况下,会导致 nginx 500
错误,通过日志中的 trace
看到,是返回的字符串无法被 cjson
解析导致,返回的字符串是 userdata:NULL
。这个返回的字符串引起我的好奇心, userdata
是什么鬼?
userdata
?
userdata
是Lua中C API的一个基本类型,可以把任意的C数据存储在lua变量中,除了赋值和相等的判断外,没有其他的lua预定义的操作。 userdata
常用来表示由C应用或者库创建的新类型。
仔细看了 lua-resty-redis
的代码,才发现空值返回的是 ngx.null
这个变量。在 nginx-api-for-lua
中, ngx.null
的含义如下:
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.
其中解决问题的方法很简单,还是RTFM!看了文档和示例,知道了 lua-resty-redis
的正确使用方法。
local ret, err = redis:get('foo') if not ret then ngx.log('err: xxxx') return end if ret == ngx.null then ngx.log('err: not found') return end -- process the value
这个库的返回值,在出错的情况下,会有 err
的值,表示出错信息;但要注意的是,使用 get
命令时,需要判断一下是否为 ngx.lua
,否则后续处理就可能会出现问题。而我遇到的问题,就是再判断没有出错的情况下,直接使用 cjson
解析,导致nginx 500错误。
最近总是充当救火队员,在几经多手的代码上,修修补补,填坑挖坑,很多时候发现出错的问题都是一些很小的问题,甚至有些读一下文档就可以避免,但还是会被想当然地使用错。另外,缺乏规范,一人一库一风格,改起来也着实费劲。算下来自己也快有两年的 ngx-lua
经验,但规范还是要多看看春哥的代码,多看多学习~