1. 程式人生 > 其它 >搶購秒殺的業務邏輯 redis實現 nginx限流演算法

搶購秒殺的業務邏輯 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型別 {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.限流演算法

  • 令牌桶演算法

演算法思想是:

​ 令牌以固定速率產生,並快取到令牌桶中;
​ 令牌桶放滿時,多餘的令牌被丟棄;
​ 請求要消耗等比例的令牌才能被處理;
​ 令牌不夠時,請求被快取。

  • 漏桶演算法
  • 水(請求)從上方倒入水桶,從水桶下方流出(被處理);
    來不及流出的水存在水桶中(緩衝),以固定速率流出;
    水桶滿後水溢位(丟棄)。
    這個演算法的核心是:快取請求、勻速處理、多餘的請求直接丟棄。
    相比漏桶演算法,令牌桶演算法不同之處在於它不但有一隻“桶”,還有個佇列,這個桶是用來存放令牌的,佇列才是用來存放請求的。