OpenResty部署nginx及nginx+lua


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

  

(2)nginx+lua開發的hello world
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()

  

分發層nginx,lua應用,會將商品id,商品店鋪id,都轉發到后端的應用nginx
位置:/usr/servers/nginx/sbin/nginx -s reload

 

1、應用nginx的lua腳本接收到請求

2、獲取請求參數中的商品id,以及商品店鋪id

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

4、如果在nginx本地緩存中沒有獲取到數據,那么就到redis分布式緩存中獲取數據,如果獲取到了數據,還要設置到nginx本地緩存中

但是這里有個問題,建議不要用nginx+lua直接去獲取redis數據

因為openresty沒有太好的redis cluster的支持包,所以建議是發送http請求到緩存數據生產服務,由該服務提供一個http接口

緩存數生產服務可以基於redis cluster api從redis中直接獲取數據,並返回給nginx
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 

  

5、如果緩存數據生產服務沒有在redis分布式緩存中沒有獲取到數據,那么就在自己本地ehcache中獲取數據,返回數據給nginx,也要設置到nginx本地緩存中

6、如果ehcache本地緩存都沒有數據,那么就需要去原始的服務中拉去數據,該服務會從mysql中查詢,拉去到數據之后,返回給nginx,並重新設置到ehcache和redis中
7、nginx最終利用獲取到的數據,動態渲染網頁模板
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/>

  

8、將渲染后的網頁模板作為http響應,返回給分發層nginx
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中


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM