43. 缓存冷启动问题解决方案:基于storm实时热点统计的分布式并行缓存预热

前言

项目地址:eshop-study
切换到相应分支:
在这里插入图片描述

缓存冷启动

缓存冷启动即缓存空的情况下启动,两种情形出现:

  1. 新系统第一次上线,此时在缓存里可能是没有数据的
  2. 系统在线上稳定运行着,但是突然间重要的redis缓存全盘崩溃了,而且不幸的是,数据全都无法找回来

系统第一次上线启动,系统在redis故障的情况下重新启动,在高并发的场景下出现的问题:
在这里插入图片描述

解决:redis重启过程中保证mysql不挂掉

缓存预热

缓存冷启动:redis启动后,一点数据都没有,直接就对外提供服务了,mysql裸奔状态

  1. 提前给redis中灌入部分数据,再提供服务
  2. 不可能将所有数据都写入redis,因为数据量太大,第一耗费的时间太长,第二根本redis容纳不下所有的数据
  3. 需要根据当天的具体访问情况,实时统计出访问频率较高的热数据
  4. 然后将访问频率较高的热数据写入redis中,肯定是热数据也比较多,多个服务并行读取数据去写,并行的分布式的缓存预热
  5. 然后将灌入了热数据的redis对外提供服务,这样就不至于冷启动,直接让数据库裸奔

开发方案

访问流量上报

nginx+lua访问流量上报到 kafka

要统计出来当前最新的实时的热数据是哪些,将商品详情页访问的请求对应的流量,日志,实时上报到kafka中

实时统计流量访问次数

storm从kafka中消费数据,实时统计出每个商品的访问次数,访问次数基于LRU内存数据结构的存储方案

  1. 优先用storm内存中的一个LRUMap去存放,性能高,而且没有外部依赖
  2. 如果使用redis,还要防止redis挂掉数据丢失,依赖耦合度高;用mysql,扛不住高并发读写;用hbase,hadoop生态系统,维护麻烦,太重
  3. 其实我们只要统计出最近一段时间访问最频繁的商品流量,然后对它们进行访问计数,同时维护出一个前N个访问最多的商品list即可
  4. 热数据,最近一段时间,比如最近1个小时,最近5分钟,1万个商品请求,统计出最近这段时间内每个商品的访问次数,排序,做出一个排名前N的list
  5. 计算好每个storm task要存放的商品访问次数的数量,计算出大小
  6. 然后构建一个LRUMapapache commons collections有开源的实现,设定好map的最大大小,就会自动根据LRU算法去剔除多余的数据,保证内存使用限制
  7. 即使有部分数据被干掉,因为如果它被LRU算法干掉,那么它就不是热数据,说明最近一段时间都很少访问了,下一轮重新统计

数据恢复

  1. 每个storm task启动的时候,基于zk分布式锁,将自己的task id写入zk同一个节点中
  2. 每个storm task负责完成自己的热数据的统计,每隔一段时间,就遍历一下这个LRUmap,然后维护一个前3个商品的list,更新这个list

实际生产中可能1000个,10000个商品的list

  1. 写一个后台线程,每隔一段时间,比如1分钟,都将排名前3的热数据list,同步到zk中去,存储到这个storm task的id对应的一个znode中去

  2. 这个服务代码可以跟缓存数据生产服务放一起,但是也可以放单独的服务

  3. 服务可能部署了很多个实例,每次服务启动的时候,就会去拿到一个storm task的列表,然后根据taskid,一个一个的去尝试获取taskid对应的znodezk分布式锁

  4. 当获取到分布式锁,将该storm task对应的热数据的list取出来,然后将数据从mysql中查询出来,写入缓存中,进行缓存的预热;

  5. 多个服务实例,分布式的并行的去做,基于zk分布式锁协调,分布式并行缓存的预热

实战项目

nginx+lua实现实时上报kafka

基于nginx+lua完成商品详情页访问流量实时上报kafka的开发。

  1. storm消费kafka中实时的访问日志,然后去进行缓存热数据的统计
  2. 技术方案非常简单,从lua脚本直接创建一个kafka producer,发送数据到kafka
  3. 下载lua+kafak脚本库
# eshop-cache01: 192.168.0.106
# eshop-cache02: 192.168.0.107
cd /usr/local
# 如果下载最新版本,nginx也要升级最新版本,否则lua脚本会执行错误
wget https://github.com/doujiang24/lua-resty-kafka/archive/v0.05.zip
yum install -y unzip
unzip lua-resty-kafka-0.05.zip
cp -rf /usr/local/lua-resty-kafka-master/lib/resty /usr/hello/lualib
  1. eshop-cache01: 192.168.0.106eshop-cache02: 192.168.0.107 nginx添加下面配置:
