电商系统秒杀二 秒杀场景下如何进行限流

news2024/9/28 15:31:25

本章学习内容
1、在秒杀页面,客户点击秒杀后,在前台弹出一个验证码,需要用户输入验证码才能往后端发送请求,这样能够错开秒杀下单的时间。
2、通过验证码,对后台下单请求进行保护,防止刷单,即防止绕开前端,直接往后端发送请求(验证码必须要做到防止刷单的功能)。
在这里插入图片描述

在秒杀页面开始秒杀后,客户点击秒杀按钮,要在前台弹出一个验证码,需要用户输入验证码才能往后端发请求,这样能够错开秒杀下单时间。
在我们的实现中,是要将memberId、producrId和验证码的值一起传入后台,后台返回一个token。然后再根据这个tokne拼接一个后台秒杀地址。这个token会存入到redis中。实际秒杀时,会增加一个判断,检测这个token是不是在redis中存在。如果不存在,就是机器刷单

一 电商项目中秒杀的实现流程

1.1 页面操作流程

在tmll-admin中添加秒杀活动,在秒杀活动中先设置活动的开始日期和结束日期,然后添加商品
在这里插入图片描述
这个秒杀活动信息会保存到mysql中。sms_flash_promotion_product_relation表;
同时,添加商品后会将活动商品保存到ZK中。(路径/ZkLock/load_db/{productId})。然后,当访问到商城前端商品页时, http://localhost:8080/#/product/{productId},会检查Redis中的产品信息缓存。如果Redis中没有产品信息,就会重建Redis缓存。key为product:detail:cache:{ProdId}

在这里插入图片描述
然后进入商城的单品页 http://localhost:8080/#/product/32 , product.vue那个"立即购买"的按钮就会变成"立即秒杀";
点击立即秒杀就会进入秒杀页,secKillDetail.vue;
在这里插入图片描述

1.2 代码实现机制

1、从Redis判断商品是否有秒杀活动

一个商品要么就只能秒杀,要么就只能普通购买,这样是否合理? 这就是为什么要单独独立出一套秒杀服务集群。

2、发送后台请求申请验证码。后台返回验证码图片,并将验证码的计算结果保存到Redis

验证码的请求路径里header里的memeberId是怎么进去的。有什么用?
生成验证码图片的这个请求要怎么防刷?

3、保护后台请求接口
输入验证码后,先验证输入的验证码结果,返回一个Token。这个Token会传入到接下来的商品确认页面,同时会保存到Redis当中,表示当前用户有购买秒杀商品的资格。有效期300秒,300秒内必须完成下单,否则就要重新申请秒杀资格。

在后续的下单过程中,需要传入这个Token才能正常下单。

验证码如果输入错误,是如何判断的?

二 如何加强限流方案的安全性

了解整理流程后,要继续深入思考下我们这个限流方案的安全性

2.1 针对验证码

针对验证码的安全性,可以加上之前的验证码内容

1、我们做了这一套机制后,到底有多安全? 下单请求依然是可以用机器人模拟的。

  • 用户ID是存在Cookie当中的,可以拿到。
  • 图形验证码是随机的,那就总有可能产生容易被机器识别的验证码。

2、怎么加强验证码本身的安全性
这个问题也是必须要前后台配合来思考的,而不是单独靠前端或者后端能够解决的。这个方案要如何设计? 提高验证码安全性的措施:

  • 加干扰线或者干扰点,
  • 将关键字符变形并且在图形上串到一起。
  • 增加更多的前端交互,行为验证。
  • 验证码的内容最好是一个比较复杂的题目,而不是简单的输入数字。这样可以有效延长下单请求的时长,更好的分散请求峰值。
  • 图形验证码可以篡改。可以用PostMan另外访问生成图形验证码的接口,这时Redis里的值就被篡改了,不再是页面上看到的计算结果了。如何处理?1、增加更多的判断因素,例如IP。2、前端签名,后端验证签名。
  • 输入了验证码之后,存在Redis中的验证码要及时删除。同时生成一个Token,代表当前用户有购买权限。这个Token有效期是非常短的。

