• 背景

    我们的服务本来使用一组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经验,但规范还是要多看看春哥的代码,多看多学习~