文章目录
缓存预热
缓存冷启动,redis启动后,一点数据都没有,直接就对外提供服务了,mysql就裸奔
(1)提前给redis中灌入部分数据,再提供服务
(2)肯定不可能将所有数据都写入redis,因为数据量太大了,第一耗费的时间太长了,第二根本redis容纳不下所有的数据
(3)需要根据当天的具体访问情况,实时统计出访问频率较高的热数据
(4)然后将访问频率较高的热数据写入redis中,肯定是热数据也比较多,我们也得多个服务并行读取数据去写,并行的分布式的缓存预热
(5)然后将灌入了热数据的redis对外提供服务,这样就不至于冷启动,直接让数据库裸奔了
1、nginx+lua将访问流量上报到kafka中
要统计出来当前最新的实时的热数据是哪些,我们就得将商品详情页访问的请求对应的流浪,日志,实时上报到kafka中
2、storm从kafka中消费数据,实时统计出每个商品的访问次数,访问次数基于LRU内存数据结构的存储方案
优先用内存中的一个LRUMap去存放,性能高,而且没有外部依赖
我之前做过的一些项目,不光是这个项目,还有很多其他的,一些广告计费类的系统,storm
否则的话,依赖redis,我们就是要防止redis挂掉数据丢失的情况,就不合适了; 用mysql,扛不住高并发读写; 用hbase,hadoop生态系统,维护麻烦,太重了
其实我们只要统计出最近一段时间访问最频繁的商品,然后对它们进行访问计数,同时维护出一个前N个访问最多的商品list即可
热数据,最近一段时间,可以拿到最近一段,比如最近1个小时,最近5分钟,1万个商品请求,统计出最近这段时间内每个商品的访问次数,排序,做出一个排名前N的list
计算好每个task大致要存放的商品访问次数的数量,计算出大小
然后构建一个LRUMap,apache commons collections有开源的实现,设定好map的最大大小,就会自动根据LRU算法去剔除多余的数据,保证内存使用限制
即使有部分数据被干掉了,然后下次来重新开始计数,也没关系,因为如果它被LRU算法干掉,那么它就不是热数据,说明最近一段时间都很少访问了
3、每个storm task启动的时候,基于zk分布式锁,将自己的id写入zk同一个节点中
4、每个storm task负责完成自己这里的热数据的统计,每隔一段时间,就遍历一下这个map,然后维护一个前3个商品的list,更新这个list
5、写一个后台线程,每隔一段时间,比如1分钟,都将排名前3的热数据list,同步到zk中去,存储到这个storm task对应的一个znode中去
6、我们需要一个服务,比如说,代码可以跟缓存数据生产服务放一起,但是也可以放单独的服务
服务可能部署了很多个实例
每次服务启动的时候,就会去拿到一个storm task的列表,然后根据taskid,一个一个的去尝试获取taskid对应的znode的zk分布式锁
如果能获取到分布式锁的话,那么就将那个storm task对应的热数据的list取出来
然后将数据从mysql中查询出来,写入缓存中,进行缓存的预热,多个服务实例,分布式的并行的去做,基于zk分布式锁做了协调了,分布式并行缓存的预热
storm+hbase(海量数据的分布式非关系型数据库),用storm计算完数据,灌入到hbase中
如果用storm+mysql,数据量大了的话,很容易把mysql整崩
在nginx这一层,接收到访问请求的时候,就把请求的流量上报发送给kafka
这样的话,storm才能去消费kafka中的实时的访问日志,然后去进行缓存热数据的统计
用得技术方案非常简单,从lua脚本直接创建一个kafka producer,发送数据到kafka
之前搭建的nginx架构是:node3上的nginx作为转发nginx,node1和node2上作为应用nginx;
首先在node1上
cd /usr/local
wget https://github.com/doujiang24/lua-resty-kafka/archive/master.zip
安装unzip命令
yum install -y unzip
unzip master.zip ---解压得到 lua-resty-kafka-master目录
cp -rf /usr/local/lua-resty-kafka-master/lib/resty /usr/hello/lualib/
如果以上操作以前,nginx就已经启动了,那么就reload一下,如果没有启动那么就启动一下
/usr/servers/nginx/sbin/nginx -s reload
然后在node2上做同样的事情
然后在node1上修改product.lua脚本
[root@node1 lua]# cd /usr/hello/lua
[root@node1 lua]# ll
总用量 8
-rw-r--r--. 1 root root 38 7月 19 06:50 hello.lua
-rw-r--r--. 1 root root 1944 7月 19 19:39 product.lua
[root@node1 lua]# vi product.lua
--begin add
ngx.say("this is node1 或者 node2")
local cjson = require("cjson")
local producer = require("resty.kafka.producer")
local broker_list = {
{ host = "10.1.218.22", port = 9092 },
{ host = "10.1.218.26", port = 9092 },
{ host = "10.1.218.24", port = 9092 }
}
local log_json = {}
log_json["headers"] = ngx.req.get_headers()
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)
ngx.say("kafka",message)
--end add
local uri_args = ngx.req.get_uri_args()
local productId = uri_args["productId"]
local shopId = uri_args["shopId"]
--begin add
local async_producer = producer:new(broker_list, { producer_type = "async"})
local ok, err = async_producer:send("access-log", productId, message)
ngx.say(ok)
if not ok then
ngx.log(ngx.ERR, "kafka send err:", err)
return
end
--end add
-- get nginx share cache
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)
-- if not found value from nginx cache
if productCache == "" or productCache == nil then
local http = require("resty.http")
local httpc = http:new()
local resp,err = httpc:request_uri("http://10.1.218.189:8080",{
method = "GET",
path = "/getProductInfo?productId="..productId,
keepalive = false
})
productCache = resp.body
cache_ngx:set(productCacheKey, productCache, 10 * 60)
end
if shopCache == "" or shopCache == nil then
local http = require("resty.http")
local httpc = http.new()
local resp,err = httpc:request_uri("http://10.1.218.189:8080",{
method = "GET",
path = "/getShopInfo?shopId="..shopId,
keepalive = false
})
shopCache = resp.body
cache_ngx:set(shopCacheKey, shopCache, 10 * 60)
end
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)
然后检验语法,重新reload nginx
[root@node1 lua]# /usr/servers/nginx/sbin/nginx -t
nginx: the configuration file /usr/servers/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/servers/nginx/conf/nginx.conf test is successful
[root@node1 lua]# /usr/servers/nginx/sbin/nginx -s reload
其中--add --add 中间的内容是本次新增加的,同样修改node2中的product.lua脚本
确保已经启动了node1,node2,node3上的zk,三个节点上的kafka都启动中
在node1上,创建topic
cd /usr/local/kafka/
bin/kafka-topics.sh --zookeeper 10.1.218.22:2181,10.1.218.26:2181,10.1.218.24:2181 --topic access-log --replication-factor 1 --partitions 1 --create
启动消费者
bin/kafka-console-consumer.sh --zookeeper 10.1.218.22:2181,10.1.218.26:2181,10.1.218.24:2181 --topic access-log --from-beginning
请求
http://10.1.218.24/hello?method=product&productId=1&shopId=1
返回了
this is node2
kafka{"request_module":"product_detail_info","raw_reader":"GET \/product?productId=1&shopId=1 HTTP\/1.1\r\nHost: 10.1.218.26\r\nUser-Agent: lua-resty-http\/0.17.0-beta.1 (Lua) ngx_lua\/9014\r\n\r\n","http_version":1.1,"method":"GET","uri_args":{"productId":"1","shopId":"1"},"headers":{"host":"10.1.218.26","user-agent":"lua-resty-http\/0.17.0-beta.1 (Lua) ngx_lua\/9014"}}
true
商品id: 1
商品名称: iphone7
商品图片列表: a.jpg,b.jpg
商品规格: iphone7的规格
商品售后服务: 售后服务
商品颜色: 红色,白色,黑色
商品大小: S
店铺id: 1
店铺名称: 小王手机店
店铺评级: 5
店铺好评率: 99
请求
http://10.1.218.24/hello?method=product&productId=22&shopId=1
this is node1 kafka{"request_module":"product_detail_info","raw_reader":"GET \/product?productId=22&shopId=1 HTTP\/1.1\r\nHost: 10.1.218.22\r\nUser-Agent: lua-resty-http\/0.17.0-beta.1 (Lua) ngx_lua\/9014\r\n\r\n","http_version":1.1,"method":"GET","uri_args":{"productId":"22","shopId":"1"},"headers":{"host":"10.1.218.22","user-agent":"lua-resty-http\/0.17.0-beta.1 (Lua) ngx_lua\/9014"}} true 商品id: 22
商品名称: iphone7
商品图片列表: a.jpg,b.jpg
商品规格: iphone7的规格
商品售后服务: 售后服务
商品颜色: 红色,白色,黑色
商品大小: S
店铺id: 1
店铺名称: 小王手机店
店铺评级: 5
店铺好评率: 99
我们发现提示lua发送kafka已经成功了
,但是kafka消费者却收不到消息
修改
需要在node1和node2中,修改nginx.conf文件,http部分,加入resolver 8.8.8.8;
vi /usr/servers/nginx/conf/nginx.conf
http {
resolver 8.8.8.8;
--忽略
(3)需要在三个节点上,kafka配置文件sever.properties中加入advertised.host.name = 当前机器ip,重启三个kafka进程
vi /usr/local/kafka/config/server.properties
# Hostname the broker will advertise to producers and consumers. If not set, it uses the
# value for "host.name" if configured. Otherwise, it will use the value returned from
# java.net.InetAddress.getCanonicalHostName().
#advertised.host.name=<hostname routable by clients>
advertised.host.name=10.1.218.22
我又尝试在三个节点中,在server.properteis中新增
advertised.listeners=PLAINTEXT://10.1.218.22:9092 node1
advertised.listeners=PLAINTEXT://10.1.218.26:9092 node2
advertised.listeners=PLAINTEXT://10.1.218.24:9092 node3
但是我本地尝试了以后,lua发送消息,kafka依然接受不到消息,奔溃!!!!!!!!!