秒杀系统的设计思路(应对高并发,超卖等问题的解决思路)

首先我们先看一下设计秒杀系统时,我们应该考虑的问题。

解决方案:

 一.页面静态化结合CDN内容分发

前端把能提前放入cdn服务器的东西都放进去,反正把所有能提升效率的步骤都做一下,减少真正秒杀时候服务器的压力。

        秒杀活动的页面内容,大多数都是固定不变的。我们可以对秒杀页面做静态化处理,减少访问服务端的压力。在秒杀活动开始前,提前生成包含秒杀商品信息、倒计时等固定内容的静态 HTML 页面。这些页面可以根据秒杀活动的特点,例如开始时间、结束时间等来预先生成。

示例如下:

// 生成秒杀页面的HTML
public String generateSeckillPage(long goodsId) {GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);// 其他固定信息的获取// 生成HTML页面,包含秒杀商品信息、倒计时等String htmlContent = "<html><head>...</head><body>...</body></html>";// 将HTML页面保存到静态文件,或者存储到数据库中return htmlContent;
}

        参与秒杀的用户可能分布在全国各地,地域不同,网速不同。为了让用户可以快速的进入到秒杀活动的页面,可以使用 CDN(Content Delivery Network,内容分发网络)让用户可以就近获取所需内容。将生成的静态 HTML 页面通过 CDN 分发,使用户可以就近获取所需内容。CDN 的作用是将页面的静态资源分发到全球不同的节点,用户访问时可以从离他们最近的节点获取内容,减少网络延迟,提高访问速度。

        用户访问秒杀活动页面时,直接请求 CDN 上的静态 HTML 页面,不再需要服务端动态生成页面内容。这样可以减轻服务端的压力,提高页面加载速度,提升用户体验。

二.按钮置灰控制

        秒杀活动开始前,按钮一般需要置灰。只有时间到了,才能变得可以点击。 这防止,秒杀用户在时间快到的前几秒,疯狂请求服务器,然后秒杀时间点 还没到,服务器就自己挂了。

三. 服务单一职责

        我们都知道微服务设计思想,也就是各个功能模块拆分,功能那个类似放 一起,再用分布式部署方式进行部署。

        如用户登录相关,就设计个用户服务,订单相关就搞个订单服务等等。那么,秒杀相关业务逻辑也可以放到一起, 搞个秒杀服务,单独给它搞个秒杀数据库。” 服务单一职责有个好处:如果秒杀 没抗住高并发压力,秒杀库崩了,服务挂了,也不会影响到系统其他服。

四.秒杀地址加盐(url动态化)

        链接如果明文暴露的话,会有人获取到请求 Url,提前秒杀了。因此,需要给 秒杀链接加盐。可以让URL 动态化,如通过 MD5 加密算法加密随机字符串来生成,秒杀链接的URL。

