基于“分发层+应用层”双层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上面去。