1. 程式人生 > >基於Memcached+Nginx+Lua的商品詳情頁html快取方案

基於Memcached+Nginx+Lua的商品詳情頁html快取方案

Memcached是一個高效能的K-V鍵值對快取,其設計的目的就是為了減少與資料庫的互動,儲存靜態資料資訊,與Redis的區別。

前言

在設計商品詳情頁時,由於資訊載入較多,需要考慮載入時間、效能問題。說白了既要保證使用者體驗,下單流程的完整性,又要儘量減少與資料庫的互動。而這部分資料偏靜態,這部分資料可以做後臺渲染和快取。我們將詳情頁按照業務模組和性質拆分成多個子連結訪問,這裡以商品資訊展示(名稱、價格、折扣等)為例進行說明。
總結一下我們要做的一件事:後端渲染,減少與資料庫的訪問,以提高頁面開啟速度。

檔案樹



Memcached

這裡用的memcached的windows版本

,裡面也包含了啟動教程,不再做過多的解釋。

Nginx

首先看Nginx的配置檔案

server{
	listen 8888;
	server_name localhost;
	lua_code_cache on;
	location /memcached {
		root lua;
		content_by_lua_file '../openresty-1.13.6.1-win32/lua/memcached.lua';
	}
}

將傳送到下面的請求對映到memcached.lua檔案

http://localhost:8888/memcached

lua

lua一共做了兩件事:memcached互動和後端渲染
下面看lua檔案的邏輯

local memcached = require "resty.memcached"
--建立memcached例項
local memc, err = memcached:new()
  if not memc then
      ngx.say("failed to instantiate memc: ", err)
      return
  end
--設定請求超時時間
  memc:set_timeout(1000) -- 1 sec
  --建立連線
  local ok, err = memc:connect("127.0.0.1", 11211)
  if not ok then
      ngx.say("failed to connect: ", err)
      return
  end

--根據傳入的productId做為鍵
  local res, flags, err = memc:get("key_demo")
  if err then
      ngx.say("failed to get key_demo: ", err)
      return
  end

  if not res then
      ngx.say("key_demonot found")
      return
  end

  ngx.say("key_demo: ", res)
--memcached儘量設定長連線,減少3次握手的時間
  local ok, err = memc:set_keepalive(10000, 100)
  if not ok then
      ngx.say("cannot set keepalive: ", err)
      return
  end

以上是建立lua與memcached連線的幾個過程,下面根據思路來依次介紹整個流程。

獲取url的傳參

args=ngx.req.get_uri_args()
local key = args["productId"]

獲取資料

給服務端傳送http請求獲取資料庫資料(第一次訪問時,memcached中並沒有資料)

local http = require "resty.http"
local httpc = http.new()
local cjon=require "cjson"
local url= ""
local res,err = httpc.request_uri(url,{method="GET",path="/api/product?productId="..key})
local result
if not res then
	ngx.log(ngx.WARN,'failed to request',err)
	return
else
	result =cjson.decode(res.body)

資料渲染

將json資料渲染到html中,首先建立html模版productdetail.html。css略,但是貌似css只能用style標籤和html寫在一起。

<div>
	<table>
		<tr>
			<th>商品名稱</th>
			<th>{{product.ProductName}}</th>
		</tr>
		<tr>
			<th>商品價格</th>
			<th>{{product.ProductPrice}}</th>
		</tr>
		<tr>
			<th>商品折扣率</th>
			<th>{{product.ProductRate}}</th>
		</tr>
	</table>
</div>

模版寫完後,如果沒配置路徑,和lua檔案放到同一級下。
渲染方式,接上面的,result是返回的json反序列化串。

local template = require "resty.template"
local context = {prodct=result}
local func = template.compile("productdetail.html")
local content = func(context)

這時候content裡存出的是渲染後的html程式碼,可以把這段程式碼直接輸出給前端。

程式碼

memcached.lua

local memcached = require "resty.memcached"
local cjon=require "cjson"
args=ngx.req.get_uri_args()
local key = args["productId"]
--建立memcached例項
local memc, err = memcached:new()
  if not memc then
      ngx.say("failed to instantiate memc: ", err)
      return
  end

--設定請求超時時間
memc:set_timeout(1000) -- 1 sec
--建立連線
local ok, err = memc:connect("127.0.0.1", 11211)
if not ok then
    ngx.say("failed to connect: ", err)
    return
end
--設定長連線
local ok ,err = memc:set_keepalive(10000,100)
if not ok then
    ngx.say("failed to keepalive", err)
    return
end
--根據傳入的productId做為鍵
local res, flags, err = memc:get(key)
if not res then
    --從資料庫拿資料
	local template = require "resty.template"
	local http = require "resty.http"
	local httpc = http.new()
	local cjon=require "cjson"
	local url= ""
	local res,err = httpc.request_uri(url,{method="GET",path="/api/product?productId="..key})
	local result
	if not res then
		ngx.log(ngx.WARN,'failed to request',err)
		return
	else
		result =cjson.decode(res.body)
	end
	local context = {prodct=result}
	local func = template.compile("productdetail.html")
	local content = func(context)
	local ok, err = memc:set(key,content)
	if not ok then
		ngx.say("failed to insert to memcached")
		return
	end
	ngx.say(content)
end
--從快取讀
ngx.say(res)

productdetail.html程式碼

<div>
	<table>
		<tr>
			<th>商品名稱</th>
			<th>{{product.ProductName}}</th>
		</tr>
		<tr>
			<th>商品價格</th>
			<th>{{product.ProductPrice}}</th>
		</tr>
		<tr>
			<th>商品折扣率</th>
			<th>{{product.ProductRate}}</th>
		</tr>
	</table>
</div>

nginx配置

worker_processes 1;
events{
	worker_connections 1024;
}
http {
	lua_package_path "../openresty-1.13.6.1-win32/lua/?.lua;../openresty-1.13.6.1-win32/lualib/?.lua"
	server{
	listen 8888;
	server_name localhost;
	lua_code_cache on;
	location /memcached {
		root lua;
		content_by_lua_file '../openresty-1.13.6.1-win32/lua/memcached.lua';
	}
}

Memcached連線

telnet 127.0.0.1 11211

可以檢視記憶體分塊,他這裡叫chunk。使用情況。

效果

第一次讀庫,介面520ms
在這裡插入圖片描述

第二次訪問,從memcacahed中讀,72ms
在這裡插入圖片描述