上一章我们解决的缓存冷启动问题: 热数据 -> 热数据的统计 -> redis中缓存的预热 -> 避免新系统刚上线,或者是redis崩溃数据丢失后重启,redis中没有数据,redis冷启动 -> 大量流量直接到数据库; redis启动前,必须确保其中是有部分热数据的缓存的
storm task
,上面算出了1万个商品的访问次数,商品id和访问次数存到``LRUMapLRUMap
,将其中的访问次数进行排序,统计出往后排的95%的商品访问次数的平均值LRUMap
中第一个商品id的访问次数超过这个值,我们就认为是瞬间出现的热点数据storm
通过算法计算出热点商品id列表后,会直接发送http请求到nginx
上,nginx上用lua脚本
去处理这个请求productId
,发送到流量分发nginx
上面去,放在本地缓存中;商品详情
缓存数据,发送到所有的应用nginx服务器
上去,直接放在本地缓存中流量分发nginx
,添加逻辑:每次访问一个商品详情页的时候,如果发现它是个热点,那么立即做流量分发策略的降级hash策略
:同一个productId的访问都同一台应用nginx服务器上,降级成对这个热点商品,流量分发采取负载均衡
发送到所有的后端应用nginx
服务器上去storm还需要保存下来上次识别出来的热点list
下次去识别的时候,这次的热点list跟上次的热点list做一下diff,看看可能有的商品已经不是热点了
热点的取消的逻辑,发送http请求到流量分发的nginx上去,取消掉对应的热点数据,从nginx本地缓存中,删除
服务机器: [eshop-cache03: 192.168.0.108]
location /hot {
default_type 'text/html';
# lua_code_cache off;
content_by_lua_file /usr/hello/lua/hot.lua;
}
local uri_args = ngx.req.get_uri_args()
local product_id = uri_args["productId"]
local cache_ngx = ngx.shared.my_cache
local hot_product_cache_key = "hot_product_"..product_id
-- 缓存1小时
cache_ngx:set(hot_product_cache_key, "true", 60 * 60)
/usr/servers/nginx/sbin/nginx -s reload
服务机器: [eshop-cache01: 192.168.0.106]
、[eshop-cache02: 192.168.0.107]
[eshop-cache03: 192.168.0.108]
的第一步;[eshop-cache03: 192.168.0.108]
的第二步,内容更换为下面的配置;local uri_args = ngx.req.get_uri_args()
local product_id = uri_args["productId"]
local product_info = uri_args["productInfo"]
local product_cache_key = "product_info_"..product_id
local cache_ngx = ngx.shared.my_cache
-- 缓存1小时
cache_ngx:set(product_cache_key,product_info,60 * 60)
/usr/servers/nginx/sbin/nginx -s reload
在nginx+lua中实现热点缓存自动降级为负载均衡流量分发策略的逻辑
服务机器:[eshop-cache03: 192.168.0.108]
vi /usr/hello/lua/distribute.lua
之前是对商品id取模,然后将请求分发到两台应用nginx机器上
local uri_args = ngx.req.get_uri_args()
local productId = uri_args["productId"]
local shopId = uri_args["shopId"]
local hosts = {"192.168.0.106", "192.168.0.107"}
local backend = ""
-- 和上面分发层nginx设置的key值相同
local hot_product_key = "hot_product_"..productId
local cache_ngx = ngx.shared.my_cache
local hot_product_flag = cache_ngx:get(hot_product_key)
-- 判断是否走原来hash分发还是降级为负载均衡流量分发策略
if hot_product_flag == "true" then
math.randomseed(tostring(os.time()):reverse():sub(1, 7))
-- 现在有两台应用nginx机器,随机获取1或2
local index = math.random(1, 2)
backend = "http://"..hosts[index]
else
local hash = ngx.crc32_long(productId)
local index = (hash % 2) + 1
backend = "http://"..hosts[index]
end
local requestPath = uri_args["requestPath"]
requestPath = "/"..requestPath.."?productId="..productId.."&shopId="..shopId
local http = require("resty.http")
local httpc = http.new()
local resp, err = httpc:request_uri(backend,{
method = "GET",
path = requestPath,
keepalive=false
})
if not resp then
ngx.say("request error: ", err)
return
end
ngx.say(resp.body)
httpc:close()
lua_shared_dict my_cache 128m;
/usr/servers/nginx/sbin/nginx -s reload
在storm拓扑中加入热点缓存消失的实时自动识别和感知的代码逻辑
ProductCountBolt.java
// 6. 实时感知热点数据的消失
if (lastTimeHotProductIdList.size() > 0) {
for (Long productId : lastTimeHotProductIdList) {
if (!hotProductIdList.contains(productId)) {
// 说明上次计算的热点商品id已经不是热点
// 发送该商品id的http请求到流量分发的nginx中,取消热点缓存标识
String cancelHotProductIdUrl = "http://192.168.0.108/cancel_hot?productId=" + productId;
HttpClientUtils.sendGetRequest(cancelHotProductIdUrl);
}
}
}
if (hotProductIdList.size() > 0) {
lastTimeHotProductIdList.clear();
lastTimeHotProductIdList.addAll(hotProductIdList);
}
[eshop-cache03: 192.168.0.108]
修改: vi /usr/hello/hello.conf
location /cancel_hot {
default_type 'text/html';
# lua_code_cache off;
content_by_lua_file /usr/hello/lua/cancel_hot.lua;
}
[eshop-cache03: 192.168.0.108]
添加lua脚本: vi /usr/hello/lua/cancel_hot.lua
local uri_args = ngx.req.get_uri_args()
local product_id = uri_args["productId"]
local cache_ngx = ngx.shared.my_cache
local hot_product_cache_key = "hot_product_"..product_id
- 缓存1分钟
cache_ngx:set(hot_product_cache_key, "false", 60 * 60)
/usr/servers/nginx/sbin/nginx -s reload
将热点缓存自动降级解决方案的代码运行后观察效果以及调试和修复bug
95%
平均值计算排序好后后面5个的平局值,这里访问6个商品不同id的请求;http://192.168.0.108/product?requestPath=product&productId=1&shopId=1
http://192.168.0.108/product?requestPath=product&productId=2&shopId=1
http://192.168.0.108/product?requestPath=product&productId=3&shopId=1
http://192.168.0.108/product?requestPath=product&productId=4&shopId=1
http://192.168.0.108/product?requestPath=product&productId=5&shopId=1
http://192.168.0.108/product?requestPath=product&productId=6&shopId=1