10. Spike spike flood relief strategy

Regarding the peak shaving technique, common scenes such as spikes are used. Why should traffic peak? The reason is called spike, which is the problem of influx of traffic during the first second. The increase in instantaneous flow may affect the machine. Therefore, we need to smooth the excessive flow of the first second, weaken the peak, and smooth the transition By the second or later, the system performance will be improved smoothly.

For the moment when we haven't done any operation, the interface that spikes the order will be constantly brushed by the script. The spike verification logic is strongly associated with the spike single interface, and the code redundancy is high. The spike verification logic is complex and generates unrelated loads on the trading system.

Therefore, we introduced the principle of spike token:

The spike interface requires a token to enter. The spike token is generated by the spike module.

The spike activity module has full authority for generating spike tokens and logically closes.

Before placing an order in seconds, the user needs to obtain the token first in order to spike.

Let's take a look at it:

Write all the user verification to an interface:

//生成秒杀令牌
    @RequestMapping(value = "/generatetoken",method = {RequestMethod.POST},consumes={CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType generatetoken(@RequestParam(name="itemId")Integer itemId,
                                        @RequestParam(name="promoId")Integer promoId) throws BusinessException {
        //根据token获取用户信息
        String token = httpServletRequest.getParameterMap().get("token")[0];
        if (StringUtils.isEmpty(token)){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能下单");
        }
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
        if(userModel == null){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能下单");
        }
        //获取秒杀访问令牌
        String promoToken = promoService.generateSecondKillToken(promoId , itemId , userModel.getId());

        if (promoToken == null){
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR , "生成令牌失败");
        }
        return CommonReturnType.create(promoToken);
    }

Further judgment, by issuing a token 

    @Override
    public String generateSecondKillToken(Integer promoId , Integer itemId , Integer userId) {
        //获取对应商品的秒杀活动信息
        PromoDO promoDO = promoDOMapper.selectByPrimaryKey(promoId);
        //dataobject->model
        PromoModel promoModel = convertFromDataObject(promoDO);
        if(promoModel == null){
            return null;
        }
        //判断当前时间是否秒杀活动即将开始或正在进行
        if(promoModel.getStartDate().isAfterNow() || promoModel.getEndDate().isBeforeNow()){
            return null;
        }
        //判断item信息
        ItemModel itemModel =itemService.getItemByIdInCache(itemId);
        if(itemModel == null){
            return null;
        }
        //判断用户信息
        UserModel userModel = userService.getUserByIdInCache(userId);
        if(userModel == null){
            return null;
        }
        String token = UUID.randomUUID().toString().replace("-" , "");
        redisTemplate.opsForValue().set("promo_token_" + promoId + "_user_" + userId + "_item_" + itemId , token);
        redisTemplate.expire("promo_token_" + promoId + "_user_" + userId + "_item_" + itemId , 5 , TimeUnit.MINUTES);
        return token;
    }

In this way, the front-end carries the token in the order request, and the back-end ordering interface can verify whether the token is legal, thus separating the verification and the order. But now there is still a problem that as long as the user requests, the token will be issued, so we next need to limit, that is, spike the gate:

The principle of spike killing:

Relying on the authorization principle of the spike token to customize the licensing logic (control token issuance), then we can achieve the function similar to the gate.

We can issue a corresponding number of tokens based on the initial inventory of spike products to control the gate flow.

The user's risk control strategy is advanced to the issuance of spike tokens.

The judgment of sold out inventory is brought forward to the issuance of spike tokens.

There is no coding here, the implementation is very simple, that is, when it is released, the number of allowed tokens is placed in redis, and it is -1 every time the token is generated.

Of course, this is still flawed. If there are 10w spiked products, it is still unable to cope with the influx of system surge traffic, and this method is only for single inventory, and the token restriction ability for multiple inventory and multiple products is weak. .

Next, solve these problems, that is, the queue flooding function.

The principle of queue flooding is a queuing strategy, because queuing is sometimes more efficient than concurrency. Rely on queuing to limit concurrent traffic, and rely on queuing and the extent of downstream blocking windows to adjust the size of queue release traffic.

    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 {
        String token = httpServletRequest.getParameterMap().get("token")[0];
        if (StringUtils.isEmpty(token)){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能下单");
        }
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
        if(userModel == null){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能下单");
        }
        //校验秒杀是否正确
        if (promoId != null){
            String redisPromoToken = (String) redisTemplate.opsForValue().get("promo_token_" + promoId + "_user_" + userModel.getId() + "_item_" + itemId);
            if (redisPromoToken == null || !StringUtils.equals(redisPromoToken , promoToken)){
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR , "秒杀令牌校验失败");
            }

        }
        //同步调用线程池的submit方法
        //用塞窗口为20的等待队列,用来队列化泄洪
        Future<Object> future = executorService.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                //加入库存流水init状态
                String stockLogId = itemService.initStockLog(itemId , amount);
                //再去完成对应的下单事务型消息机制
                boolean result = mqProducer.transactionAsyncReduceStock(userModel.getId() ,  promoId ,  itemId ,  amount , stockLogId);
                if (!result){
                    throw new BusinessException(EmBusinessError.UNKNOWN_ERROR , "下单失败");
                }
                return null;
            }
        });
        try {
            future.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR , "下单失败");
        } catch (ExecutionException e) {
            e.printStackTrace();
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR , "下单失败");
        }
        return CommonReturnType.create(null);
    }

In fact, the implementation is very simple. Create a thread pool, place the order in the thread, and block to wait for the return, and then return to the front end.

The above is the local flood discharge strategy. Of course, we also have a distributed strategy. The queue is set to external redis and managed by the distributed queue. I'll talk about this later.

Published 97 original articles · won 28 · 10,000+ views

Guess you like

Origin blog.csdn.net/haozi_rou/article/details/105450457