HM-SpringCloud微服務系列11.3.5【實現多級快取(4)】
阿新 • • 發佈:2022-05-05
6. 查詢Redis快取
現在,Redis快取已經準備就緒,我們可以再OpenResty中實現查詢Redis的邏輯了。如下圖紅框所示:
當請求進入OpenResty之後:
- 優先查詢Redis快取
- 如果Redis快取未命中,再查詢Tomcat
6.1 封裝Redis工具
OpenResty提供了操作Redis的模組,我們只要引入該模組就能直接使用。
但是為了方便,我們將Redis操作封裝到之前的common.lua工具庫中。
修改/usr/local/openresty/lualib/common.lua
檔案:
完整的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
6.2 實現Redis查詢
修改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))
6.3 測試
瀏覽器訪問http://localhost/item.html?id=10004一次(此時本地idea中tomcat是執行狀態)
然後停掉tomcat
再訪問一次
老師演示的還可以正常訪問,我的報500,openResty有問題,lua程式碼問題?;難道是因為redis亂碼導致?
發現還能正常訪問,說明這時的資料流來自虛擬機器的redis快取(本地遠端檢視一下虛擬機器的redis資料,如下)
看一下openResty的日誌
應該就是redis快取亂碼的問題,因為key對不上所以查不到資料
6.4 RedisTempalte亂碼問題解決
參考:
https://blog.csdn.net/hy8363321/article/details/108967526
https://blog.csdn.net/qq_33764491/article/details/80955772
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;
}
}
亂碼問題解決,[4.7.3 測試]正常了,即在停止tomcat後再次訪問http://localhost/item.html?id=10004時可以直接從虛擬機器的redis快取中獲取資料,正常訪問資料,如下