因為用nginx+lua去開發,所以會選擇用最流行的開源方案,就是用OpenResty
nginx+lua打包在一起,而且提供了包括redis客戶端,mysql客戶端,http客戶端在內的大量的組件
1、部署第一個nginx,作為應用層nginx
(1)部署openresty
mkdir -p /usr/servers cd /usr/servers/ yum install -y readline-devel pcre-devel openssl-devel gcc wget http://openresty.org/download/ngx_openresty-1.7.7.2.tar.gz tar -xzvf ngx_openresty-1.7.7.2.tar.gz cd /usr/servers/ngx_openresty-1.7.7.2/ cd bundle/LuaJIT-2.1-20150120/ make clean && make && make install ln -sf luajit-2.1.0-alpha /usr/local/bin/luajit cd bundle wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz tar -xvf 2.3.tar.gz cd bundle wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/v0.3.0.tar.gz tar -xvf v0.3.0.tar.gz cd /usr/servers/ngx_openresty-1.7.7.2 ./configure --prefix=/usr/servers --with-http_realip_module --with-pcre --with-luajit --add-module=./bundle/ngx_cache_purge-2.3/ --add-module=./bundle/nginx_upstream_check_module-0.3.0/ -j2 make && make install cd /usr/servers/ ll /usr/servers/luajit /usr/servers/lualib /usr/servers/nginx /usr/servers/nginx/sbin/nginx -V 啟動nginx: /usr/servers/nginx/sbin/nginx
vi /usr/servers/nginx/conf/nginx.conf
在http部分添加:
lua_package_path "/usr/servers/lualib/?.lua;;";
lua_package_cpath "/usr/servers/lualib/?.so;;";
/usr/servers/nginx/conf下,創建一個lua.conf
server {
listen 80;
server_name _;
}
在nginx.conf的http部分添加:
include lua.conf;
驗證配置是否正確:
/usr/servers/nginx/sbin/nginx -t
在lua.conf的server部分添加:
location /lua {
default_type 'text/html';
content_by_lua 'ngx.say("hello world")';
}
/usr/servers/nginx/sbin/nginx -t
重新nginx加載配置
/usr/servers/nginx/sbin/nginx -s reload
訪問http: http://192.168.31.187/lua
vi /usr/servers/nginx/conf/lua/test.lua
ngx.say("hello world");
修改lua.conf
location /lua {
default_type 'text/html';
content_by_lua_file conf/lua/test.lua;
}
查看異常日志
tail -f /usr/servers/nginx/logs/error.log
(3)工程化的nginx+lua項目結構
項目工程結構
hello
hello.conf
lua
hello.lua
lualib
*.lua
*.so
放在/usr/hello目錄下
/usr/servers/nginx/conf/nginx.conf
worker_processes 2;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type text/html;
lua_package_path "/usr/hello/lualib/?.lua;;";
lua_package_cpath "/usr/hello/lualib/?.so;;";
include /usr/hello/hello.conf;
}
/usr/hello/hello.conf
server {
listen 80;
server_name _;
location /lua {
default_type 'text/html';
lua_code_cache off;
content_by_lua_file /usr/example/lua/test.lua;
}
}
用eshop-cache01和eshop-cache02作為應用層nginx服務器,用eshop-cache03作為分發層nginx
在eshop-cache03,也就是分發層nginx中,編寫lua腳本,完成基於商品id的流量分發策略
步驟:
1、獲取請求參數,比如productId
2、對productId進行hash
3、hash值對應用服務器數量取模,獲取到一個應用服務器
4、利用http發送請求到應用層nginx
5、獲取響應后返回
作為一個流量分發的nginx,會發送http請求到后端的應用nginx上面去,所以要先引入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
代碼:
local uri_args = ngx.req.get_uri_args()
local productId = uri_args["productId"]
local hosts = {"192.168.31.187", "192.168.31.19"}
local hash = ngx.crc32_long(productId)
local index = (hash % 2) + 1
backend = "http://"..hosts[index]
local requestPath = uri_args["requestPath"]
requestPath = "/"..requestPath.."?productId="..productId
local http = require("resty.http")
local httpc = http.new()
local resp, err = httpc:request_uri(backend,{
method = "GET",
path = requestPath
})
if not resp then
ngx.say("request error: ", err)
return
end
ngx.say(resp.body)
httpc:close()
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
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
在hello.conf的server中配置模板位置
set $template_location "/templates";
set $template_root "/usr/hello/templates";
mkdir /usr/hello/templates
vi product.html
product id: {* productId *}<br/>
product name: {* productName *}<br/>
product picture list: {* productPictureList *}<br/>
product specification: {* productSpecification *}<br/>
product service: {* productService *}<br/>
product color: {* productColor *}<br/>
product size: {* productSize *}<br/>
shop id: {* shopId *}<br/>
shop name: {* shopName *}<br/>
shop level: {* shopLevel *}<br/>
shop good cooment rate: {* shopGoodCommentRate *}<br/>
hello.conf中: lua_shared_dict my_cache 128m;
lua腳本中:
local uri_args = ngx.req.get_uri_args()
local productId = uri_args["productId"]
local shopId = uri_args["shopId"]
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)
if productCache == "" or productCache == nil then
local http = require("resty.http")
local httpc = http.new()
local resp, err = httpc:request_uri("http://192.168.31.179:8080",{
method = "GET",
path = "/getProductInfo?productId="..productId
})
productCache = resp.body
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.31.179:8080",{
method = "GET",
path = "/getShopInfo?shopId="..shopId
})
shopCache = resp.body
cache_ngx:set(shopCacheKey, shopCache, 10 * 60)
end
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本地緩存中是取不到的,所以會發送http請求到后端的緩存服務里去獲取,會從redis中獲取
拿到數據以后,會放到nginx本地緩存里面去,過期時間是10分鍾
然后將所有數據渲染到模板中,返回模板
以后再來訪問的時候,就會直接從nginx本地緩存區獲取數據了
緩存數據生產 -> 有數據變更 -> 主動更新兩級緩存(ehcache+redis)-> 緩存維度化拆分
分發層nginx + 應用層nginx -> 自定義流量分發策略提高緩存命中率
nginx shared dict緩存 -> 緩存服務 -> redis -> ehcache -> 渲染html模板 -> 返回頁面
如果你的數據在nginx -> redis -> ehcache三級緩存都不在了,可能就是被LRU清理掉了
這個時候緩存服務會重新拉去數據,去更新到ehcache和redis中
