秒杀(使用内存数据避免并发问题)

server.js

var express = require("express");
var app = express();

var db_count = 50;  // mysql中的持久化数据

var remain_num = 0; // 内存数据

var sync_num_arr = [];

async function sync_data_to_mysql() {
    setTimeout(async function () {
        if (sync_num_arr.length > 0) {
            var final_num = sync_num_arr[sync_num_arr.length - 1]; // 以最后一次的数据为准,同步到数据库
            sync_num_arr = [];
            await set_remain_num(final_num);
        }
        await sync_data_to_mysql();

        console.log("----------剩余个数", db_count);
    }, 2000);
}

// 初始状态,从数据库获取剩余数目
async function get_remain_num_from_db() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(db_count);
        }, Math.random() * 1000)
    });
}

// 设置数据库中剩余数目
async function set_remain_num(num) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            db_count = num;
            resolve();
        }, Math.random() * 1000)
    });
}

//
async function random_sleep() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, Math.random() * 1000)
    });
}

async function init() {
    // 从数据库得到剩余数目存储为内存变量
    remain_num = await get_remain_num_from_db();

    app.get("/rob", async function (req, res) {
        if (remain_num > 0) {
            remain_num = remain_num - 1;
            sync_num_arr.push(remain_num);
            console.log("remain_num=", remain_num);
            res.send(req.query.name + " 抢到一个剩余 " + remain_num);
        } else {
            res.send("商品售罄,秒杀失败!!!");
        }
    });

    app.get("/remain", async function (req, res) {
        res.send("剩余数量=" + db_count);
    });

    app.listen(3000);

    // 定时同步到数据库
    await sync_data_to_mysql();
}

init();

client.js

var http = require("http");

function start_rob(user_name) {
    var url = "http://localhost:3000/rob?name=" + user_name;
    http.get(url, function (incoming_msg) {
        incoming_msg.on("data", function (data) {
            if (incoming_msg.statusCode == 200) {
                console.log(data.toString());
            } else {
                console.log("ERROR");
            }
        })
    });
}

// 模拟秒杀100次
var num = parseInt(Math.random() * 5000);
// num = 60;
console.log("模拟秒杀人数num", num);

async function random_sleep() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, Math.random() * 5000)
    });
}

async function main() {
    for (var i = 0; i < num; i++) {
        await random_sleep();
        console.log("<<<<开抢用户=guest_" + i);
        start_rob("guest_" + i);
    }
}

main();

思路

秒杀活动
服务器开启后,客户端模拟并发请求
同步策略为: 先读取数据库数据到内存中,然后当客户端请求秒杀到后,如果还有剩余,则修改剩余数量,加入同步列表
定时把同步列表最后一次的数据同步到mysql数据库
参考doc中的结果
1)服务器
node server.js
2)客户端
node client.js

猜你喜欢

转载自blog.csdn.net/themagickeyjianan/article/details/107380815