基于“分发层+应用层”双层nginx架构提升缓存命中率方案

1.nginx缓存命中率低
在生产环境中,通常会部署多个nginx来提升系统的负载能力,如果nginx开启了本地缓存,对于请求A浏览商品详情页,第一次请求打在了nginxA上,拿到了数据缓存到nginxA本地,第二次请求就有可能打在nginxB上,这就造成nginx的缓存命中率很低的问题。

2.如何提升缓存命中率
采用分发层+应用层,双层nginx,分发层nginx,负责流量分发的逻辑和策略,这个里面它可以根据你自己定义的一些规则,比如根据productId去进行hash,然后对后端的nginx数量取模,将某一个商品的访问的请求,就固定路由到一个nginx后端服务器上去,保证说只会从redis中获取一次缓存数据,后面全都是走nginx本地缓存了,后端的nginx服务器,就称之为应用服务器; 最前端的nginx服务器,被称之为分发服务器,看似很简单,其实很有用,在实际的生产环境中,可以大幅度提升你的nginx本地缓存这一层的命中率,大幅度减少redis后端的压力,提升性能


3.nginx的流量分发的实现原理
在分发层nginx中编写lua脚本,lua去写分发的逻辑,在应用层nginx,用lua去写多级缓存架构存取的控制逻辑。还要做热点数据的自动降级机制,也是用lua脚本去写降级机制的。因为我们要用nginx+lua去开发,所以会选择用最流行的开源方案,就是用OpenResty,nginx+lua打包在一起,而且提供了包括redis客户端,mysql客户端,http客户端在内的大量的组件。


4.实战

1、部署第一个nginx,作为应用层nginx(192.168.31.187那个机器上)

(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://localhost/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; 





在分发层nginx中,编写lua脚本,完成基于商品id的流量分发策略,当然了,这里主要是简化策略,简化业务逻辑,实际上在公司中,你可以随意根据自己的业务逻辑和场景,去制定自己的流量分发策略。

1、获取请求参数,比如productId
2、对productId进行hash
3、hash值对应用服务器数量取模,获取到一个应用服务器
4、利用http发送请求到应用层nginx
5、获取响应后返回

这个就是基于商品id的定向流量分发的策略,lua脚本来编写和实现

我们作为一个流量分发的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 host = {"192.168.31.19", "192.168.31.187"}
local hash = ngx.crc32_long(productId)
hash = (hash % 2) + 1 
backend = "http://"..host[hash]

local method = uri_args["method"]
local requestBody = "/"..method.."?productId="..productId

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

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

if not resp then 
ngx.say("request error :", err) 
return 
end

ngx.say(resp.body) 

httpc:close() 

/usr/servers/nginx/sbin/nginx -s reload

基于商品id的定向流量分发策略的lua脚本就开发完了,而且也测试过了,我们就可以看到,如果你请求的是固定的某一个商品,那么就一定会将流量打到固定的一个应用nginx上面去。

posted @ 2018-09-07 11:02  大虾张三丰  阅读(437)  评论(0编辑  收藏  举报