38. 【實戰】基於nginx+lua+java完成多級緩存架構的核心業務邏輯

分發層nginx,lua應用,會將商品id,商品店鋪id,都轉發到後端的應用nginx

nginx+lua+java多級緩存流程

修改分發層nginx

  1. 分發層nginxeshop-cache03<192.168.0.108>vi /usr/hello/hello.conf
    在這裏插入圖片描述
location /product {
        default_type 'text/html';
        # lua_code_cache off;
        content_by_lua_file /usr/hello/lua/distribute.lua;
    }
  1. 修改lua腳本: vi /usr/hello/lua/distribute.lua
local uri_args = ngx.req.get_uri_args()
local productId = uri_args["productId"]
local shopId = uri_args["shopId"]

local host = {"192.168.0.106", "192.168.0.107"}
local hash = ngx.crc32_long(productId)
local index = (hash % 2) + 1
local backend = "http://"..host[index]

local requestPath = uri_args["requestPath"]
requestPath = "/"..requestPath.."?productId="..productId.."&shopId="..shopId

local http = require("resty.http")
local httpc = http.new()

local resp, err = httpc:request_uri(backend, {
    method = "GET",
    path = requestPath,
    keepalive=false
})

if not resp then
    ngx.say("request error :", err)
    return
end
ngx.say(resp.body)
httpc:close()
  1. 重新加載nginx配置:
# 驗證配置
/usr/servers/nginx/sbin/nginx -t
# 啓動nginx
/usr/servers/nginx/sbin/nginx
# 重載配置
/usr/servers/nginx/sbin/nginx -s reload

配置應用層nginx

  1. 應用層nginx的lua腳本接收到之前分發層nginx分發的請求

  2. 獲取請求參數中的商品id:productId,以及商品店鋪id:shopId

  3. 根據商品id和商品店鋪id,在nginx本地緩存中嘗試獲取數據

  4. 如果在nginx本地緩存中沒有獲取到數據,那麼就到redis分佈式緩存服務中獲取數據,獲取到了數據後,還要設置到nginx本地緩存中

但是這裏有個問題,建議不要用nginx+lua直接去獲取redis數據,因爲openresty沒有太好的redis cluster的支持包,所以建議是發送http請求到緩存數據生產服務,由該服務提供一個http接口

  1. 緩存數據生產服務可以基於redis cluster api從redis中直接獲取數據,並返回給nginx

應用層nginx會發送http請求到後端的緩存數據服務,所以eshop-cache01eshop-cache02也要先引入lua http lib

cd /usr/hello/lualib/resty/  
wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua  
wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua 
  1. 如果緩存數據生產服務沒有在redis分佈式緩存中沒有獲取到數據,那麼就在自己本地ehcache中獲取數據,返回數據給nginx,也要設置到nginx本地緩存中

  2. 如果ehcache本地緩存都沒有數據,那麼就需要去原始的服務中拉去數據,該服務會從mysql中查詢,拉去到數據之後,返回給nginx,並重新設置到ehcache和redis中

後面會講分佈式緩存重建併發衝突的問題和解決方案

  1. 應用層nginx最終利用獲取到的數據,動態渲染網頁模板

eshop-cache01eshop-cache02引入lua

cd /usr/hello/lualib/resty/
wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template.lua
mkdir /usr/hello/lualib/resty/html
cd /usr/hello/lualib/resty/html
wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template/html.lua
  1. vi /usr/hello/hello.conf:配置模板位置
    在這裏插入圖片描述
set $template_location "/templates";  
set $template_root "/usr/hello/templates";
  1. 創建頁面模板目錄和文件
mkdir /usr/hello/templates
vi product.html
<html>
        <head>
                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
                <title>商品詳情頁</title>
        </head>
<body>
商品id: {* productId *}<br/>
商品名稱: {* productName *}<br/>
商品圖片列表: {* productPictureList *}<br/>
商品規格: {* productSpecification *}<br/>
商品售後服務: {* productService *}<br/>
商品顏色: {* productColor *}<br/>
商品大小: {* productSize *}<br/>
店鋪id: {* shopId *}<br/>
店鋪名稱: {* shopName *}<br/>
店鋪等級: {* shopLevel *}<br/>
店鋪好評率: {* shopGoodCommentRate *}<br/>
</body>
</html>
  1. 配置使用nginx的本地緩存
    在這裏插入圖片描述
vi /usr/servers/nginx/conf/nginx.conf

# nginx配置全局本地緩存名稱:my_cache,內存大小:128m
lua_shared_dict my_cache 128m;
  1. 將渲染後的網頁模板作爲http響應,返回給分發層nginx
    在這裏插入圖片描述
vi /usr/hello/hello.conf

