秒杀系统并发应对的设计思路小结

秒杀系统跟抽奖不同,抽奖是随机中奖,秒杀需要在启动时的瞬间,
把所有物料,顺序分配给进入系统的用户,直到分配完毕为止,
用户量大的时候,对系统的冲击也是非常大的,经常出现系统失去响应,崩溃等,
甚至于系统挂了,用户却中不到奖品(奖品超发是程序bug,本文暂不讨论)。

说明:
- 本文主要针对移动h5网站 或 pc端web网站,对手机app而言,其实思路类似
- 文中所有限制数据,需要根据业务实际情况配置,比如10秒,每秒3次之类

设计思路:
1、针对99%的正常用户,在前端进行限制,大致需要实现如下2个逻辑:
1.1、用户点击按钮后,按钮置灰,不再触发点击事件,直到响应返回才启用,
避免用户拼命点击鼠标,代码参考:

<button onclick="doKill(this);">秒杀</button>

function doKill(obj){
    $(obj).attr('disabled', 'disabled');
    if(window.sending){
        return;
    }
    window.sending = true;
    $.ajax({
        type: 'GET',
        url: 'https://app.baidu.com/api?secondKill',
        cache: true, 
        dataType: 'json',
        success: function (response) {
            $(obj).removeAttr('disabled');
            window.sending = false;
            if (response.code !== 200) {
                alert('error' + response.msg);
                return;
            }
            alert(response.result);
        }
    });
}

1.2、cookie限制每用户第二次点击必须10秒以后,才能再次发送请求到后端,所以用户看似在拼命点击,实际上10秒内只发一次有效请求,示例代码:

var cookiename = 'last_kill_time';
var lastSendTime = getCookie(cookiename);
var currentTime = Date.parse(new Date());
var diff = currentTime - lastSendTime;
if(diff < 10000){
    return;
}
// 发送请求到服务器的代码,然后再设置cookie
setCookie(cookiename, currentTime);

2、第1步限制了绝大多数的正常用户冲击,但是无法应对有经验的程序员,so,要服务端在IP和用户层面进行限制:
2.1、每IP,每秒只允许3次提交,超过3次,直接返回失败;
2.2、每个用户ID,每秒只允许3次提交,超过3次,直接返回失败;
注1:这2个步骤,可以在内存计数进行限制,也可以通过redis计数限制(存在多服务器分布式部署时)。
注2:需要考虑公司IP,一个IP多用户共用情况,另外无法限制有多ip代理和手上储存了很多用户账号的恶意用户,而且会出现同一时间,对不同用户响应不一致的情况,比如剩余奖品数有差异等等。

3、请求队列,单线程处理所有用户请求
比如有100个奖品,却有1万个正常用户来抢,把全部用户请求透传到DB显然有问题。
通常做法:
3.1、每放过一个请求,redis加1 ,超过总奖品数,直接返回失败;
这一步,要有失败补偿失败,比如有些用户抢到不要了,要给redis计数重置。
3.2、使用消息队列,正常请求全部加入队列,队列达到一定长度,就停止入队,返回失败,队列处理完毕,又允许入队,如此反复。

基本上就是如上3步了,在运维层面也可以做不少事情,比如多服务器负载均衡、前端网关层防冲击等等。

猜你喜欢

转载自blog.csdn.net/youbl/article/details/78240528