抢购秒杀的业务逻辑 redis实现 nginx限流算法
1.秒杀的业务逻辑
-
秒杀会出现高并发的情况,关系型数据库并发能力较弱,高并发会导致数据库崩溃
-
使用非关系型数据库
-
在网关这一块,使用nginx进行负载均衡,保证访问可以被消化,在应用服务器端使用tomcat集群,唯一有问题的是在数据库这一端
抢购秒杀(限时特惠)
高并发
限时
限量
实现抢购秒杀 1:服务端(网关、应用服务器)要进行负载均衡
2: 尽可能的少用或者不用关系型数据库
3:尽量的提高流程的体验(页面静态化等)
实现思路:基于redis来实现抢购秒杀(抢购秒杀的数据提前存到redis中)
- redis中的数据格式
- seckill:seq:active string类型 分期对象。
- seckill:items:list:+分期id string类型 list
对 - seckill:items:count:+商品id list类型
- seckill:items:users:+商品id set集合
2.定时存放数据
SysProperties 存放系统当中的静态常量
package com.oracle.shop.constant;
//存放程序的公共常量
public class SysProperties {
public static final String SECKILL = "seckill:seq:active";
public static final String SECKILL_ITEMS_LIST = "seckill:items:list:";
public static final String SECKILL_ITEM_COUNT = "seckill:items:count:";
public static final String SECKILL_ITEMS_USERS="seckill:items:users:";
}
spring-task.xml 定时任务的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">
<!-- 开启spring task的注解支持-->
<task:annotation-driven></task:annotation-driven>
</beans>
SeckillRedisDataInit 编写定时器,定时将数据放入redis当中
//定时任务将 秒杀商品信息初始化
@Component
@Lazy(false)
public class SeckillRedisDataInit {
@Autowired
private SeckillSeqService seckillSeqService;
@Autowired
private SeckillItemsService seckillItemsService;
@Autowired
private RedisTemplate redisTemplate;
//每天定时刷新,更新分期秒杀 列表
@Scheduled(cron = "1 1 1 * * ?")
public void init(){
//查询活跃的分期对象
SeckillSeq seckillSeq = seckillSeqService.findActiveSeckillSeq();
//放入redis中
String key = SysProperties.SECKILL;
redisTemplate.boundValueOps(key).set(seckillSeq);
//查询该期秒杀活动的商品
List<SeckillItems>seckillItems= seckillItemsService.findBySeqId(seckillSeq.getId());
//将商品信息放入redis中
key=SysProperties.SECKILL_ITEMS_LIST+seckillSeq.getId();
redisTemplate.boundValueOps(key).set(seckillItems);
//将每个商品的数量做成list放入redis中
for(SeckillItems seckillItems1:seckillItems){
key= SysProperties.SECKILL_ITEM_COUNT +seckillItems1.getId();
for (int i=1;i<=seckillItems1.getSeckillCount();i++){
redisTemplate.boundListOps(key).leftPush(i);
}
}
}
}
控制器中 抢购秒杀的逻辑实现
//处理用户的 购买请求
@RequestMapping("/kill")
public @ResponseBody
ResponseVo seckill(@Param("id") int id, HttpSession session){
ResponseVo responseVo = new ResponseVo();
//1.是否在秒杀期限内
SeckillSeq seckillSeq =(SeckillSeq) redisTemplate.boundValueOps(SysProperties.SECKILL).get();
Date now=new Date();
if (now.before(seckillSeq.getStartDate())){
//活动未开始
responseVo.setCode("201");
}else if (now.after(seckillSeq.getEndDate())){
//活动以及结束
responseVo.setCode("202");
}else {
User user = (User)session.getAttribute("user");
if (redisTemplate.boundSetOps(SysProperties.SECKILL_ITEMS_USERS+id).isMember(user.getId())){
//用户是否已经抢购过商品
responseVo.setCode("203");
}else {
Object obj = null;
if ((obj=redisTemplate.boundListOps(SysProperties.SECKILL_ITEM_COUNT+id).rightPop())!=null){
//抢购成功,将该用户放入 相应商品购买者的set集合当中
redisTemplate.boundSetOps(SysProperties.SECKILL_ITEMS_USERS+id).add(user.getId());
//抢购成功
responseVo.setCode("200");
}else {
//商品买完了
responseVo.setCode("204");
}
}
}
return responseVo;
}
3.限流算法
- 令牌桶算法
算法思想是:
令牌以固定速率产生,并缓存到令牌桶中;
令牌桶放满时,多余的令牌被丢弃;
请求要消耗等比例的令牌才能被处理;
令牌不够时,请求被缓存。
- 漏桶算法
- 水(请求)从上方倒入水桶,从水桶下方流出(被处理);
来不及流出的水存在水桶中(缓冲),以固定速率流出;
水桶满后水溢出(丢弃)。
这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃。
相比漏桶算法,令牌桶算法不同之处在于它不但有一只“桶”,还有个队列,这个桶是用来存放令牌的,队列才是用来存放请求的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了