标签:缓存 浅谈 -- redis 接口 html 秒杀 页面
前两节,只是强迫自己再现Springboot项目搭建的过程,增强记忆,熟悉一下SpringBoot的使用;
后面做的是 实现秒杀的业务逻辑 + 优化。
业务逻辑就是 增删改查了,没什么好说的。
但是优化,相较于以前在学校做的增删改查,能打开一个新的视野,对业务处理也会有一个更深的认识。
并且这些经验思想 可以在任何一个项目 复用。
就像一段好的基础框架代码(见Unity开发中的 缓存池),复制到任何一个项目,都能起到很大的基础构建作用。
下面是实际操作过的 优化动作, 最后有一个 视野更大 但是没实践过(很虚)的思路小结。
做过的优化
1、 缓存
用于读取多,变更少场景。 主要应对并发读。
一般用于前端展示中。
2、服务角度
目的: 减少数据库操作、减少redis操作。
思想:将读取的数据 缓存起来
3、接口安全性保障
- 秒杀接口地址隐藏
防止接口暴露,被脚本频繁访问 - 验证码
也是阻止脚本顺畅进行,设一道门坎 - 接口限流
接口限流比较实用;防止脚本级别才能做出的 频繁访问
1、缓存
(1)html缓存
**应用:**一般用于列表展示。
**思路:**将整个html页面放入 redis缓存中,加一个过期时间(比如60s),后续再次访问的时候,就直接从redis拿取页面数据,返回到 浏览器。
**为什么:**所有用户都会访问 商品列表,所以是频繁访问;且修改只有内部人员在修改商品时才会修改,所以有一个 60s失效时间是可以忍受的,或者再更新的时候更新redis数据。
**注意:**一般只缓存列表前 几十页,因为后面的页面很少被访问。
优化提升: (访问数据库得到列表的时间 - 访问redis缓存得到html页面的时间)* 访问量。
**缺点:**传输的是 整个 html页面,所以也有重复的数据(静态资源),见下面的优化。
(2)对象缓存(前后端分离)
引导: 在前后端分离的 http请求中, 数据传输可以看作两部分: 静态的html页面 + html页面需要渲染的数据。
- 静态的html页面: 可以被视为静态资源 被管理的(Springboot有对静态资源处理),这部分可以保存在客户端的浏览器中,作为缓存。
- 动态的渲染数据:可以通过redis 把它当作对象 缓存起来。
Springboot 配置文件 静态资源管理:
spring:
#静态资源处理
resources:
#启用静态资源处理
add-mappings: true
cache:
cachecontrol:
#缓存响应时间,单位秒
max-age: 3600
chain:
#资源连 启动缓存,默认true,没开启
cache: true
enabled: true
#启用压缩(gzip,brotli) 解析,默认禁用
compressed: true
#h5应用缓存
html-application-cache: true
#静态资源 路径
static-locations: classpath:/static/
应用: 需要渲染数据的 静态html页面, 比如商品详情页面(只有商品数据 不同,需要渲染)
**思路:**我们只需要得到对象的数据,可以从redis里得到缓存对象数据, 也可以直接访问数据库获取数据。前端,用ajax获取数据即可。
**优化提升:**省去了 html静态资源数据的传输时间(相较于上一点)
2、服务角度
主要思想: 减少数据库操作;
辅助工具: Redis、RabbitMQ。
下面以秒杀为背景:
秒杀请求 -》 redis预减库存;失败直接返回。 -》 消息队列,异步处理请求和数据库操作;返回给客户端 正在排队; 同时前端 轮询,是否抢到,没有抢到 返回失败。
(1)启动项目,redis放入预加载数据
- Controller 实现 InitializingBean 接口
- 实现 接口函数: afterPropertiesSet(); 里面 放入Redis预加载数据。
(2)预减库存
进入秒杀接口:
- 重复抢购
(1)用户下单成功的话,添加该用户的 下单缓存,有效时间:秒杀时间。
(2)再次进入秒杀接口,查看是否有缓存,有则直接返回失败。 - 库存不足
(1)查看是否有该商品的内存标记(map记录即可),有则返回失败。
(1)redis调用原子方法,预减库存。
(2)库存不足,直接返回;同时,添加一个内存标记:标记某个商品id 已被抢购。 - 其他
(3)异步下单、同时 前端轮询是否秒杀成功
这里主要保证数据的正确性。同时给客户一个及时反馈的
- sender发送消息,数据转为str。
- receive接收消息,得到应有数据;进行下单处理。
- 前端页面轮询,查询是否得到数据。《这里又回到 并发读 的问题了,优化仁者见仁》
3、接口安全性保障
微服务、限流等,一下从三个角度来简单说说:
(1)秒杀接口地址隐藏
**目的:**不要让秒杀这样重要的接口 直接暴露出来。 见接口暴露造成损失的实例。
思路: 每次发起请求,将url一段 包装一下,(1)比如md5加密,存到redis;(2) 前端ajax获取path,发起真正请求;(3) 后端验证path正确性,才真正进行请求。
(2)验证码
一般的验证码,OCR等可以轻松识别并破解;建议用脚本不能轻易破解的验证码:鼠标拖动、算术运算等。
目的: (1)阻止脚本,不让脚本顺畅进行。(2)流量削峰:比如算数运算结果,一般人在0-10秒内算出,那么这一波 高并发的请求就被分到10秒里了,而不是一瞬间堆积起来。
思路:(1)生成验证码,存入redis,加入有效时间。 (2)发起请求时,先验证最新的验证码,通过才及进行后续请求。
(3)接口限流
简单接口限流: 需求是 对某一个接口, 1分钟(一段时间)指定用户 只能访问 固定次数,超过次数,直接返回。
**Springboot里思路:**把这一段 通用 成注解:时间、次数、是否需要登录。
每次访问,如果为空,创建 时间 的redis数据; 如果大于 次数,直接返回 失败; 否则调用 原子函数 加一。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {
int second();
int maxCount();
boolean needLogin() default true;
}
写好,就能以函数注解的形式,监听这个接口了。
扩展:漏桶算法、令牌桶算法。
小结
优化
1、缓存 频繁需要访问的数据。
2、对于无效的请求, 直接返回。
看到一个比较系统的描述
两种提高系统并发能力的方式:垂直扩展、水平扩展。
1、垂直扩展
垂直扩展有两种:
(1)增强单机硬件性能: CPU核数、网卡、硬盘、硬盘容量、系统内存等。
(2)提升单机架构性能: 使用缓存 减少IO次数,使用异步增加服务吞吐量,使用无锁数据结构减少响应时间。
单机性能总是有限的,所有终极解决方案:水平扩展。
2、水平扩展
- 只要增加服务器数量,就能先行扩充系统性能。
- 需要对系统架构设计有要求。
(1)常见互联网分层架构
(1)反向代理层
DNS轮询
理论上: nginx瓶颈的时候,只要增加服务器数量,新增nginx服务的部署,增加一个外网ip,就能无线高并发。
(2)web站点
和nginx配合,增加服务器数量,新增web服务部署,nginx配置新的web后端。
(3)服务层
(4)数据层
数据库拆分:
- 按照范围水平拆分
user0: 1-1kw
user1: 1kw-2kw等
负载不一定均衡;比如 新注册用户会比老玩家活跃。 - 按照哈希水平拆分
user0, 偶数uid
user1,奇数uid
水平拆分,和主从同步读写分离不同:
水平拆分,读写都扩充了n被; 主从 读扩充n倍,写不变;等等。
标签:缓存,浅谈,--,redis,接口,html,秒杀,页面 来源: https://blog.csdn.net/weixin_44919162/article/details/120627093
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。