针对验证机制的安全性,可以增加一些安全机制

换一种验证码
我们动手来换一种复杂一点的验证码,HappyCaptcha 官网地址: https://gitee.com/ramostear/Happy-Captcha

换的方式比较简单,首先在pom.xml中加入HappyCaptcha的依赖

<dependency>
	<groupId>com.ramostear</groupId>
	<artifactId>Happy-Captcha</artifactId>
	<version>1.0.1</version>
</dependency>

然后在OmsPortalOrderController中getVerifyCode方法,将生成验证码的部分修改一下:

try {
            //====== HappyCaptcha验证码 =======
            //这个步骤就会完成生成图片并且往response发送的步骤。
            HappyCaptcha.require(request, response).style(CaptchaStyle.ANIM)
                    .type(CaptchaType.ARITHMETIC_ZH)
                    .build().finish();
            Object captcha = request.getSession().getAttribute("happycaptcha"); //HappyCaptcha生成的验证码是String类型
            int code = Integer.parseInt(captcha.toString());
            log.info("验证码答案:{}", captcha);
            redisOpsUtil.set(RedisKeyPrefixConst.MIAOSHA_VERIFY_CODE_PREFIX
                            + memberId + ":" + productId
                    , code
                    , 300
                    , TimeUnit.SECONDS);
//====== HappyCaptcha验证码结束 =======
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            return CommonResult.failed("秒杀失败");
        }

这样,前台的验证码就变成了一闪一闪的动画。并且是中文的加减法,更难破解。
可以看到整个HappyCaptcha的实现机制跟我们自己的实现机制是差不多的,也是使用session来存储答案。

其他还有哪些更难以破解的验证码?

2.2 针对下单请求

我们的实现机制是要求将token拼凑到请求路径上来。这跟把token作为参数传递有什么区别?

如果一个模拟程序需要使用机器来参与秒杀抢单,首先需要根据其他用户的请求来分析获取下单路径。如果是同一个请求路径,只是带的参数不同,那机器完全可以尝试用暴力破解的方式来尝试进行下单。如果碰巧传入了一个Redis中的token值,那他就下单成功了。但是现在把参数隐藏到了请求路径当中,动态的请求路径对于下单的机器来说,就比较难试探出请求的地址,这样就增加了他下单的难度。

三 电商整体的秒杀限流方案

我们天天都在说三高,高并发、高可用、高可扩展,那到底应该如何去落地一个三高的设计方案?

构建大并发、高性能、高可用系统中几种通用的优化思路,可以抽象总结为“4 要 1不要”原则。也就是:数据要尽量少、请求数要尽量少、路径要尽量短、依赖要尽量少,以及不要有单点。当然,这几点是你要努力的方向,具体操作时还是要密切结合实际的场景和具体条件来进行。

针对秒杀这个场景,其实方案设计往往比技术细节更为重要。因为你可以想象,每一个秒杀环节的经典问题,都意味着互联网的秒杀业务出现过大的问题,这都是实打实买来的教训。发现了问题之后才会有针对性的方案设计。那现在,我们整体来回顾下电商的秒杀限流方案。

在这里插入图片描述
错峰1:动静分离的本质是将包含浏览者信息的动态数据和不包含浏览者信息的静态资源区分开。例如在商品单品页,商品信息是不包含浏览者信息的,这部分就可以抽象出静态资源。而用户登录状态、cookie等这些动态数据也尽可能缓存起来,并且使缓存能够离用户更近。

错峰2:秒杀答题的形式可以是多种多样的,目的是防止机器刷单,以及错开用户的下单时长。在秒杀场景下,答题速度靠后的请求自然就没有库存了,也可以减少系统的请求量。

错峰3:缓存的作用主要有两个,一是快速扣减库存,保护数据库流量,并且库存扣减完成后,快速通知Nginx,屏蔽后续请求;二是提前识别热点数据,并且针对热点数据提供优化处理。处理的方案主要是三个,一是优化,二是限制,三是隔离,包括业务隔离、系统隔离、数据隔离。

