Day100 Java项目 (SSM+Dubbo)商城(九) 缓存解决方案

1.SpringDataRedis

一.SpringDataRedis简介

  1. SpringDataRedis 属于Spring Data 家族一员,用于对redis的操作进行封装的框架
  2. Spring Data ----- Spring 的一个子项目。Spring 官方提供一套数据层综合解决方案,用于简化数据库访问,支持NoSQL和关系数据库存储。包括Spring Data JPA 、SpringData Redis 、SpringDataSolr 、SpringDataElasticsearch 、Spring DataMongodb 等框架。

二.SpringDataRedis快速入门

  1. 准备工作
    1. 构建Maven工程 SpringDataRedisDemo 引入Spring相关依赖、JUnit依赖、Jedis和SpringDataRedis依赖
    2. 在src/main/resources下创建properties文件夹,建立redis-config.properties

      maxIdle :最大空闲数
      maxWaitMillis: 连接时的最大等待毫秒数
    3. 在src/main/resources下创建spring文件夹,创建applicationContext-redis.xml
  2. 值类型操作
  3. Set类型操作


  4. List类型操作
    1. 右压栈 后添加的对象排在后边

      运行结果:[刘备, 关羽, 张飞]
    2. 左压栈 后添加的对象排在前边

      运行结果:[张飞, 关羽, 刘备]
    3. 根据索引查询元素
    4. 移除指定个数的值
  5. Hash类型操作
    1. 存入值
    2. 提取所有的KEY

      运行结果:[a, b, c, d]
    3. 提取所有的值
    4. 根据KEY提取值
    5. 根据KEY移除值
  6. ZSet类型操作
            zset是set的升级版本,它在set的基础上增加了一格顺序属性,这一属性在添加元素的同时可以指定,每次指定后,zset会自动重新按照新的值调整顺序。可以理解为有两列的mysql表,一列存储value,一列存储分值。
    1. 存值 ,指定分值
    2. 查询,由低到高
    3. 查询,由高到低
    4. 增加分数
    5. 查询值和分数

      TypedTuple是值与分数的封装。
  7. 过期时间设置
    以值类型为例:存值时指定过期时间和时间单位

2.缓存穿透、缓存击穿、缓存雪崩

一.缓存穿透

  1.       缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。如下面这段代码就存在缓存穿透的问题。
  2. 解决方案:
    1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
    2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-0。这样可以防止攻击用户反复用同一个id暴力攻击。代码举例:
    3. 使用缓存预热
              缓存预热就是将数据提前加入到缓存中,当数据发生变更,再将最新的数据更新到缓存。后边我们就用缓存预热的方式实现对分类导航、广告轮播图等数据的缓存。

二.缓存击穿

  1. 缓存击穿是指缓存中没有但数据库中有的数据。这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
    以下代码可能会产生缓存击穿:
  2. 解决方案:
    1.设置热点数据永远不过期。
    2.缓存预热

三.缓存雪崩

  1. 缓存雪崩是指缓存数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
  2. 解决方案:
    1.缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
    2.设置热点数据永远不过期。
    3.使用缓存预热

3.商品分类导航缓存

一.需求分析

  1. 为了提升首页的加载速度,减轻数据库访问压力,我们将首页的商品分类导航数据加载在缓存中。

二.实现思路

  1. 为了避免缓存穿透、击穿等问题,我们采用缓存预热的方式实现对分类导航数据的缓存。
  2. 考虑到商品分类导航数据不经常变动,所以我们不设置过期时间。

三.通用模块整合spring data redis

  1. qingcheng_common_service引入依赖
  2. qingcheng_common_service新增配置文件 redis-config.properties

    maxWait:连接池中连接用完时,新的请求等待时间,毫秒
    maxIdle: 最大闲置个数
  3. qingcheng_common_service新增spring配置文件applicationContext-redis.xml
  4. qingcheng_common_service工程新增枚举

四.商品分类加载到缓存

  1. 服务接口CategoryService新增方法定义
  2. CategoryServiceImpl实现此方法
  3. qingcheng_service_goods工程新增类

    实现InitializingBean接口的类会在启动时自动调用。

五.查询商品分类缓存

  1. 修改CategoryServiceImpl的findCategoryTree方法 ,直接从缓存中提取数据。

六.更新商品分类缓存

  1. 修改CategoryServiceImpl的增删改方法,在增删改后重新加载缓存

4.广告轮播图缓存

一.需求分析

  1. 为了提升首页的加载速度,减轻数据库访问压力,我们将首页的广告轮播图数据加载在缓存中。

二.实现思路

  1. 使用“缓存预热”的方式实现
    广告数据不只是轮播图,我们可以使用hash来存储广告数据。

三.广告数据加载到缓存

  1. AdService新增方法定义
  2. AdServiceImpl方法实现

  3. qingcheng_service_business工程新增类

四.查询广告缓存

  1. 修改AdServiceImpl的findByPosition方法

五.更新广告缓存

  1. 修改AdServiceImpl的增删改方法

5.商品详细页价格缓存

一.需求分析

  1. 我们已经将商品的信息生成为静态页面,但是商品价格经常变动,如果每次价格变动后都对静态页重新生成会影响服务器性能。所以,对于商品价格,我们采用异步调用的方式来进行客户端渲染。

二.实现思路

  1. 商品服务启动后加载全部价格数据到缓存。使用hash存储,skuID作为小KEY
  2. 从缓存从查询商品价格,封装为controller,并设置可跨域调用
    1. 什么叫跨域?
      当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。

      JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。那什么是跨域呢,简单地理解就是因为JavaScript同源策略的限制,a.com域名下的js无法操作b.com或是c.a.com域名下的对象。
    2. 如何解决跨域问题?我们使用CORS实现跨域。
      CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resourcesharing)。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了AJAX只能同源使用的限制。使用非常简单,只需要在controller类上添加一个 @CrossOrigin注解即可
  3. 修改商品详细页模板,使用ajax读取价格,并进行客户端渲染。

三.价格数据加载到缓存

  1. SkuService接口新增方法定义
  2. SkuServiceImpl类新增方法
  3. 修改InitService类

四.查询价格缓存

  1. SkuService新增方法定义
  2. SkuServiceImpl实现findPrice方法
  3. qingcheng_web_portal工程新增类
  4. 前端代码(修改模板):
    (1)将vue.js axios.js 放到我们html输出文件夹下的js文件夹中。
    (2)修改qingcheng_web_portal工程的模板 item.html

    th:inline 定义js脚本可以使用变量 js脚本的变量用 /*[[${ }]]*/ 渲染
    (3)修改qingcheng_web_portal工程的模板 item.html ,添加 <div id="app">...
    </div> ,并将价格修改为vue表达式

五.更新价格缓存

  1. SkuService接口新增方法定义
  2. SkuServiceImpl类新增方法
  3. SpuServiceImpl类引入SkuService
  4. 修改SpuServiceImpl类saveGoods方法,在SKU列表循环体中添加代码

六.删除价格缓存

  1. SkuService新增方法定义
  2. SkuServiceImpl新增方法实现
  3. 修改SpuServiceImpl的delete方法,新增代码逻辑
一、基于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、付费专栏及课程。

余额充值