vim /usr/servers/nginx/conf/nginx.conf
resolver 8.8.8.8;

在这里插入图片描述

  1. 修改kafka配置,重启三个kafka进程
vi /usr/local/kafka/config/server.properties
advertised.host.name = 192.168.0.106
# 重启三台服务器中kafka进程
nohup bin/kafka-server-start.sh config/server.properties &

在这里插入图片描述

  1. 启动原来写的eshop-cache缓存服务,因为nginx重启后,本地缓存可能没了;项目地址:https://blog.csdn.net/qq_34246646/article/details/104596143
  2. 发送商品请求消息到后台服务之前,上报到kafka:vi /usr/hello/lua/product.lua

echop-cache01: 192.168.0.106,echop-cache02: 192.168.0.107

-- 上报数据到kafka
local cjson = require("cjson")  
local producer = require("resty.kafka.producer")  

local broker_list = {
     
    {
    host = "192.168.0.106", port = 9092 },  
    {
    host = "192.168.0.107", port = 9092 },  
    {
    host = "192.168.0.108", port = 9092 }
}
local log_json = {
   }
log_json["request_module"] = "product_detail_info"
log_json["headers"] = ngx.req.get_headers()  
log_json["uri_args"] = ngx.req.get_uri_args()  
log_json["body"] = ngx.req.read_body()  
log_json["http_version"] = ngx.req.http_version()  
log_json["method"] =ngx.req.get_method() 
log_json["raw_reader"] = ngx.req.raw_header()  
log_json["body_data"] = ngx.req.get_body_data()  

local message = cjson.encode(log_json);  

-- 获取请求参数
local uri_args = ngx.req.get_uri_args()
local productId 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、基于nginx+lua完成商品详情页访问流量实时上报kafka的开发 ==================================== 在nginx这一层,接收到访问请求的时候,就把请求的流量上报发送给kafka 这样的话,storm才能去消费kafka中的实时的访问日志,然后去进行缓存热数据的统计 用得技术方案非常简单,从lua脚本直接创建一个kafka producer,发送数据到kafka ``` wget https://github.com/doujiang24/lua-resty-kafka/archive/master.zip yum install -y unzip unzip lua-resty-kafka-master.zip cp -rf /usr/local/lua-resty-kafka-master/lib/resty /usr/hello/lualib nginx -s reload local cjson = require("cjson") local producer = require("resty.kafka.producer") local broker_list = { { host = "192.168.31.187", port = 9092 }, { host = "192.168.31.19", port = 9092 }, { host = "192.168.31.227", port = 9092 } } local log_json = {} log_json["headers"] = ngx.req.get_headers() log_json["uri_args"] = ngx.req.get_uri_args() log_json["body"] = ngx.req.read_body() log_json["http_version"] = ngx.req.http_version() log_json["method"] =ngx.req.get_method() log_json["raw_reader"] = ngx.req.raw_header() log_json["body_data"] = ngx.req.get_body_data() local message = cjson.encode(log_json); local productId = ngx.req.get_uri_args()["productId"] local async_producer = producer:new(broker_list, { producer_type = "async" }) local ok, err = async_producer:send("access-log", productId, message) if not ok then ngx.log(ngx.ERR, "kafka send err:", err) return end ``` 两台机器上都这样做,才能统一上报流量到kafka ``` bin/kafka-topics.sh --zookeeper 192.168.31.187:2181,192.168.31.19:2181,192.168.31.227:2181 --topic access-log --replication-factor 1 --partitions 1 --create bin/kafka-console-consumer.sh --zookeeper 192.168.31.187:2181,192.168.31.19:2181,192.168.31.227:2181 --topic access-log --from-beginning ``` (1)kafka在187上的节点死掉了,可能是虚拟机的问题,杀掉进程,重新启动一下 nohup bin/kafka-server-start.sh config/server.properties & (2)需要在nginx.conf中,http部分,加入resolver 8.8.8.8; (3)需要在kafka中加入advertised.host.name = 192.168.31.187,重启三个kafka进程 (4)需要启动eshop-cache缓存服务,因为nginx中的本地缓存可能不在了 二、基于storm+kafka完成商品访问次数实时统计拓扑的开发 ==============

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值