错峰4:单独提供秒杀服务集群,有利于减少秒杀商品的超大流量对普通商品的性能冲击,不要让1%的商品影响到另外的99%。

后台错峰:这一部分是我们的重点。我们这个图中每一个错峰点虽然在图上就是比较简单的一个点,但是深入进去,每个地方要考虑的细节都还是非常多的,大家可以回顾下之前的内容,体会下如何在后端对秒杀服务做针对性的优化。首先想到的是使用MQ进行削峰。但是实际上,后端需要考虑的三高问题也远不止MQ削峰这一步。每一个环节都需要考虑后端组件是否能够承载得住。例如秒杀服务集群,到底应该部署多大的集群?部署多少台机器呢?显然为了顶住秒杀的大流量,秒杀集群就需要部署得非常大。但是,如果在大部分没有秒杀服务的时间内,这个集群的资源就闲置得非常厉害。所以,虚拟化+云计算进行弹性部署也是非常重要的。在我后边博客里会给大家带来k8s和云部署的实战讲解。

然后:在后端系统中,添加了Redis、MQ这样的一些中间产品。而这些产品集群本身,也存在效率低下、服务崩溃的风险。这样也就给系统整体带来了更多的风险点。那要怎么去屏蔽这些产品给系统带来的风险呢?下一节博客会讲系统降级方面的设计;

TAGS
方案优先 > 技术优先。学习技术的同时,都要增加对软件问题的思考,很多同学技术学得很快,但是缺乏思考。秒杀这种超大并发场景下的限流问题,不是任何一个技术或者任何一个步骤可以限制住的,需要一个完整全面的方案才能保证业务稳定性。所以我们在开发过程中,不能只埋头于技术点,要站在更高的角度,整体来理解解决方案,这样才能更深入的理解自己在做的事情,也才能真正来解决问题。这才是高级程序员与普通程序员真正的区别。

例如针对前端验证问题,还有哪些优化方案?
提前发Token。可以在秒杀前设置一个预约活动。 在活动中提前发放token。例如一个秒杀活动有20W个商品,那就可以预先准备200W个token。用户进行预约时,只发放200W个Token,其他人也能预约成功,但是其实没有获得token,那后面的秒杀,直接通过这个token就可以过滤掉一大部分人。相当于没有token的人都只预约了个寂寞。这也是互联网常用的一个套路。

例如针对超卖问题,在之前的课程中,介绍了如何使用Redis分布式锁防超卖。针对同一个商品ID,使用一把分布式锁,确实可以很快很方便的处理超卖问题。但是如果同时进行秒杀的商品多了呢?像京东、淘宝一场大型的秒杀活动,同时有成千上万个商品要进行秒杀,那就意味着同一时间Redis上锁解锁的操作会要执行成千上万次,这对Redis的性能消耗是相当巨大的,Redis就有可能升级成为新的性能瓶颈。这时该怎么办?

当然具体问题的解决方案从来不止一个,这里我们可以选择一种返璞归真的方案,把秒杀超卖的问题从分布式降级到本地JVM中,来获取极限性能。例如将秒杀服务接入配置中心(Nacos),然后在秒杀服务开始前,由配置中心给每个应用服务实例下发一个库存数量。然后每次下单,每个服务器只管自己的库存数量,与其他应用服务器完全不进行库存同步,在各自的内存里扣减库存,这样就不会有超卖的情况发生。减少了网络消耗,性能也能够进一步提升。

这种方案可不可行呢?当然也会有一些问题需要去处理。有可能某给服务器上的库存很快消耗完了,而其他的服务器上仍有库存。整个服务就会表现为你抢不到商品,但是在你后面抢商品的人却能抢到商品。(你们 在参与秒杀时有没有过这样的经历?)但是这在秒杀这种场景下,完全是可以接受的。另外,如果某一个应用服务器挂了,那给他分配的库存就会丢失。这时候又要怎么办?其实也没必要再去设置什么复杂的逻辑,大不了少卖一点出去。反正都是售罄了,全卖完了,和卖了99%,其实没什么区别。这时只需要统计好订单的数量(可以通过MQ来统计,也可以通过Redis统计),等秒杀活动的30分钟等待支付期过去后,再将没卖出去的库存重新丢回库存池,与没有付款而被取消的订单商品一起返场售卖就可以了。这也是很多互联网公司目前采用的方案。

