Nginx+Lua 从Nginx和Redis缓存获取数据案例 详解,nginxredis


在 Nginx、Redis、Ehcache 三级缓存中,Nginx 分发层 和 应用层。在分发层 Nginx,通过 Lua,将商品id、商品店铺id,都转发到后端的应用nginx
可以通过 Nginx 指令重新加载所有配置包括 Lua 脚本# /opt/modules/openresty/nginx/sbin/nginx/sbin/nginx -s reload
1> 应用 Nginx 的 Lua脚本接收到请求2> 获取请求参数中的商品id,以及商品店铺id3> 根据 商品id 和 商品店铺id,在 Nginx 本地缓存中尝试获取数据4> 如果在 Nginx本地缓存中没有获取到数据,那么就到 Redis分布式缓存中获取数据,如果获取到数据,还要设置到 Nginx本地缓存中
这里有个问题,建议不要用 Nginx+Lua 直接去获取 Redis数据,因为 OpenResty 没有太好的 redis cluster 的支持包,所以建议是发送http请求到缓存数据生产服务,由该服务提供一个 http接口。缓存数生产服务可以基于 redis cluster api 从 Redis 中直接获取数据,并返回给 Nginx
在 OpenResty 编译文件中,引入 lua http lib包(一个网络请求的库)GitHub访问地址 : https://github.com/pintsized/lua-resty-http# cd /opt/modules/openresty/lualib/restywget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua--2018-05-10 21:23:25--  https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua正在解析主机 raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.72.133正在连接 raw.githubusercontent.com (raw.githubusercontent.com)|151.101.72.133|:443... 已连接。已发出 HTTP 请求,正在等待回应... 200 OK长度:1150 (1.1K) [text/plain]正在保存至: “http_headers.lua”
100%[==============================================================================================================================================>] 1,150       --.-K/s 用时 0s
2018-05-10 21:23:27 (262 MB/s) - 已保存 “http_headers.lua” [1150/1150])
# wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua--2018-05-10 21:24:33--  https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua正在解析主机 raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.72.133正在连接 raw.githubusercontent.com (raw.githubusercontent.com)|151.101.72.133|:443... 已连接。已发出 HTTP 请求,正在等待回应... 200 OK长度:29686 (29K) [text/plain]正在保存至: “http.lua”
100%[==============================================================================================================================================>] 29,686      --.-K/s 用时 0.1s
2018-05-10 21:24:34 (239 KB/s) - 已保存 “http.lua” [29686/29686])5> 如果缓存数据生产服务没有在 Redis分布式缓存中没有获取到数据,那么就在本地 Ehcache 中获取数据,返回数据给 Nginx,也要设置到 Nginx本地缓存中6> 如果 Ehcache 本地缓存都没有数据,那么就需要去原始的服务中拉去数据,该服务会从 MySQL 中查询,拉去到数据之后,返回给 Nginx,并重新设置到 Ehcache和 Rdis 中注 : 这里存在一个问题,那就是分布式缓存重建并发冲突问题7> Nginx 最终利用获取到的数据,动态渲染网页模板
在 OpenResty 编译文件中,引入lua-resty-template库GitHub 访问地址 : https://github.com/bungle/lua-resty-template# cd /opt/modules/openresty/lualib/resty# wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template.lua--2018-04-18 03:49:12--  https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template.lua正在解析主机 raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...正在连接 raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... 已连接。已发出 HTTP 请求,正在等待回应... 200 OK长度:15473 (15K) [text/plain]正在保存至: “template.lua”
100%[=================================================================================================>] 15,473      95.8KB/s 用时 0.2s
2018-04-18 03:49:14 (95.8 KB/s) - 已保存 “template.lua” [15473/15473])
# mkdir html# cd html# wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template/html.lua--2018-04-18 03:50:32--  https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template/html.lua正在解析主机 raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...正在连接 raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... 已连接。已发出 HTTP 请求,正在等待回应... 200 OK长度:1235 (1.2K) [text/plain]正在保存至: “html.lua”
100%[=================================================================================================>] 1,235       --.-K/s 用时 0s在 lua.conf 也就是 Nginx 配置中引入的配置文件,在该文件的 server 标签中配置模板位置# cd /opt/modules/openresty# mkdir templates/# mkdir -p hello/templates
# cd /opt/modules/openresty/nginx/conf# vi lua.conf
server {    listen       80;    server_name  _;    # template_root (set $template_root /var/www/site/templates)    # template_location (set $template_location /templates)    # 如果在 Nginx 配置中没有这些设置,则使用 ngx.var.document_root的值。如果设置 template_location,则正常返回(状态码200),则使用优先使用该配置其渲染。    # 如果找不到,将尝试使用 template_root 或 document_root    # 如果使用 $template_location 此时服务器使用 ngx_static 模式    # $template_root 无法识别该目录下子目录中存放的模板
    #set $template_location "/opt/modules/openresty/templates";    set $template_root "/opt/modules/openresty/templates";    location /lua {        default_type 'text/html';        content_by_lua_file conf/lua/test.lua;    }    location /hello {        default_type 'text/html';        content_by_lua_file conf/lua/hello.lua;    }}创建产品 HTML 模板# cd /opt/modules/openresty/templatesvi product.html
<!DOCTYPE html><html lang="en"><head>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">    <title>商品详情页</title></head><body>
商品id: {* productId *}<br/>商品名称: {* productName *}<br/>商品图片列表: {* productPictureList *}<br/>商品规格: {* productSpecification *}<br/>商品售后服务: {* productService *}<br/>商品颜色: {* productColor *}<br/>商品大小: {* productSize *}<br/>店铺id: {* shopId *}<br/>店铺名称: {* shopName *}<br/>店铺评级: {* shopLevel *}<br/>店铺好评率: {* shopGoodCommentRate *}<br/>
</body></html>
8> 将渲染后的网页模板作为 http 响应,返回给 分发层Nginx修改 Lua 缓存大小# cd /opt/modules/openresty/nginx/conf# vi nginx.conf
#user  nobody;worker_processes  1;
#error_log  logs/error.log;#error_log  logs/error.log  notice;#error_log  logs/error.log  info;
#pid        logs/nginx.pid;
events {    worker_connections  1024;}

http {    include       mime.types;    default_type  application/octet-stream;    sendfile        on;    keepalive_timeout  65;    # 设置缓存大小    lua_shared_dict my_cache 128m;
    lua_package_path "/opt/modules/openresty/lualib/?.lua;;";    lua_package_cpath "/opt/modules/openresty/lualib/?.so;;";    include lua.conf;}
添加产品访问监控,并设置产品 Lua 脚本# cd /opt/modules/openresty/nginx/conf# vi lua.confserver {    listen       80;    server_name  _;    # 设置模板存放位置    set $template_location "/opt/modules/openresty/templates";    set $template_root "/opt/modules/openresty/hello/templates";    # 设置缓存大小    lua_shared_dict my_cache 128m;    location /lua {        default_type 'text/html';        content_by_lua_file conf/lua/test.lua;    }    location /hello {        default_type 'text/html';        content_by_lua_file conf/lua/hello.lua;    }    # 产品访问位置    location /product {        default_type 'text/html';        content_by_lua_file conf/lua/product.lua;    }}

# cd lua# vi product.lualocal uri_args = ngx.req.get_uri_args()local productId = uri_args["productId"]local shopId = uri_args["shopId"]
-- 使用在 nginx.conf 中通过 lua_shared_dict my_cache 128m; 定义的缓存中获取数据local cache_ngx = ngx.shared.my_cache
-- 生成产品和商店的 keylocal productCacheKey = "product_info_" .. productIdlocal shopCacheKey = "shop_info_" .. shopId
-- 获取缓存信息local productCache = cache_ngx:get(productCacheKey)local shopCache = cache_ngx:get(shopCacheKey)
-- 请求访问地址
-- 检查 product 缓存信息,如果不存在,则向服务端获取if productCache == "" or productCache == nil then    local http = require("resty.http")    local httpc = http.new()    local resp, err = httpc:request_uri("http://192.168.86.226:8080", {        method = "GET",        path = "/getProductInfo?productId=" .. productId    })    productCache = resp.body    -- 缓存产品信息,并设置缓存时间 10 * 60 秒,也就是 10分钟    cache_ngx:set(productCacheKey, productCache, 10 * 60)end
-- 检查 shop 缓存信息,如果不存在,则向服务端获取if shopCache == "" or shopCache == nil then    local http = require("resty.http")    local httpc = http.new()    local resp, err = httpc:request_uri("http://192.168.86.226:8080", {        method = "GET",        path = "/getShopInfo?shopId=" .. shopId    })    shopCache = resp.body    -- 缓存店铺信息,并设置缓存时间 10 * 60 秒,也就是 10分钟    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 重新加载一次模板# cd /opt/modules/openresty/nginx# ./sbin/nginx -s reload
第一次访问的时候,其实在 Nginx本地缓存中是取不到的,所以会发送 http请求到后端的缓存服务里去获取,会从 Redis 中获取。拿到数据以后,会放到 Nginx 本地缓存里面去,过期时间是 10分钟,然后将所有数据渲染到模板中,返回模板,以后再来访问的时候,就会直接从 Nginx本地缓存区获取数据 :缓存数据生产 -> 有数据变更 -> 主动更新两级缓存(ehcache+redis) -> 缓存维度化拆分分发层Nginx + 应用层Nginx -> 自定义流量分发策略提高缓存命中率nginx shared dict缓存 -> 缓存服务 -> redis -> ehcache -> 渲染html模板 -> 返回页面
还差最后一个很关键的要点,就是如果数据在 nginx -> redis -> ehcache 三级缓存都不在,可能就是数据通过 LRU 算法给清理掉了,这个时候缓存服务会重新拉去数据,去更新到 ehcache 和 redis中,这里存在 分布式的缓存重建的并发问题,这个问题可以通过分布式锁解决 

相关内容

    暂无相关文章