本文共 8354 字,大约阅读时间需要 27 分钟。
在 Nginx、Redis、Ehcache 三级缓存中,Nginx 分发层 和 应用层。在分发层 Nginx,通过 Lua,将商品id、商品店铺id,都转发到后端的应用nginx
可以通过 Nginx 指令重新加载所有配置包括 Lua 脚本
# /opt/modules/openresty/nginx/sbin/nginx/sbin/nginx -s reload 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包(一个网络请求的库)
# cd /opt/modules/openresty/lualib/resty # wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua 正在解析主机 ()... 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 正在解析主机 ()... 151.101.72.133 正在连接 ()|151.101.72.133|:443... 已连接。 已发出 HTTP 请求,正在等待回应... 200 OK 长度:29686 (29K) [text/plain] 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库
# 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] 100%[=================================================================================================>] 15,473 95.8KB/s 用时 0.2s 2018-04-18 03:49:14 (95.8 KB/s) - 已保存 “template.lua” [15473/15473]) # 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] 100%[=================================================================================================>] 1,235 --.-K/s 用时 0s 在 lua.conf 也就是 Nginx 配置中引入的配置文件,在该文件的 server 标签中配置模板位置
# cd /opt/modules/openresty # mkdir -p hello/templates # cd /opt/modules/openresty/nginx/conf # 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"; default_type 'text/html'; content_by_lua_file conf/lua/test.lua; default_type 'text/html'; content_by_lua_file conf/lua/hello.lua; # cd /opt/modules/openresty/templates <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 商品id: {* productId *}<br/> 商品名称: {* productName *}<br/> 商品图片列表: {* productPictureList *}<br/> 商品规格: {* productSpecification *}<br/> 商品售后服务: {* productService *}<br/> 商品颜色: {* productColor *}<br/> 商品大小: {* productSize *}<br/> 店铺名称: {* shopName *}<br/> 店铺评级: {* shopLevel *}<br/> 店铺好评率: {* shopGoodCommentRate *}<br/> 8> 将渲染后的网页模板作为 http 响应,返回给 分发层Nginx
# cd /opt/modules/openresty/nginx/conf #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; default_type application/octet-stream; lua_shared_dict my_cache 128m; lua_package_path "/opt/modules/openresty/lualib/?.lua;;"; lua_package_cpath "/opt/modules/openresty/lualib/?.so;;"; # cd /opt/modules/openresty/nginx/conf set $template_location "/opt/modules/openresty/templates"; set $template_root "/opt/modules/openresty/hello/templates"; lua_shared_dict my_cache 128m; default_type 'text/html'; content_by_lua_file conf/lua/test.lua; default_type 'text/html'; content_by_lua_file conf/lua/hello.lua; default_type 'text/html'; content_by_lua_file conf/lua/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 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 resp, err = httpc:request_uri("http://192.168.86.226:8080", { path = "/getProductInfo?productId=" .. productId -- 缓存产品信息,并设置缓存时间 10 * 60 秒,也就是 10分钟 cache_ngx:set(productCacheKey, productCache, 10 * 60) -- 检查 shop 缓存信息,如果不存在,则向服务端获取 if shopCache == "" or shopCache == nil then local http = require("resty.http") local resp, err = httpc:request_uri("http://192.168.86.226:8080", { path = "/getShopInfo?shopId=" .. shopId -- 缓存店铺信息,并设置缓存时间 10 * 60 秒,也就是 10分钟 cache_ngx:set(shopCacheKey, shopCache, 10 * 60) local cjson = require("cjson") local productCacheJSON = cjson.decode(productCache) local shopCacheJSON = cjson.decode(shopCache) 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 第一次访问的时候,其实在 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/