多級緩存-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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM