秒殺項目用於處理高併發狀況,咱們採起發放令牌機制,根據用戶的token、商品id、活動商品id和一串uuid產生一個令牌存入redis中redis
同時引入了秒殺大閘,目的是流量控制,好比當前活動商品只有100件,咱們就發放500個令牌,秒殺前會先發放令牌,令牌發放完則把後來的用戶擋在這一層以外,控制了流量算法
獲取令牌後會對比redis中用戶產生的令牌,對比成功才能夠購買商品服務器
這樣一來就減輕了巨大訪問流量帶來的壓力併發
可是儘管如此,咱們能夠試想,若是一個網站作秒殺活動,有10個商品秒殺,每一個有1000件,那麼就要發放50000個令牌,這樣的數量仍然很大,因此咱們採起了隊列泄洪來解決此問題app
什麼是隊列化泄洪呢,就比如不少人同時進一個門,門很小,人不少,就能夠排成隊,假如一次能進20我的,就20我的一波,一波一波的進負載均衡
同理,隊列化泄洪就是把訪問的用戶分紅批次,來減輕同時巨大的訪問量形成的壓力,通常分爲令牌桶算法和漏桶算法,咱們通常用令牌桶算法,那麼怎麼實現呢?ide
隊列化泄洪——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);
}