最后虽然我们是后台开发工程师,但是前端也必须要有所了解。今天我们关注的这个问题,也不能只关注后端,需要前后端一起才能理解他的作用。

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

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

相关文章

产品推荐 | 基于 Xilinx ZU19/ZU17/ZU11-Zyng Ultrascale+SOM 板卡

一、产品描述 iWare推出基于Xilinx FFVC1760封装的Zynq Ultrascale MPSoC系列SOM板卡&#xff0c;完美兼容ZU19/ZU17/ZU11 EG设备&#xff0c;具备卓越性能&#xff0c;最大内存带宽达64位&#xff0c;搭载8GB PS DDR4 RAM并支持ECC&#xff0c;满足高端应用需求。 二、产品参数…

MySQL主键冲突问题分析处理

目录 背景问题分析分析数据分析代码验证分析结果 原因分析验证MySQL参数解决办法修改MySQL配置参数修改代码 背景 因公司业务及预算调整&#xff0c;系统部署从原有云服务提供商迁移到另外一家云服务提供商&#xff0c;在测试新服务能力的时候&#xff0c;发现应用系统某个功能…

总结: HQL语句

总结: HQL语句 Part1 数据库的操作Part2 数据表的操作1. 创建普通表2. 内外部表3. 内外部表转换 Part1 数据库的操作 查看数据库: show databases; 创建数据库: create database if not exists 数据库名 使用数据库: use 数据库名; 查看数据库详细信息: desc database 数据库名…

通过Anaconda安装Python会得到的重要文件夹

E:\Anaconda\路径下 Scripts 文件夹&#xff1a;该文件夹包含了可执行的Python脚本文件&#xff0c;例如pip和conda等命令行工具。【pip3.exe和django-admin.exe等】Lib 文件夹&#xff1a;该文件夹包含了Python的标准库和其他第三方库的源代码文件。【Lib下面的site-packages…

JVM—内存可见性

什么是可见性 可见性&#xff1a;一个线程对共享变量值的修改,能够及时地被其他线程看到共享变量&#xff1a;如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量 Java内存模型(JMM) Java内存模型(Java Memory Model)描述了Java程序中各种…

【滑动窗口】长度最小的子数组|无重复字符的最长子串|最大连续1的个数 III|将 x 减到 0 的最小操作数

1. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 1.题目解析&#xff1a; 2.算法原理 &#xff08;1&#xff09;方法一&#xff1a;暴力列举出所有的子数组的和 时间复杂度&#xff1a;O&#xff08;n**2&#xff09;&#xff1a;枚举所有子数组O&#xff08;…

Linux信号处理

Linux信号处理 什么是linux信号 本质是一种通知机制&#xff0c;用户 or 操作系统通过发送一定的信号&#xff0c;通知进程&#xff0c;某些事情已经发生&#xff0c;你可以在后续进行处理。 信号产生是随机的&#xff0c;进程可能正在忙自己的事情&#xff0c;所以&#xf…

【LVGL-按钮按钮矩阵部件】

LVGL-按钮&按钮矩阵部件 ■ LVGL-按钮部件■ 按钮部件&#xff1a; 点击三个按钮一个回调函数修改label值。 ■ LVGL-按钮矩阵部件■ 示例一&#xff1a;按钮换行&#xff0c;和宽度设置。■ 示例二&#xff1a;设置按钮宽度为2倍■ 示例三&#xff1a;获取点击的按钮下标&…

Deep_Learning_readme

本文目录 1.环境安装2. A1-1实验&#xff1a;3. A1-2实验&#xff1a;4. A1-3实验 &#xff1a;**5. A2-CNN实验****6. A3-RNN实验** &#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;提示&#xff1a;全程路径和环境名字不…

Linux安装Nacos