location /product {
    default_type 'text/html';
    content_by_lua_file /usr/hello/lua/product.lua;
}
  1. vi /usr/hello/lua/product.lua 腳本:
-- 獲取請求參數
local uri_args = ngx.req.get_uri_args()
local productId = uri_args["productId"]
local shopId = uri_args["shopId"]
-- 獲取nginx緩存
local cache_ngx = ngx.shared.my_cache

local productCacheKey = "product_info_"..productId
local shopCacheKey = "shop_info_"..shopId

local productCache = cache_ngx:get(productCacheKey)
local shopCache = cache_ngx:get(shopCacheKey)
-- 如果nginx本地緩存沒有,發送請求到緩存服務
if productCache == "" or productCache == nil then
	local http = require("resty.http")
	local httpc = http.new()
-- 此處ip地址爲你java服務部署或測試啓動地址
	local resp, err = httpc:request_uri("http://192.168.0.113:8080",{
  		method = "GET",
  		path = "/getProductInfo?productId="..productId,
-- lua lib庫bug,必須設置下面參數
  		keepalive=false
	})

	productCache = resp.body
-- 設置到nginx本地緩存中,過期時間10分鐘
	cache_ngx:set(productCacheKey, productCache, 10 * 60)
end

if shopCache == "" or shopCache == nil then
	local http = require("resty.http")
	local httpc = http.new()

	local resp, err = httpc:request_uri("http://192.168.0.113:8080",{
  		method = "GET",
  		path = "/getShopInfo?shopId="..shopId,
  		keepalive=false
	})

	shopCache = resp.body
	cache_ngx:set(shopCacheKey, shopCache, 10 * 60)
end
-- 商品信息和店鋪信息轉成json對象
local cjson = require("cjson")
local productCacheJSON = cjson.decode(productCache)
local shopCacheJSON = cjson.decode(shopCache)

local context = {
	productId = productCacheJSON.id,
	productName = productCacheJSON.name,
	productPrice = productCacheJSON.price,
	productPictureList = productCacheJSON.pictureList,
	productSpecification = productCacheJSON.specification,
	productService = productCacheJSON.service,
	productColor = productCacheJSON.color,
	productSize = productCacheJSON.size,
	shopId = shopCacheJSON.id,
	shopName = shopCacheJSON.name,
	shopLevel = shopCacheJSON.level,
	shopGoodCommentRate = shopCacheJSON.goodCommentRate
}
-- 渲染到模板
local template = require("resty.template")
template.render("product.html", context)

到這裏應用層nginx配置已經結束。

  1. 重新加載nginx配置:

注意以上配置兩個應用層nginx都要配置:eshop-cache01eshop-cache02

# 驗證配置
/usr/servers/nginx/sbin/nginx -t
# 啓動nginx
/usr/servers/nginx/sbin/nginx
# 重載配置
/usr/servers/nginx/sbin/nginx -s reload

緩存服務Java代碼

  1. 可以查看34. 【實戰】基於kafka+ehcache+redis完成緩存數據生產服務的開發與測試,生成測試redis數據

  2. 項目地址參考:0. 【緩存高可用微服務實戰】資料總結
    切換到相應分支:
    在這裏插入圖片描述

測試

  1. 分發層nginxeshop-cache03 [192.168.0.108]發出訪問獲取商品信息和商家信息請求:
  2. 商品請求:http://192.168.0.108/product?requestPath=product&productId=1&shopId=1
  3. 如果報錯,可以查看日誌
    在這裏插入圖片描述
cat /usr/servers/nginx/logs/error.log

在這裏插入圖片描述

我這裏是因爲redis中沒有商家信息緩存,現在也沒有做查詢數據庫操作,lua解碼轉json對象時報錯了。

  1. 成功請求響應:
    在這裏插入圖片描述
    在這裏插入圖片描述

  2. 第一次訪問的時候,在nginx本地緩存中是取不到的,會發送http請求到後端的緩存服務裏去獲取,會從redis中獲取

  3. 拿到數據以後,會放到nginx本地緩存裏面去,過期時間是之前設置的10分鐘

  4. 然後將所有數據渲染到模板中,返回模板

  5. 以後再來訪問的時候,就會直接從nginx本地緩存區獲取數據了

總結

  1. 緩存數據生產 -> 有數據變更 -> 主動更新兩級緩存(ehcache+redis)-> 緩存維度化拆分
  2. 分發層nginx + 應用層nginx -> 自定義流量分發策略提高緩存命中率
  3. nginx shared dict緩存 -> 緩存服務 -> redis -> ehcache -> 渲染html模板 -> 返回頁面

如果數據在nginx -> redis -> ehcache三級緩存都不在了,可能就是被LRU清理掉了

這個時候緩存服務會重新拉去數據,去更新到ehcache和redis中,由此會引發下一篇中的分佈式的緩存重建的併發問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章