博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Nginx+Lua 从Nginx和Redis缓存获取数据案例 详解
阅读量:2394 次
发布时间:2019-05-10

本文共 8354 字,大约阅读时间需要 27 分钟。

在 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,以及商品店铺id
3> 根据 商品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访问地址 : 
# cd 
/opt/modules/openresty/lualib/resty
wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua
--2018-05-10 21:23:25--  
正在解析主机 ()... 151.101.72.133
正在连接 ()|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--  
正在解析主机 ()... 151.101.72.133
正在连接 ()|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 访问地址 : 
# 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/templates
vi 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.conf
server {
    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.lua
local 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

-- 生成产品和商店的 key
local productCacheKey = "product_info_" .. productId
local 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中,这里存在 分布式的缓存重建的并发问题,这个问题可以通过分布式锁解决 

转载地址:http://fogab.baihongyu.com/

你可能感兴趣的文章
C++是什么?怎么学?学完了能得到什么?
查看>>
初学C语言没有项目练手怎么行,这17个小项目收下不谢
查看>>
学好C语言,你只需要这几句口诀!
查看>>
选择大于努力!0基础学好C语言编程,首先要掌握的是什么?
查看>>
C语言和其他语言的不得不说的差别!
查看>>
夫妻俩在互联网公司工作,年收入曝光,网友:这么高!
查看>>
程序员5年工作经验,因频繁跳槽被面试官压工资!
查看>>
千万程序员关心的问题,学历重要吗?
查看>>
国内程序员加班严重!听听外国网友怎么说。
查看>>
程序员都长得丑?颜值底线是程序员?
查看>>
世界是你们的,也是我们的,但终究是他们的!致程序员
查看>>
来看看程序员们是怎么自黑的
查看>>
论程序员的核心竞争力
查看>>
职场中神奇的程序员,却常常被人说“太直”,这是什么样的思维?
查看>>
@初学编程的朋友们,如果你能学得这些方法,学习将会更快一步!
查看>>
C/C++编程笔记:C/C++ 的编译和链接
查看>>
C/C++编程知识分享:C++四种强制转换,教你多种类型转换方式!
查看>>
那些程序员身上共有的属性,这就是他为什么比你进步快的原因!
查看>>
零基础学习什么编程语言比较合适?别的不说,听说大佬都学了这个!
查看>>
单片机为什么一直用C语言,不用其他编程语言?只有学过的知道!
查看>>