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

6. 查询Redis缓存

现在,Redis缓存已经准备就绪,我们可以再OpenResty中实现查询Redis的逻辑了。如下图红框所示:

image-20220505101537924

当请求进入OpenResty之后:

  • 优先查询Redis缓存
  • 如果Redis缓存未命中,再查询Tomcat

6.1 封装Redis工具

OpenResty提供了操作Redis的模块,我们只要引入该模块就能直接使用。

但是为了方便,我们将Redis操作封装到之前的common.lua工具库中。

修改/usr/local/openresty/lualib/common.lua文件:

image-20220505101834382

image-20220505101844180


image-20220505102247227

image-20220505102133865


完整的common.lua:

-- 导入redis
local redis = require('resty.redis')
-- 初始化redis对象
local red = redis:new()
-- 设置redis超时时间(1000ms=1s,建立连接、发送请求、接收响应)
red:set_timeouts(1000, 1000, 1000)

-- 关闭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的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)
    -- 获取一个连接
    local ok, err = red:connect(ip, port)
    if not ok then
        ngx.log(ngx.ERR, "连接redis失败 : ", err)
        return nil
    end
    -- 查询redis
    local resp, err = red:get(key)
    -- 查询失败处理
    if not resp then
        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)
    local resp = ngx.location.capture(path,{
        method = ngx.HTTP_GET,
        args = params,
    })
    if not resp then
        -- 记录错误信息,返回404
        ngx.log(ngx.ERR, "http请求查询失败, path: ", path , ", args: ", args)
        ngx.exit(404)
    end
    return resp.body
end

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

image-20220505103258543

6.2 实现Redis查询

image-20220505103423654

修改item.lua文件,实现对Redis的查询了。查询逻辑是:

  • 根据id查询Redis
  • 如果查询失败则继续查询Tomcat
  • 将查询结果返回

1)添加一个查询函数:

-- 导入common函数库
local common = require('common')
local read_http = common.read_http
local read_redis = common.read_redis
-- 封装查询函数
function read_data(key, path, params)
    -- 查询本地缓存
    local resp = read_redis("127.0.0.1", 6379, key) -- 因为本代码是由openResty运行的,而redis和openResty都在同一台虚拟机中,127.0.0.1代表本地
    -- 判断查询结果
    if not val then
        ngx.log(ngx.ERR, "redis查询失败,尝试查询http, key: ", key)
        -- redis查询失败,去查询http
        val = read_http(path, params)
    end
    -- 返回数据
    return val
end

2)修改商品查询、库存查询的业务:

-- 查询商品信息
local itemJSON = read_data("item:id:" .. id,  "/item/" .. id, nil)
-- 查询库存信息
local stockJSON = read_data("item:stock:id:" .. id, "/item/stock/" .. id, nil)

完整的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")

-- 封装查询函数
function read_data(key, path, params)
    -- 查询redis本地缓存
    local resp = read_redis("127.0.0.1", 6379, key)
    -- 判断查询结果
    if not resp then
        ngx.log(ngx.ERR, "redis查询失败,尝试查询http, key: ", key)
        -- redis查询失败,去查询http
        resp = read_http(path, params)
    end
    -- 返回数据
    return resp
end

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

-- 根据id查询商品
-- local itemJSON = read_http("/item/".. id, nil)
local itemJSON = read_data("item:id:"..id, "/item/"..id, nil)
-- 根据id查询商品库存
-- local itemStockJSON = read_http("/item/stock/".. id, nil)
local itemStockJSON = read_data("item:stock:id:"..id, "/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))

image-20220505104354429

image-20220505104517662

6.3 测试

image-20220505104632914

浏览器访问http://localhost/item.html?id=10004一次(此时本地idea中tomcat是运行状态)

image-20220505105638969

然后停掉tomcat

image-20220505105752480

image-20220505105802816

再访问一次

image-20220505114129826

image-20220505114150782

老师演示的还可以正常访问,我的报500,openResty有问题,lua代码问题?;难道是因为redis乱码导致?

发现还能正常访问,说明这时的数据流来自虚拟机的redis缓存(本地远程查看一下虚拟机的redis数据,如下)

image-20220505114300843

看一下openResty的日志

image-20220505121558005

image-20220505121712833

应该就是redis缓存乱码的问题,因为key对不上所以查不到数据

6.4 RedisTempalte乱码问题解决

参考:
https://blog.csdn.net/hy8363321/article/details/108967526
https://blog.csdn.net/qq_33764491/article/details/80955772

image-20220505201436899

package com.heima.item.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * RedisTemplate,在传递String类型的数据结构后,查看redis缓存会发现数据乱码现象,需要修改RedisTemplate的序列化策略
 * https://blog.csdn.net/qq_33764491/article/details/80955772
 * https://blog.csdn.net/hy8363321/article/details/108967526
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        
        /*// 使用Jackson2JsonRedisSerialize替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);*/
        
        // 设置key和value的序列化规则
//        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        /*redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());*/
        redisTemplate.afterPropertiesSet();
        
        return redisTemplate;
    }
}

image-20220505201154074

image-20220505201206057

乱码问题解决,[4.7.3 测试]正常了,即在停止tomcat后再次访问http://localhost/item.html?id=10004时可以直接从虚拟机的redis缓存中获取数据,正常访问数据,如下

image-20220505201827978

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