异步多级缓存静态化(大型电商处理)

前提:

1.nginx+lua+openresty环境搭建  

https://blog.csdn.net/Mr_xiayijie/article/details/111224818

2.Nginx静态化html本地缓存处理 

https://blog.csdn.net/Mr_xiayijie/article/details/111225089

 

1.思路流程图:

 

叙述:

1.用户通过前端发送请求Nginx,获取Nginx本地缓存渲染Html模板

2.如果Nginx本地缓存没有找到,就去Redis中获取,并且放入Nginx缓存

3.Redis中没有找到就去Ehcache中获取,并且放入redis和Nginx缓存

4.Ehcache没有找到就去请求Mysql,并且放入Ehcache、redis和Nginx缓存

5.如果数据发生变更就给kafka或者其他MQ发送消息,监听到了将Ehcache、redis和Nginx缓存更新

核心代码如下:

lua脚本:

-- ===========Nginx本地缓存处理=====================
local uri_args = ngx.req.get_uri_args()
local goodsId = uri_args [goodsId]
local cache_ngx = ngx.shared.my_cache
local goodsCacheKey =  Goods..goodsId 
local goodsCache = cache_ngxget(goodsCacheKey)


-- =======发送http请求===========
if  goodsCache ==  or goodsCache  == nil or goodsCache==null then    --这里是redis没有数据存如Nginx就去发送http获取数据 
ngx.say(开始发送HTTP请求获取数据=========,br)
local http = require(resty.http)
local httpc =http.new()
local resp,err=httpcrequest_uri(http172.16.21.589999,{
    method=GET,
    path=goodsCachegetgoods..goodsId ,
    keepalive=false
})
goodsCache = resp.body   --得到请求响应结果
cache_ngxset(goodsCacheKey ,goodsCache,10)  --10秒失效
end    

-- ================JSON化处理(导入)================
local cjson = require(cjson)
local goodsJson=cjson.decode(goodsCache)  -- redis Json化处理

--=====最后Json数据重组=========
local context = {
goodsId=goodsJson.goodsId,
goodsName=goodsJson.goodsName,
price=goodsJson.price,
desc=goodsJson.desc,
}


--==template模板处理=========
local template =  require(resty.template)
template.render(goods.html,context)

Controller处理:

package com.zking.controller;

import com.alibaba.fastjson.JSON;
import com.zking.config.GoodsEhcache;
import com.zking.pojo.GoodsInfo;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: codinglife
 * @time: 2020/12/14 13:51
 */
@Controller
@RequestMapping("/goodsCache")
public class GoodsCacheController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private KafkaTemplate kafkaTemplate;


    /**
     * 发送kafka消息模拟修改数据
     *
     * @param
     */
    @GetMapping("/send/message")
    public String kafkaTest() {
        GoodsInfo goodsInfo = new GoodsInfo();
        goodsInfo.setGoodsId("1");
        goodsInfo.setGoodsName("kafka改变的华为P");
        goodsInfo.setDesc("这是kafka改变的一款华为的产品");
        goodsInfo.setPrice(Integer.valueOf(12));
        String str = JSON.toJSONString(goodsInfo);
        kafkaTemplate.send("goods_topic_N01", str);
        return "kafka执行成功";
    }


    /**
     * 模拟后台数据请求操作
     * 注:尽量保持ehcache和redis的key一致
     *
     * @param goodsId
     */
    @GetMapping("/get/goods/{goodsId}")
    @ResponseBody
    public GoodsInfo queryGoodsInfo(@PathVariable String goodsId) {
        GoodsInfo goodsInfo = null;
        Cache cache = GoodsEhcache.commonEchache("GoodsCache");          //设置了20秒过期
        Element element = cache.get("Goods" + goodsId);                             //从ehcache获取数据
        if (element != null && !"".equals(element)) {                               //ehcache有直接返回
            goodsInfo = (GoodsInfo) element.getObjectValue();
            logger.info("ehcache有直接返回");
        } else {                                                                  //如果ehcache没有  就去Redis数据库获取数据
            ValueOperations ops = redisTemplate.opsForValue();
            goodsInfo = (GoodsInfo) ops.get("Goods" + goodsId);
            logger.info("Redis数据库获取数据");
            if (goodsInfo != null) {                                               //redis中获取到的数据存存入ehcache
                Element elementPut = new Element("Goods" + goodsId, goodsInfo);
                cache.put(elementPut);                                           //放入ehcache
            }
            if (goodsInfo == null) {                                             //redis中没有数据请求Mysql数据库获取数据
                logger.info("Mysql数据库获取数据");
                //模拟数据库回去出来的商品详情数据
                goodsInfo = new GoodsInfo();
                goodsInfo.setGoodsId(goodsId + "");
                goodsInfo.setGoodsName("华为P" + goodsId);
                goodsInfo.setDesc("这是一款华为的产品" + goodsId);
                goodsInfo.setPrice(Integer.valueOf(goodsId));
                Element elementPut = new Element("Goods" + goodsId, goodsInfo);
                cache.put(elementPut);                                              //放入ehcache
                ops.set("Goods" + goodsId, goodsInfo, 600, TimeUnit.SECONDS); //再存入redis
            }
        }
        return goodsInfo;
    }


}

 

kafka监听处理:

package com.zking.monitor;

import com.alibaba.fastjson.JSONObject;
import com.zking.config.GoodsEhcache;
import com.zking.pojo.GoodsInfo;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: codinglife
 * @time: 2020/12/10 14:02
 */
@Component
public class GoodsMonitor {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private RedisTemplate redisTemplate;