时间校验: 通过获取商品的起始时间和结束时间,判断当前时间与秒杀活动的状态(未开始、进行中、已结束),从而判断用户是否可以参与秒杀。

 GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);long startAt = goods.getStartDate().getTime();long endAt = goods.getEndDate().getTime();long now = System.currentTimeMillis();int miaoshaStatus = 0;int remainSeconds = 0;if(now < startAt ) {//秒杀还没开始,倒计时miaoshaStatus = 0;remainSeconds = (int)((startAt - now )/1000);}else  if(now > endAt){//秒杀已经结束miaoshaStatus = 2;remainSeconds = -1;}else {//秒杀进行中miaoshaStatus = 1;remainSeconds = 0;}

        但仅仅是这样的话还是不够的,如果我们的秒杀的真实地址被人知道了,就可以写一个脚本不断的获取北京时间,在秒杀开始的毫秒级别时就可以请求。而且机器能在短时间内发送大量的请求,绝对会对我们的秒杀活动造成巨大的影响。
那怎么办呢?
        我们可以把我们的秒杀地址动态化,也就是通过MD5之类的加密算法去处理我们的秒杀地址,存入 redis缓存中,根据前端请求的url获取path。 判断与缓存中的字符串是否一致,一致就认为请求是正常的。这就是秒杀链接加盐,用这样的方法来阻止恶意用户直接请求我们的秒杀地址。

@RequestMapping(value="/path", method=RequestMethod.GET)
@ResponseBody
public Result<String> getMiaoshaPath( MiaoshaUser user,@RequestParam("goodsId")long goodsId,@RequestParam(value="verifyCode", defaultValue="0")int verifyCode) {if(user == null) {return Result.error(CodeMsg.SESSION_ERROR);}boolean check = miaoshaService.checkVerifyCode(user, goodsId, verifyCode);if(!check) {return Result.error(CodeMsg.REQUEST_ILLEGAL);}String path  =miaoshaService.createMiaoshaPath(user, goodsId);return Result.success(path);}public String createMiaoshaPath(MiaoshaUser user, long goodsId) {if(user == null || goodsId <=0) {return null;}String str = MD5Util.md5(UUIDUtil.uuid()+"123456");redisService.set(MiaoshaKey.getMiaoshaPath, ""+user.getId() + "_"+ goodsId, str);return str;}

实现原理总结如下:

  1. 用户通过前端请求获取秒杀地址的path。
  2. 后端根据用户信息、商品ID等生成动态的秒杀地址。
  3. 将生成的地址通过加盐的加密算法处理,存入Redis缓存中,建立用户ID和商品ID的关联。
  4. 用户提交秒杀请求时,携带path参数,后端根据前端请求的URL获取path,再与Redis缓存中的地址进行比对,判断请求的合法性。

通过这种方法,成功防止了用户通过直接请求秒杀地址进行恶意操作,保障了秒杀活动的公正性和安全性。

五.接口限流进行防刷

        一般有两种方式限流:nginx 限流和redis 限流。

为了防止某个用户请求过于频繁,我们可以对同一用户限流;

为了防止黄牛模拟几个用户请求,我们可以对某个 IP 进行限流;

为了防止有人使用代理,每次请求都更换 IP 请求,我们可以对接口进行限流。

1. Nginx 提供了基于令牌桶算法的限流机制,可以有效控制请求的流量。通过配置 ngx_http_limit_req_module 模块,可以设定每个 IP 或每个 URI 的请求频率上限。

http {limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;server {location /api {limit_req zone=one burst=5;# 其他配置}}
}

上述配置中,limit_req_zone 定义了一个名为 one 的限流区域,每个 IP 的请求速率为 1 次/秒,limit_req 用于限制每秒的请求数,burst 指定了可以超出速率的请求数 

2.0为了防止瞬时过大流量压垮系统,还可以使用阿里Sentinel、Hystrix 组件进行限流。

六. 超卖问题解决思路

        只要是一个秒杀系统,就必然会存在超卖问题。不同用户在读请求的时候,发现商品库存足够,然后同时发起请求,进行秒杀操作,减库存,导致库存减为负数。

1.0使用乐观锁解决

        如果要使用乐观锁,那么我们就要给个商品库存一个版本号version字段,在每次我们读取库存的时候把版本号也读取出来,当这一个线程去执行扣减库存的操作的时候,去判断数据库当前的版本号是否是刚刚读取出来的版本号,如果不是则秒杀失败。

/*** 查询商品库存* @param id 商品id* @return*/@Select("SELECT * FROM goods WHERE id = #{id}")Goods getStock(@Param("id") int id);/*** 乐观锁方案扣减库存* @param id 商品id* @param version 版本号* @return*/@Update("UPDATE goods SET stock = stock - 1, version = version + 1 WHERE id = #{id} AND stock > 0 AND version = #{version}")int decreaseStockForVersion(@Param("id") int id, @Param("version") int version);

service层:

/*** 扣减库存* @param gid 商品id* @param uid 用户id* @return SUCCESS 1 FAILURE 0*/@Transactionalpublic int sellGoods(int gid, int uid) {// 获取库存Goods goods = goodsDao.getStock(gid);if (goods.getStock() > 0) {// 乐观锁更新库存int update = goodsDao.decreaseStockForVersion(gid, goods.getVersion());// 更新失败,说明其他线程已经修改过数据,本次扣减库存失败,可以重试一定次数或者返回if (update == 0) {return 0;}// 库存扣减成功,生成订单Order order = new Order();order.setUid(uid);order.setGid(gid);int result = orderDao.insertOrder(order);return result;}// 失败返回return 0;}

2.0使用redis分布式锁

用 Redis  SET EX PX NX + 校验唯一随机值,再删除释放锁。

if (jedis.set(key_resource id, uni request id,"NX","Ex",100s) == 1)[ //加锁try {do something //业务处理catch(){}finally {//判断是不是当前线程加的锁,是才释放if (uni_request id.equals(jedis.get(key_resource_ id))) {jedis.del(lockKey);}}
}
七.MQ异步处理

如果瞬间流量特别大,可以使用消息队列削峰,异步处理。用户请求过来的时 候,先放到消息队列,再拿出来消费。

八.限流&降级&熔断

 限流,就是限制请求,防止过大的请求压垮服务器;

降级,就是秒杀服务有问题了,就降级处理,不要影响别的服务;

熔断,服务有问题就熔断,一般熔断降级是一起出现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/585348.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

九州金榜|家庭教育中如何培养优秀的孩子

家庭是孩子心中最大的底气&#xff0c;而家庭的形态也就构成孩子未来的模样。所以给为孩子提供最好的教育&#xff0c;就是为孩子创建一个学习气氛浓厚&#xff0c;成员关系美满的家庭&#xff0c;它会成孩子成长道路上最大的助力&#xff0c;最温暖的港湾。 北京大学校长蔡元培…

Windows Sockets 2 笔记

文章目录 一、Winsock简介二、Windows中Winsock对网络协议支持的情况三、使用Winsock3.1 关于服务器和客户端3.2 创建基本Winsock应用程序3.3 初始化Winscok3.3.1 初始化步骤3.3.2 初始化的核心代码3.3.3 WSAStartup函数的协调3.3.4 WSACleanup函数3.3.5 初始化的完整代码 3.4 …

Spring Boot学习随笔- 集成MyBatis-Plus,第一个MP程序(环境搭建、@TableName、@TableId、@TableField示例)

学习视频&#xff1a;【编程不良人】Mybatis-Plus整合SpringBoot实战教程,提高的你开发效率,后端人员必备! 引言 MyBatis-Plus是一个基于MyBatis的增强工具&#xff0c;旨在简化开发&#xff0c;提高效率。它扩展了MyBatis的功能&#xff0c;提供了许多实用的特性&#xff0c;…

Visual Studio 配置DLL

我们在用Visual Studio进行开发时&#xff0c;如果没有正确配置DLL&#xff0c;就会出现类似“丢失***.dll”的错误。DLL配置有哪些方法&#xff1f; 1、手动复制 将dll文件拷贝到生成的.exe所在的文件夹里 2、配置环境 在右键属性->配置属性->调试->环境&#xf…

Linux之定时任务调度

crond crond是Linux系统中的一个守护进程&#xff0c;主要用于周期性地执行某种任务或等待处理某些事件。而crondtab是配套的工作&#xff0c;用于定时任务的设置。 语法 crontab [选项]常用选项 入门案例 执行crontab -e命令输入任务到调度文件中 */1 * * * * ls -l /et…

鸿蒙原生应用再添新丁!爱奇艺入局鸿蒙

鸿蒙原生应用再添新丁&#xff01;爱奇艺 入局鸿蒙 来自 HarmonyOS 微博12月29日消息&#xff0c;#爱奇艺完成鸿蒙原生应用Beta版#作为中国头部在线视频平台&#xff0c;爱奇艺 完成鸿蒙原生应用Beta版&#xff0c;将以丰富的正版高清视频资源促进鸿蒙生态的进一步繁荣&#x…

准备用vscode代替sourceinsight

vscode版本1.85.1 有的符号&#xff0c;sourceinsight解析不到。 看网上说vscode内置了ripgrep&#xff0c;但ctrlshiftf在文件里查找的时候&#xff0c;速度特别慢&#xff0c;根本不像ripgrep的速度。ripgrep的速度是很快的。 但今天再查询&#xff0c;速度又很快了&#x…

SparkStreaming_window_sparksql_reids

1.5 window 滚动窗口滑动窗口 window操作就是窗口函数。Spark Streaming提供了滑动窗口操作的支持&#xff0c;从而让我们可以对一个滑动窗口内的数据执行计算操作。每次掉落在窗口内的RDD的数据&#xff0c;会被聚合起来执行计算操作&#xff0c;然后生成的RDD&#xff0c;会…

m3u8网络视频文件下载方法

在windows下&#xff0c;使用命令行cmd的命令下载m3u8视频文件并保存为mp4文件。 1.下载ffmpeg&#xff0c;访问FFmpeg官方网站&#xff1a;https://www.ffmpeg.org/进行下载 ffmpeg下载&#xff0c;安装&#xff0c;操作说明 https://blog.csdn.net/m0_53157282/article/det…

下载和安装AD14 - Altium Designer 14.3.20.54863

这个版本应该还支持XP 系统[doge]&#xff0c;总之就是想安装一下&#xff0c;没什么特别的意义。 下载 资源来自毛子网站&#xff1a;https://rutracker.net/forum/viewtopic.php?t5140739&#xff0c;带上个网页翻译插件就行。要用磁力链接下载&#xff0c;推荐用qbittorr…

RabbitMQ之快速入门、上手

前言 学习一样新技术、新框架&#xff0c;最重要的是学习其思想、原理。即原理性思维。 如果是因为工作原因&#xff0c;需要快速上手RabbitMQ&#xff0c;本篇或许适合你。 核心概念 Connection&#xff1a;publisher&#xff0f;consumer 和 broker 之间的 TCP 连接Channel…

Android 理解Context

文章目录 Android 理解ContextContext是什么Activity能直接new吗&#xff1f; Context结构和源码一个程序有几个ContextContext的作用Context作用域获取ContextgetApplication()和getApplicationContext()区别Context引起的内存泄露错误的单例模式View持有Activity应用正确使用…

八数码问题

八数码问题 在3x3的棋盘&#xff0c;摆有八个棋子&#xff0c;每个棋子上标有1至8的某一数字&#xff0c;不同棋子上标的数 字不相同。棋盘上还有一个空格&#xff0c;与空格相邻的棋子可以移到空格中。要求解决的问题 是:给出一个初始状态和一个目标状态&#xff0c;找出一一种…

详解—数据结构—<常用排序>基本实现和代码分析

目录 一.排序的概念及其运用 1.1排序的概念 1.2排序运用​编辑 1.3 常见的排序算法​编辑 二.常见排序算法的实现 2.1 插入排序 2.1.1基本思想&#xff1a; 2.1.2直接插入排序&#xff1a; 2.1.3 希尔排序( 缩小增量排序 ) 2.2 选择排序 2.2.1基本思想&#xff1a; …

日志记录、跟踪和指标

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 日志记录、跟踪和指标是系统可观察性的三大支柱。 下图显示了它们的定义和典型架构。 记录 日志记录系统中的离散事件。例如&#xff0c;我们可以将传入请求或对…

论文阅读——UniRepLKNet

UniRepLKNet: A Universal Perception Large-Kernel ConvNet for Audio, Video, Point Cloud, Time-Series and Image Recognition 当我们将一个33的conv添加到一个小卷积核ConvNet中时&#xff0c;我们预计它会同时产生三种效果——1&#xff09;使感受野更大&#xff0c;2&am…

Python之自然语言处理库snowNLP

一、介绍 SnowNLP是一个python写的类库&#xff0c;可以方便的处理中文文本内容&#xff0c;是受到了TextBlob的启发而写的&#xff0c;由于现在大部分的自然语言处理库基本都是针对英文的&#xff0c;于是写了一个方便处理中文的类库&#xff0c;并且和TextBlob不同的是&…

elasticsearch 笔记三:查询建议介绍、Suggester、自动完成

一、查询建议介绍 1. 查询建议是什么&#xff1f; 查询建议&#xff0c;为用户提供良好的使用体验。主要包括&#xff1a; 拼写检查&#xff1b; 自动建议查询词&#xff08;自动补全&#xff09; 拼写检查如图&#xff1a; 自动建议查询词&#xff08;自动补全&#xff09;…

Rust之构建命令行程序(二):读取文件

开发环境 Windows 10Rust 1.74.1 VS Code 1.85.1 项目工程 这次创建了新的工程minigrep. 读取文件 现在&#xff0c;我们将添加读取file_path参数中指定的文件的功能。首先&#xff0c;我们需要一个样本文件来测试它:我们将使用一个包含少量文本的文件&#xff0c;多行包含一…

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】ShuffleNet_V2模型算法详解

【图像分类】【深度学习】【轻量级网络】【Pytorch版本】ShuffleNet_V2模型算法详解 文章目录 【图像分类】【深度学习】【轻量级网络】【Pytorch版本】ShuffleNet_V2模型算法详解前言ShuffleNet_V2讲解四条实用指导思想G1:相等的通道宽度可以降低存储访问成本G2:大量的分组卷积…