秒殺項目之——通過令牌發放機制,控制秒殺大閘以及隊列泄洪


秒殺項目用於處理高並發情況,我們采取發放令牌機制,根據用戶的token、商品id、活動商品id和一串uuid產生一個令牌存入redis中

同時引入了秒殺大閘,目的是流量控制,比如當前活動商品只有100件,我們就發放500個令牌,秒殺前會先發放令牌,令牌發放完則把后來的用戶擋在這一層之外,控制了流量

獲取令牌后會對比redis中用戶產生的令牌,對比成功才可以購買商品

 

首先,獲取活動商品詳情時就要根據庫存設置大閘數量,我們設置大閘為庫存的5倍

 

 

在發放令牌時就要根據大閘數量判斷要不要發放

 

 

 

這樣一來就減輕了巨大訪問流量帶來的壓力

 

但是盡管如此,我們可以試想,如果一個網站做秒殺活動,有10個商品秒殺,每個有1000件,那么就要發放50000個令牌,這樣的數量仍然很大,所以我們采取了隊列泄洪來解決此問題

 

什么是隊列化泄洪呢,就好比很多人同時進一個門,門很小,人很多,就可以排成隊,假如一次能進20個人,就20個人一波,一波一波的進

同理,隊列化泄洪就是把訪問的用戶分成批次,來減輕同時巨大的訪問量造成的壓力,一般分為令牌桶算法和漏桶算法,我們一般用令牌桶算法,那么怎么實現呢?

隊列化泄洪——ExecutorService, 通過這個可以從隊列中每次獲取一定數據量的請求進行處理,處理的速度取決於下游的窗口的處理速度。

 

@Autowired

private ExecutorService executorService;

@PostConstruct

    public void init(){

        executorService = Executors.newFixedThreadPool(20);

 

}

   //封裝下單請求

    @RequestMapping(value = "/createorder",method = {RequestMethod.POST},consumes={CONTENT_TYPE_FORMED})

    @ResponseBody

    public CommonReturnType createOrder(@RequestParam(name="itemId")Integer itemId,

                                        @RequestParam(name="amount")Integer amount,

                                        @RequestParam(name="promoId",required = false)Integer promoId,

                                        @RequestParam(name="promoToken",required = false)String promoToken) throws BusinessException {

     -------------省略驗證登錄、令牌等信息

        //同步調用線程池的submit方法

        //擁塞窗口為20的等待隊列,用來隊列化泄洪

        Future<Object> future = executorService.submit(new Callable<Object>() {

            @Override

            public Object call() throws Exception {

                //加入庫存流水init狀態

                String stockLogId = itemService.initStockLog(itemId,amount);

                //再去完成對應的下單事務型消息機制

                if(!mqProducer.transactionAsyncReduceStock(userModel.getId(),itemId,promoId,amount,stockLogId)){

                    throw new BusinessException(EmBusinessError.UNKNOWN_ERROR,"下單失敗");

                }

                return null;

            }

        });

 

        try {

            future.get();

        } catch (InterruptedException e) {

            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);

        } catch (ExecutionException e) {

            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);

        }

        return CommonReturnType.create(null);

    }

 

 

為了防止服務器承受不確定的洪峰流量,可以采取限流的策略--  單機限流,負載均衡還不錯的情況下,效果還是不錯的,每次訪問一個接口,先判斷當前的訪問量是否達到設定值,如果達到了直接回絕。-- Guava Limiter,   設定的訪問值根據我們壓測的結果設定的--300qps    

集群限流:依賴redis或者其他的中間件技術做統一的計數器,往往會產生性能瓶頸

單機限流:負載均衡的前提下,單機限流的效果要好

 我們使用guava limiter 也就是單機限流,限制單個接口的流量

 

private RateLimiter orderCreateRateLimiter;

 

    @PostConstruct

    public void init(){

        executorService = Executors.newFixedThreadPool(20);

 

        orderCreateRateLimiter = RateLimiter.create(300);

 

}

 

 

在下單之前校驗

if(!orderCreateRateLimiter.tryAcquire()){

            throw new BusinessException(EmBusinessError.RATELIMIT);

 }

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM