(转)开源项目miaosha(上)
石墨文档:https://shimo.im/docs/iTDoZs4CVfICgSfV/
【课程19】几张图.xmind0.6MB
【课程19】开源秒...解读.xmind0.1MB
【课程19】秒杀项...要点.xmind68.6KB
【课程19】秒杀项...方向.xmind74.7KB
【课程19预习】秒...解读.xmind0.5MB
【课程19】redis的运用.xmind47.8KB
秒杀场景一般会在电商网站举行一些活动或者节假日在12306网站上抢票时遇到。对于电商网站中一些稀缺或者特价商品,电商网站一般会在约定时间点对其进行限量销售,因为这些商品的特殊性,会吸引大量用户前来抢购,并且会在约定的时间点同时在秒杀页面进行抢购。
瞬时并发量大
- 秒杀时大量用户会在同一时间同时进行抢购,网站瞬时访问流量激增。
库存量少
- 秒杀一般是访问请求数量远远大于库存数量,只有少部分用户能够秒杀成功。
业务简单
- 秒杀业务流程比较简单,一般就是下订单减库存。
限流: 鉴于只有少部分用户能够秒杀成功,所以要限制大部分流量,只允许少部分流量进入服务后端。
削峰:对于秒杀系统瞬时会有大量用户涌入,所以在抢购一开始会有很高的瞬间峰值。高峰值流量是压垮系统很重要的原因,所以如何把瞬间的高流量变成一段时间平稳的流量也是设计秒杀系统很重要的思路。实现削峰的常用的方法有利用缓存和消息中间件等技术。
异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。
内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。
可拓展:当然如果我们想支持更多用户,更大的并发,最好就将系统设计成弹性可拓展的,如果流量来了,拓展机器就好了。像淘宝、京东等双十一活动时会增加大量机器应对交易高峰。
- 对现有业务冲击
- 高并发应用负载高
- 突然增加网络与服务宽带
- 直接下单
- 控制商品页面购买按钮点亮
- 下单前置检查
- 前端:
页面静态化:将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素。通过CDN来抗峰值。
禁止重复提交:用户提交之后按钮置灰,禁止重复提交。
- 服务端:
(1)将请求尽量拦截在系统上游(不要让锁冲突落到数据库上去)。传统秒杀系统之所以挂,请求都压倒了后端数据层,数据读写锁冲突严重,并发高响应慢,几乎所有请求都超时,流量虽大,下单成功的有效流量甚小。以12306为例,一趟火车其实只有2000张票,200w个人来买,基本没有人能买成功,请求有效率为0。
(2)充分利用缓存,秒杀买票,这是一个典型的读多写少的应用场景,大部分请求是车次查询,票查询,下单和支付才是写请求。一趟火车其实只有2000张票,200w个人来买,最多2000个人下单成功,其他人都是查询库存,写比例只有0.1%,读比例占99.9%,非常适合使用缓存来优化。
(3)消息队列:消息队列可以削峰,将拦截大量并发请求,这也是一个异步处理过程,后台业务根据自己的处理能力,从消息队列中主动的拉取请求消息进行业务处理。
(4)用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取IP限流。
- 数据库层:
数据库层是最脆弱的一层,一般在应用设计时在上游就需要把请求拦截掉,数据库层只承担“能力范围内”的访问请求。所以,上面通过在服务层引入队列和缓存,让最底层的数据库高枕无忧。
(秒杀项目参考逻辑)
。本项目是使用Redis作为缓存的一个秒杀项目实例。
磁盘IO,开发机器实测2280 OPS,速度太低,当出现海量请求时会导致大量请求线程被阻塞,拒绝后续请求,拖垮整个tomcat和DB。
- a、用户请求过来,将请求入消息队列;
- b、消息处理,先减redis库存量,如果减库存成功,则生成下单token存入redis(设定有效期,比如2分钟之内下单有效),等待用户下单(这样就避免下单也面对大量并发);如果减库存失败,则消息记录回到消息队列中,等待再次处理;
- c、用户下单:判断token是否失效(比对时间)了,如果未失效则扣减库存(也可能扣减库存失败),生成订单;如果已经失效了,则redis库存增加1; 如何确保下单token过期了释放资格?JOB 每分钟扫token缓存,如果失效了的则清除调,并回馈redis缓存(redis库存+1);
- d、前端用户如何获知抢购成功了(获得了下单资格):ajax轮训查询接口。 说明:为什么要采用轮询而不是用实时的websocket推送?经测试,一台tomcat最多能连接3000个websocket,如果类似抢购的大量用户抢购,机器肯定是扛不住这么多长连接的,而查询用户是否抢购成功也只是查询的redis,因此采用轮询是很好的选择。
- e、为什么要秒杀和下单操作分离?
- 一方面,秒杀接口可以阻挡大部分并发流程,从而让下单操作错开并发高峰;
- 另一方面,可以让秒杀操作和下单操作从业务上相分离,使得秒杀操作可以独立于订单相关业务。
针对第2方案中可能出现被辅助软件而已刷单的现象,可以增加过滤器:如果用户在指定时间内请求多少次,则认为是恶意用户,可以直接将该用户加入黑名单,并在后续的消息队列处理中不给黑名单的用户分配资格。
- 前端
- ajax
- countdown
- 后端
- redis作为缓存、消息队列
(页面逻辑)
(后台接口逻辑)
(消息队列异步处理流程图)
(总流程时序图)
秒杀设计时序图.puml3.5KB
- 秒杀详情页,等待秒杀开始
- 秒杀开始,获取秒杀链接
- 进入秒杀,限流,判断是否重复秒杀,把请求消息推入redis消息处理队列
- 消息处理器监听到消息后开始处理:
- 监测黑名单
- 判断秒杀是否已结束
- 先减redis库存(占位)
- 生成下单token,存入redis供前端查询
- 前端查询秒杀结果
- 成功:显示下单按钮,用户使用token去下单
- 用户下单
- 检查token有效性,检查库存
- 减库存,下单。(真正减库存)
拦截器:
- 恶意IP检测拦截器
- 获取真实ip
- 匹配ip是否是正常ip,是否在黑名单库
- 使用redis增加ip的访问次数
- 超过限定次数就加入到黑名单库
- 恶意用户检测拦截器
- 同ip监测拦截
商品库存信息预先生成,服务器启动时加载。
入口只有活动开启前才能获得
入口恶意用户检测:多秒内多少次请求---可以记录最近10次请求时间,和前第九次请求时间对比
- 实时限流:限制正在处理的请求量(通过消息队列获取正在处理的请求数目)为库存的100倍请求(这个可自定义);
- 如果出现了限流器满了,但仍然有库存的情况怎么办?直接拒绝请求,允许用户重新提交请求 ##请求减库存
- 请求通过了过滤之后,交给消息队列减库存+下单 ##消息队列处理
- 消息队列再次过滤请求是否是恶意的用户
- 否则,执行减库存+下单
限制用户维度访问频率
- ip黑名单
- 用户黑名单
Redis是一个分布式缓存系统,支持多种数据结构,我们可以利用Redis轻松实现一个强大的秒杀系统。作为缓存和消息队列使用
Redis incr 可以实现原子性的递增,可应用于高并发的秒杀活动、分布式序列号生成等场景。
Redis丰富的数据结构适用于多种场景,如消息队列。
message-trunk是以redis为基础搭建的轻量级高性能消息总线(队列),和主流MQ相比使用起来更灵巧简便。
git地址:https://gitee.com/1028125449/message-trunk
使用方法:
- 获取消息队列全局对象MessageTrunk(可以用spring注入),put入消息即可。
// 获取MessageTrunk实例
MessageTrunk mt = (MessageTrunk) SpringBeanUtils.getBean("messageTrunk");
Message<DemoMessage> message = new Message<DemoMessage>(MessageType.DEMO_MESSAGE, new DemoMessage(value));
// 消息入MT
mt.put(message);
- 消息处理器:继承AbstarctMessageHandler抽象类。
public class DemoHandler extends AbstarctMessageHandler<DemoMessage>{
private static Log logger = LogFactory.getLog(DemoHandler.class);
public DemoHandler(){
// 说明该handler监控的消息类型
super(MessageType.DEMO_MESSAGE);
}
/**
* 监听到消息后处理方法
*/
@Override
public void handle(DemoMessage message){
// do handle
}
@Override
public void handleFailed(DemoMessage obj){
// handle failed
}
}
- 哈希
- 存储黑名单(ip,用户)
- 秒杀处理请求列表
- 字符串
- 增减库存
- 商品描述是否结束标志
- 下单token
- ...
- 列表
- 用户访问记录
我们常用的操作数据库语言 SQL 语句在执行的时候需要要先编译,然后执行,而存储过程( Stored Procedure )是一组为了完成特定功能的 SQL 语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。
FOUND_ROWS : 获取上一个select语句查询到的行数;
ROW_COUNT : 获取上一条update, insert ,delete 影响的行数;
MySQL存储过程创建的格式:CREATE PROCEDURE 过程名 ([过程参数 [,...]])
[特性 ...] 过程体
mysql> DELIMITER //
mysql> CREATE PROCEDURE proc1(OUT s int)
-> BEGIN
-> SELECT COUNT(*) INTO s FROM user;
-> END
-> //
mysql> DELIMITER ;
用call和你过程名以及一个括号,括号里面根据需要,加入参数,参数包括输入参数、输出参数、输入输出参数。
参数:
IN 输入参数:表示该参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值
OUT 输出参数:该值可在存储过程内部被改变,并可返回
INOUT 输入输出参数:调用时指定,并且可被改变和返回
存储过程通常有以下优点:
(1).存储过程增强了SQL语言的功能和灵活性。存储过程可以用流控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。
(2).存储过程允许标准组件是编程。存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句。而且数据库专业人员可以随时对存储过程进行修改,对应用程序源代码毫无影响。
(3).存储过程能实现较快的执行速度。如果某一操作包含大量的Transaction-SQL代码或分别被多次执行,那么存储过程要比批处理的执行速度快很多。因为存储过程是预编译的。在首次运行一个存储过程时查询,优化器对其进行分析优化,并且给出最终被存储在系统表中的执行计划。而批处理的Transaction-SQL语句在每次运行时都要进行编译和优化,速度相对要慢一些。
(4).存储过程能过减少网络流量。针对同一个数据库对象的操作(如查询、修改),如果这一操作所涉及的Transaction-SQL语句被组织程存储过程,那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,从而大大增加了网络流量并降低了网络负载。
(5).存储过程可被作为一种安全机制来充分利用。系统管理员通过执行某一存储过程的权限进行限制,能够实现对相应的数据的访问权限的限制,避免了非授权用户对数据的访问,保证了数据的安全。
(转)开源项目miaosha(上)的更多相关文章
- 个人开源项目如何上传maven中央仓库
最近在写一些开源项目,想把自己写的东西放到maven中央仓库,提供给更多的人使用.所以写这一篇文章,记录一下自研开源项目jar包上传同步maven中央仓库成功的整个过程,这其中还是有不少的坑的. 目录 ...
- (转)开源项目miaosha(下)
石墨文档:https://shimo.im/docs/2XlwliBQAYsKCHbq/ (二期)20.开源秒杀项目miaosha解读(下) [课程20]jmeter.xmind81.5KB [课程2 ...
- 你必须了解的RecyclerView的五大开源项目-解决上拉加载、下拉刷新和添加Header、Footer等问题
前段时间做项目由于采用的MD设计,所以必须要使用RecyclerView全面代替ListView.但是开发中遇到了需要实现RecyclerView上拉加载.下拉刷新和添加Header以及Footer等 ...
- 加密解密、食谱、新冠序列,各种有趣的开源项目Github上都有
Github上是我们程序员学习开源代码.提升编程技巧的好地方.好学校,但是除了学习,小伙伴们有没有发现过Github上一些特别有意思的项目呢? 今天TJ君就来和大家分享几个自认为特别有趣的开源项目: ...
- GitHub上史上最全的Android开源项目分类汇总 (转)
GitHub上史上最全的Android开源项目分类汇总 标签: github android 开源 | 发表时间:2014-11-23 23:00 | 作者:u013149325 分享到: 出处:ht ...
- 扬我国威,来自清华的开源项目火爆Github
前几天TJ君跟大家分享了几个有趣的Github项目(加密解密.食谱.新冠序列,各种有趣的开源项目Github上都有),其中呢,有不少是来自斯坦福大学的项目,当时TJ君就不由得想,什么时候能看到的项目都 ...
- 2018年12月份GitHub上最热门的Java开源项目
来自:开源最前线(ID:OpenSourceTop) 链接:https://www.itcodemonkey.com/article/12747.html 又到了公布 GitHub 上热门项目的时候啦 ...
- Github上关于iOS的各种开源项目集合(强烈建议大家收藏,查看,总有一款你需要)
下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITableVie ...
- android开发中在界面上实现曲线图的几个开源项目
转自:https://wapiknow.baidu.com/question/1959128379041474620?qq-pf-to=pcqq.c2c 几个相关开源项目: 1. MPAndroid ...
随机推荐
- c#之如何计算哈希值字符串
代码如下: /// <summary> /// 计算哈希值字符串 /// </summary> public static string ComputeHash(byte[] ...
- Vivado的安装程序没反应怎么办
在Windows操作系统上,在安装Vivado的时候会遇到双击xsetup.exe没有反应的情况,即使是用管理员权限再加上设置兼容模式也没有任何效果,且此问题有可能在多个版本上都存在,包括最新的201 ...
- 20155228 2016-2017-2 《Java程序设计》第2周学习总结
20155228 2006-2007-2 <Java程序设计>第2周学习总结 教材学习内容总结 类型 Java可以区分为基本类型和类类型(或称参考类型).对于基本类型,使用时得考虑一下数据 ...
- centos中yum命令删除还原的补救方法介绍
前言 yum,是Yellow dog Updater Modified的简称,起初是由yellow dog这一发行版的开发者Terra Soft研发,用python写成,那时还叫做yup(yellow ...
- Locust 设置响应断言
转:http://www.testclass.net/locust/assert/ 性能测试也需要设置断言么? 某些情况下是需要,比如你在请求一个页面时,就可以通过状态来判断返回的 HTTP 状态码是 ...
- Codeforce 507B - Amr and Pins
Amr loves Geometry. One day he came up with a very interesting problem. Amr has a circle of radius r ...
- JAVA基本语法测试
一: 1,JAVA的基本运行单位是类 2,类的成员:成员变量,构造方法,普通方法和内部类 3,成员变量种类:字符类型:char 布尔类型:boolean 数值类型:byte, s ...
- java加载配置文件信息
#基金数据存放根目录fund_save_root_path=E:/fundCrawling #龙虎榜数据存放根目录long_hu_root_path=E:/longHuCrawling #巨潮数据存放 ...
- bp暴力破解(转载)
在kali linux系统环境下自带burpsuite软件工具. 一.打开浏览器需要先设置将代理设置为本地. 打开firefox浏览器->open menu->preferences-&g ...
- Prometheus监控学习笔记之prometheus的远端存储
0x00 概述 prometheus在容器云的领域实力毋庸置疑,越来越多的云原生组件直接提供prometheus的metrics接口,无需额外的exporter.所以采用prometheus作为整个集 ...