OpenResty缓存

缓存原则

缓存是一个大型系统中非常重要的一个组成部分。一个生产环境的缓存,需要根据自己的业务瓶颈制作合理的缓存方案。一般来说缓存有两个原则:

  1. 缓存越靠近用户的请求越好:能在用户本地的,就不要再发,能使用cdn的就不要回源服务器。
  2. 尽量使用本机和本进程的缓存解决,因为跨机器或者跨机房会造成带宽压力和延迟。

OpenResty缓存

OpenResty自带的缓存机制,是提前创建了一块固定大小的共享内存,所有的worker都可以(加锁)使用。内部使用LRU算法,过期的数据将被清除。

完整示例:

worker_processes  1;
error_log logs/error.log info;

events {
    worker_connections 512;
}

http {

    log_format myformat '$remote_addr $status $time_local';
    access_log logs/access.log myformat;

    lua_shared_dict mycache 8M;

    server {
        listen 8080;
        charset utf-8;

        location /redis {
            content_by_lua_file lua/redis_exp.lua;
        }

        location /mixed {
            content_by_lua_block {
                function get_cache(key)
                    local cache = ngx.shared.mycache
                    local value = cache:get(key)
                    return value
                end

                function set_cache(key, value, expire)
                    if not expire then
                        expire = 0
                    end

                    local cache = ngx.shared.mycache
                    local succ, err, forcible = cache:set(key, value, expire)
                    return succ
                end

                local sum = get_cache("sum")
                if not sum then
                    set_cache("sum", 1)
                    sum = 1
                end
                sum = sum + 1
                set_cache("sum", sum)
                ngx.say("sum ", sum)
            }
        }
    }
}

Lua LRU cache

这个引入了一个Lua的外部库,在单个worker中各个请求之间进行数据共享,这里尤其是要注意库的使用方式,因为不同的调用方式会导致缓存失效。

我们先给出正确的版本:

worker_processes  1;
error_log logs/error.log info;

events {
    worker_connections 512;
}

http {

    log_format myformat '$remote_addr $status $time_local';
    access_log logs/access.log myformat;

    lua_package_path "/home/zhoulihai/Desktop/work/lua/?.lua;;";

    server {
        listen 8080;
        charset utf-8;

        location /redis {
            content_by_lua_file lua/redis_exp.lua;
        }

        location /mixed {
            content_by_lua '
                require("lru").go()
            ';
        }
    }
}

这里主要注意两点:lua_package_path "/home/zhoulihai/Desktop/work/lua/?.lua;;";content_by_lua

第一个参数是指定lua脚本的位置,作用是为第二句话打基础,直接使用content_by_lua_file来调用lua脚本使用的位置变量和content_by_lua脚本的位置变量是不同的。这里需要声明一下位置。

第二个地方应该是这样使用content_by_lua,因为这个缓存不能每次都创建一个新的,利用的是OpenResty的只加载一次lua代码的特性,使创建缓存的代码只运行一次。还有一种方法是另创建一个lua脚本来调用这句话。

lru.lua文件:

local _M = {}

local lrucache = require "resty.lrucache"

local c, err = lrucache.new(200)
if not c then
    return error("error on new lrucache: ", err or "unknow")
end

function _M:go()
    local sum = c:get("sum")
    if not sum then
        c:set("sum", 0)
        sum = 0
    end
    sum = sum + 1
    c:set("sum", sum)
    ngx.say("sum ", sum)
end

return _M

这样每访问一次都会增加一个计数。

另一个演示

location /mixed {
    content_by_lua_file lua/lru_t.lua;
}

lru_t.lua文件:

require("lru").go()