因為用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中