搶購秒殺的業務邏輯 redis實現 nginx限流演算法
阿新 • • 發佈:2021-11-06
1.秒殺的業務邏輯
-
秒殺會出現高併發的情況,關係型資料庫併發能力較弱,高併發會導致資料庫崩潰
-
使用非關係型資料庫
-
在閘道器這一塊,使用nginx進行負載均衡,保證訪問可以被消化,在應用伺服器端使用tomcat叢集,唯一有問題的是在資料庫這一端
搶購秒殺(限時特惠)
高併發
限時
限量
實現搶購秒殺 1:服務端(閘道器、應用伺服器)要進行負載均衡
2: 儘可能的少用或者不用關係型資料庫
3:儘量的提高流程的體驗(頁面靜態化等)
實現思路:基於redis來實現搶購秒殺(搶購秒殺的資料提前存到redis中)
- redis中的資料格式
- seckill:seq:active string型別 分期物件。
- seckill:items:list:+分期id string型別 list
對 - seckill:items:count:+商品id list型別 {1,2,3,4.。。。。100}
- 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.限流演算法
- 令牌桶演算法
演算法思想是:
令牌以固定速率產生,並快取到令牌桶中;
令牌桶放滿時,多餘的令牌被丟棄;
請求要消耗等比例的令牌才能被處理;
令牌不夠時,請求被快取。
- 漏桶演算法
- 水(請求)從上方倒入水桶,從水桶下方流出(被處理);
來不及流出的水存在水桶中(緩衝),以固定速率流出;
水桶滿後水溢位(丟棄)。
這個演算法的核心是:快取請求、勻速處理、多餘的請求直接丟棄。
相比漏桶演算法,令牌桶演算法不同之處在於它不但有一隻“桶”,還有個佇列,這個桶是用來存放令牌的,佇列才是用來存放請求的。