多级缓存-nginx(OpenResty)本地缓存

在查询商品时,优先查询OpenResty的本地缓存,需求:
1.修改item.lua中的read_data函数,优先查询本地缓存,未命中时再查询Redis、Tomcat
2.查询Redis或Tomcat成功后,将数据写入本地缓存,并设置有效期
3.商品基本信息,有效期30分钟
4.库存信息,有效期1分钟

1、nginx.conf 

#user  nobody;
worker_processes  1;
error_log  logs/error.log;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    access_log "D:/dev/openresty-1.19.9.1/logs/item.log";  
    error_log "D:/dev/openresty-1.19.9.1/logs/item.error" info; 
    
    #加载lua 模块
    lua_package_path "D:/dev/openresty-1.19.9.1/lualib/?.lua;;";  
    #加载c模块     
    lua_package_cpath "D:/dev/openresty-1.19.9.1/lualib/?.so;;"; 
    # 添加共享字典,也就是本地缓存,名称叫做:item_cache,大小150m
    lua_shared_dict item_cache 150m;
 
    
    server {
        listen       80;
        server_name  localhost;
        
        location /item {
            proxy_pass http://192.168.8.70:8081;
            proxy_redirect  off;
            proxy_send_timeout 3000;
            proxy_read_timeout 3000;
            proxy_connect_timeout 3000;
        }

        location ~ /item/(d+) {
            # 响应类型,这里返回json
            default_type application/json;
            # 响应数据由 lua/item.lua这个文件来决定
            content_by_lua_file lua/item.lua;
        }
 
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

2、item.lua脚本

-- 引入自定义工具模块
local common = require("common")
local read_http=common.read_http
local read_redis=common.read_redis

-- 导入cjson库
local cjson = require("cjson")

--获取本地缓存对象
local item_cache=ngx.shared.item_cache

-- 封装函数,先查询redis,再查询http
local function read_data1(key, expire, path, params)
    ngx.log(ngx.INFO, "path=", path)
    -- 查询本地缓存
    local val = item_cache:get(key)
    if not val then
        ngx.log(ngx.ERR, "本地缓存查询失败,尝试查询redis,key:", key)
        -- 查询redis
        val = read_redis("127.0.0.1", 6379, key)
        ngx.log(ngx.INFO, "查询Redis数据, val = ", val)
        -- 判断redis是否命中
        if not val then
            ngx.log(ngx.INFO, "Redis查询失败,尝试查询http")
            ngx.log(ngx.INFO, path, params)
            -- Redis查询失败,查询http
            val = read_http(path, params)
        end
    end
    
    ngx.log(ngx.INFO, "val=", val)
    -- 查询成功,把数据写入本地缓存,单位秒
    -- item_cache:set(key, val, expire) 
    -- 返回结果
    return val
end


--获取路径参数
local id=ngx.var[1]

-- 根据id查询商品
local paths = "/item/"..id
local itemJSON=read_data1("item:id:"..id,60,paths,nil)


--根据id查询商品库存
paths = "/item/stock/"..id
local itemStockJSON=read_data1("item:stock:id:"..id,paths,nil)


-- JSON转换为lua的table
local item = cjson.decode(itemJSON)
local itemStock = cjson.decode(itemStockJSON)


-- 组合数据
item.stock = itemStock.stock -- 商品库存
item.sold = itemStock.sold -- 商品销量
 
-- 把item系列化JSON,并返回结果
ngx.say(item.name)

3、common.lua工具类

--引入redis模块
local redis = require("resty.redis")
--初始化Redis对象
local red = redis:new()
--设置Redis超时时间
red:set_timeouts(5000,5000,5000)


-- 释放Redis连接API
-- 关闭redis连接的工具方法,其实是放入连接池
local function close_redis(red)
    local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
    if not ok then
        ngx.log(ngx.ERR, "放入redis连接池失败: ", err)
    end
end


-- 读取Redis数据的API
-- 查询redis的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)
    -- 获取一个连接
    local ok, err = red:connect(ip, port)
    -- ok, err = red:auth("123456") -- redis设置的密码
    if not ok then
        ngx.log(ngx.ERR, "连接redis失败 : ", err)
        return nil
    end
    
    -- 查询redis
    local resp, err = red:get(key)
    -- 查询失败处理
    if not resp then
        resp = nil
        ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
    end
    --得到的数据为空处理
    if resp == ngx.null then
        resp = nil
        ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
    end
    close_redis(red)
    return resp
end

-- 封装函数,发送http请求,并解析响应
local function read_http(path, params)
    ngx.log(ngx.INFO, "http请求进来了path:", path,",params:", params)
    local resp = ngx.location.capture(path,{
        method = ngx.HTTP_GET,
        args = params,
    })
    
    ngx.log(ngx.INFO, "http请求返回结果,resp.status:", resp.status, ",resp.body:", resp.body)
    if not resp then
        -- 记录错误信息,返回404
        ngx.log(ngx.ERR, "http not found, path: ", path , ", args: ", args)
        ngx.exit(404)
    end
     
    return resp.body
end



-- 将方法导出
local _M = {  
    read_http = read_http,
    read_redis = read_redis
}  
return _M

 

原文地址:https://www.cnblogs.com/linjiqin/p/15474037.html