安装前必要准备 准备Java环境 &#xff0c;8以上的版本&#xff0c;mysql&#xff08;集群相关信息&#xff09;&#xff0c;nginx&#xff08;进行代理&#xff09; 安装Nacos 首先我们要有一个nacos的包&#xff0c;我们可以在线下载&#xff0c;也可以提前下载好&#xf…

MySQL数据库事务介绍

前言 在MySQL数据库中&#xff0c;事务&#xff08;Transaction&#xff09;是指一组SQL语句的执行序列&#xff0c;这些SQL语句要么全部执行成功&#xff0c;要么全部执行失败&#xff0c;保证数据库的一致性和完整性&#xff1b;用于操作量大、复杂度高的数据。 目录 一、…

Ubuntu 22.04安装Python3.10.13

Ubuntu最好设置为英文&#xff0c;我之前用中文在make的test的时候&#xff0c;总是会有fail。 查了下有人怀疑是language的问题&#xff0c;保险起见都用英文&#xff0c;个人实践也证明改为英文就不报错了。 issue 44031: test_embed and test_tabnanny fails if the curre…

【史上最全面arduino esp32教程】ESP32Time时间库

文章目录 前言一、安装ESP32Time库二、ESP32Time使用2.1 基础使用构造ESP32Time对象设置当前时间获取当前时间结构体 2.2 其他函数 总结 前言 欢迎来到这篇Arduino ESP32教程&#xff01;在本教程中&#xff0c;我们将介绍ESP32Time时间库的使用。时间在许多项目中起着重要的作…

IPC网络摄像头媒体视屏流MI_VIF结构体

一个典型的IPC数据流 下图是一个典型的IPC数据流模型&#xff0c;流动过程如下&#xff1a; 1. 建立Vif->Vpe->Venc的绑定关系&#xff1b; 2. Sensor 将数据送入vif处理&#xff1b; 3. Vif 将处理后的数据写入Output Port申请的内存&#xff0c;送入下一级&#xff1b;…

docker 不同架构镜像融合问题解决

1、背景 docker 作为目前容器的标准之一&#xff0c;但是对于多种架构的平台的混合编译支撑不是很好。因此衍生了镜像融合&#xff0c;分别将多种不同的架构构建好&#xff0c;然后将镜像进行融合上传。拉取镜像的会根据当前系统的架构拉取不同的镜像&#xff0c;也可以通过 -…

Redis 不再“开源”,对中国的影响及应对方案

Redis 不再“开源”&#xff0c;使用双许可证 3 月 20 号&#xff0c;Redis 的 CEO Rowan Trollope 在官网上宣布了《Redis 采用双源许可证》的消息。他表示&#xff0c;今后 Redis 的所有新版本都将使用开源代码可用的许可证&#xff0c;不再使用 BSD 协议&#xff0c;而是采用…

深入理解HTTP协议本质与应用

教程介绍 HTTP是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。简单来说&#xff0c;如果你不懂HTTP协议&#xff0c;那就相当于是个只有“半桶水”的程序员。在这个课程中&#xff0c;带你一起花最少的时间&#xff0c;用最少的精力…

前端学习笔记 | Node.js

一、Node.js入门 1、什么是Node.js 定义&#xff1a;是跨平台JS运行环境&#xff08;可以独立执行JS的环境&#xff09;作用&#xff1a; 编写数据接口&#xff0c;提供网页资源功能等等前端工程化&#xff1a;为后续学Vue和React等框架做铺垫 2、Node.js为何能执行JS&#xff…

html第一次作业

常用标签 0, 骨架&#xff08;&#xff01;tap&#xff09; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><t…

鸿蒙预览报错 Only files in a module can be previewed

HarmonyOS第一课下载的源码无法运行&#xff0c;也无法预览&#xff0c;报错如题。 解决&#xff1a; 1、在预览页如“index.ets”文件下预览。 2、如果在通知栏看到如图提示&#xff0c;可看出是ohos/hvigor-ohos-plugin插件版本的问题&#xff0c;可点击蓝色解决方案同步并导…