HM-SpringCloud微服务系列11.3.6【实现多级缓存(5)】

7. Nginx本地缓存

现在,整个多级缓存中只差最后一环,也就是nginx的本地缓存了。如图:

image-20220505202712143

7.1 本地缓存API

OpenResty为Nginx提供了shard dict的功能,可以在nginx的多个worker之间共享数据,实现缓存功能。

7.1.1 开启共享字典

在nginx.conf的http下添加配置:

 # 共享字典,也就是本地缓存,名称叫做:item_cache,大小150m
 lua_shared_dict item_cache 150m; 

image-20220505210015134

7.1.2 操作共享字典

在item.lua中导入并使用本地缓存:

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

-- 以下为操作示例
-- 存储, 指定key、value、过期时间,单位s,默认为0代表永不过期
item_cache:set('key', 'value', 1000)
-- 读取
local val = item_cache:get('key')

image-20220505210517531

7.2 实现本地缓存查询[案例]

7.2.1 案例需求

image-20220505210614515

7.2.2 实现

1)修改/usr/local/openresty/lua/item.lua文件,修改read_data查询函数,添加本地缓存逻辑:

-- 封装查询函数(expire是有效期)
function read_data(key, expire, path, params)
	-- 查询本地缓存
	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) 
		-- 因为本代码是由openResty运行的,而redis和openResty都在同一台虚拟机中,127.0.0.1代表本地
		-- 判断redis查询结果
		if not val then
			ngx.log(ngx.ERR, "redis查询失败,尝试查询http, key: ", key)
			-- redis查询失败,去查询http
			val = read_http(path, params)
		end
	end
	-- 查询成功,先把数据写入本地缓存
	item_cache:set(key, val, expire)
    -- 然后再返回数据
    return val
end

2)修改item.lua中查询商品和库存的业务,实现最新的read_data函数:

-- 根据id查询商品
-- local itemJSON = read_http("/item/" .. id, nil)
-- local itemJSON = read_data("item:id:" .. id, "/item/" .. id, nil)
local itemJSON = read_data("item:id:" .. id, 1800, "/item/" .. id, nil)

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

​ 其实就是多了缓存时间参数,过期后nginx缓存会自动删除,下次访问即可更新缓存。

​ 这里给商品基本信息设置超时时间为30分钟,库存为1分钟。

​ 因为库存更新频率较高,如果缓存时间过长,可能与数据库差异较大。

3)完整的item.lua文件:

-- 引入自定义common工具模块,返回值是common中返回的 _M
local common = require("common")
-- 从 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

-- 封装查询函数(expire是有效期)
function read_data(key, expire, path, params)
	-- 查询本地缓存
	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) 
		-- 因为本代码是由openResty运行的,而redis和openResty都在同一台虚拟机中,127.0.0.1代表本地
		-- 判断redis查询结果
		if not val then
			ngx.log(ngx.ERR, "redis查询失败,尝试查询http, key: ", key)
			-- redis查询失败,去查询http
			val = read_http(path, params)
		end
	end
	-- 查询成功,先把数据写入本地缓存
	item_cache:set(key, val, expire)
    -- 然后再返回数据
    return val
end

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

-- 根据id查询商品
-- local itemJSON = read_http("/item/" .. id, nil)
-- local itemJSON = read_data("item:id:" .. id, "/item/" .. id, nil)
local itemJSON = read_data("item:id:" .. id, 1800, "/item/" .. id, nil)

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

-- JSON转换为lua的table(反序列化)
local item = cjson.decode(itemJSON)
local stock = cjson.decode(itemStockJSON)
-- 组合数据(item中原本没有但现在需要的数据去stock中取)
item.stock = stock.stock
item.sold = stock.sold

-- 把item序列化为json返回结果
ngx.say(cjson.encode(item))

7.2.3 测试

image-20220505211707507

通过查看日志文件进行验证nginx本地缓存是否生效

image-20220505211833175

访问http://localhost/item.html?id=10001

image-20220505212234055

image-20220505212409521

可以看到第一次访问时,本地缓存中并没有数据,需要去查询redis

此时,再去刷新几次页面,重新访问几次

image-20220505212625371

image-20220505212740535

可以看到并没有输出“本地缓存查询失败”,说明刚刚第一次访问时,redis查完后先存入了本地缓存,而之后的访问就可以直接从本地缓存中查询了而不需再走redis

PS:红×掉的报错是因为前后两次访问时间间隔过长,超过了过期时间QAQ

此时,实际上就算删掉redis中10001的数据,也可以正常访问,因为本地缓存中已有数据

image-20220505213255673

image-20220505213315864

image-20220505213355973

而且可以发现,走本地缓存时,速度非常快,仅耗时6ms

posted @   yub4by  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示