    @KafkaListener(topics = {"goods_topic_N01"})
    public void processMessage(String content){
        logger.info("kafka接收消息:{}",content);
        GoodsInfo goodsInfo =JSONObject.parseObject(content,GoodsInfo.class);

        //kafka接收到消息  将redis中的数据和ehcache中的数据进行替换
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("Goods" + goodsInfo.getGoodsId(), goodsInfo, 600, TimeUnit.SECONDS); //再存入redis


        Cache cache = GoodsEhcache.commonEchache("GoodsCache");
        Element elementPut = new Element("Goods" + goodsInfo.getGoodsId(), goodsInfo);
        cache.put(elementPut);

    }


}

 

 

结果:

     

资料获取:  这里面包括源码,包和配置信息

链接: https://pan.baidu.com/s/1sJ6VsUgLXT63j9ok6-pmxQ

提取码: tsmu

 

 

问题:

1.lua如何整合Redis?

我这里有个整合脚本:

-- ================JSON化处理(导入)================
local cjson = require("cjson")
-- ===========Nginx本地缓存处理=====================
local uri_args = ngx.req.get_uri_args()
local goodsId = uri_args ["goodsId"]
local cache_ngx = ngx.shared.my_cache
local goodsCacheKey =  "Goods"..goodsId 
local goodsCache = cache_ngx:get(goodsCacheKey)


--======= 连接redis===============================
if goodsCache == "" or goodsCache  == nil or goodsCache==null then    --判断是否有Nginx缓存开始
ngx.say("Nginx缓存没有开始获取Redis缓存数据========")
local ngx_redis = require("resty.redis")
--创建实例
local redis_instance = ngx_redis :new()
--设置超时(毫秒)
redis_instance:set_timeout(3000)
--建立连接
local host = "127.0.0.1"
local port = 6379
local ok, err = redis_instance:connect(host, port)
if not ok then    --连接失败
ngx.say("connect to redis error : ", err)
return close_redis(redis_instance)
end
local resp, err =  redis_instance:get(goodsCacheKey) 
redis_instance:close()

if resp~=ngx.null  then   --redis存在就放入nginx本地缓存
-- ===========创建JSON实例============
-- local expireTime=math.random(10,20)   --//随机时间
cache_ngx:set(goodsCacheKey ,resp,10)   -- //这个过期时间要随机设置 反正并发对机器的负载过大
end
end         

-- =======发送http请求===========
goodsCache = cache_ngx:get(goodsCacheKey )   --再次从Nginx缓存中获取
if  goodsCache == "" or goodsCache  == nil or goodsCache==null then    --这里是redis没有数据存如Nginx就去发送http获取数据 
ngx.say("Redis缓存没有开始发送HTTP请求获取数据=========")
local http = require("resty.http")
local httpc =http.new()
local resp,err=httpc:request_uri("http://172.16.21.58:9999",{
    method="GET",
    path="/goodsCache/get/goods/"..goodsId ,
    keepalive=false
})
goodsCache = resp.body   --//得到请求响应结果
end    


local goodsJson=cjson.decode(goodsCache)  -- redis Json化处理

--=====最后Json数据重组=========
local context = {
goodsId=goodsJson.goodsId,
goodsName=goodsJson.goodsName,
price=goodsJson.price,
desc=goodsJson.desc,
}


--==template模板处理=========
local template =  require("resty.template")
template.render("goods.html",context)

 

但是lua在cjson.decode(Json数据) 只解析 

{"goodsName":"华为P1","goodsId":"1","price":1,"desc":"这是一款华为的产品1"} 这种格式

而我通过java进行redis存储的格式是

["com.zking.pojo.GoodsInfo",{"goodsName":"华为P1","goodsId":"1","price":1,"desc":"这是一款华为的产品1"}]

  或者

 "{\"desc\":\"这是一款华为的产品2\",\"goodsId\":\"2\",\"goodsName\":\"华为P2\",\"price\":2}"

lua脚本根本无法解析

2.在数据发生改变通过kafka消息队列去更新缓存数据如果更新Nginx缓存?

 

大家在研究到这里的时候 希望大佬能够指点一下,蟹蟹

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
多级缓存是一种在缓存系统中引入多个层级的缓存结构,用于提高系统的容错性和稳定性,同时减少缓存雪崩问题的影响。 多级缓存的结构通常包括三个层级:一级缓存、二级缓存和三级缓存。 1. 一级缓存:一级缓存是位于应用程序内部的内存缓存,通常使用较小的容量来存储热点数据。这样可以快速响应请求,减少对后端服务的依赖。一级缓存的数据更新速度比较快,可以通过订阅/发布模式或者回调机制来实时更新数据。 2. 二级缓存:二级缓存通常是一个分布式缓存系统,如Redis或Memcached。它具有较大的容量,可以存储更多的数据。二级缓存负责处理一级缓存未命中的请求,并将数据保存在内存中,提供更快的访问速度。二级缓存的数据更新通常由一级缓存触发,并通过缓存失效策略(如LRU、TTL等)来控制数据的过期和更新。 3. 三级缓存:三级缓存是位于后端存储系统的缓存层,如数据库或分布式文件系统。它用于存储冷数据或者需要持久的数据。三级缓存通常具有较大的容量,但响应速度较慢。数据从三级缓存读取到二级缓存时,可以采用预加载或者异步加载的方式,以减少请求的响应时间。 通过引入多级缓存,可以有效地减轻缓存雪崩的影响。当一级缓存或二级缓存发生故障或者缓存失效时,系统可以从更低级别的缓存中获取数据,减少对后端存储系统的直接访问。同时,多级缓存还可以根据数据的访问频率和重要性,动态调整每个层级的容量和失效策略,以提高系统的性能和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值