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.限流算法
- 令牌桶算法
算法思想是:
令牌以固定速率產生,並緩存到令牌桶中;
令牌桶放滿時,多余的令牌被丟棄;
請求要消耗等比例的令牌才能被處理;
令牌不夠時,請求被緩存。
- 漏桶算法
- 水(請求)從上方倒入水桶,從水桶下方流出(被處理);
來不及流出的水存在水桶中(緩沖),以固定速率流出;
水桶滿后水溢出(丟棄)。
這個算法的核心是:緩存請求、勻速處理、多余的請求直接丟棄。
相比漏桶算法,令牌桶算法不同之處在於它不但有一只“桶”,還有個隊列,這個桶是用來存放令牌的,隊列才是用來存放請求的。