电商的秒杀和抢购,对程序员来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验。当一个Web系统,在一秒钟内收到数以万计甚至更多请求时,系统的优化和稳定至关重要。
我们直接将请求放入队列Queue中的,采用FIFO(FirstInput First Output,先进先出),这样的话,我们就不会导致某些请求永远获取不到锁。这里有点强行将多线程变成单线程的感觉。
秒杀看似简单,但是可能会存在两个问题:高并发和超卖
高并发:比较火秒杀活动同时参与秒杀人数都是10w+的,如此之高的秒杀人数对于网站架构从前到后都是一种考验。
超卖:秒杀商品都会有固定的数量,如何避免成功下订单买到商品的人数不超过商品数量的上限,这是每个抢购活动都要面临的一大难题。
特别提醒,在处理的过程中多线程异步处理的时候,Queue不是线程安全的,这个时候需要在关键逻辑是lock锁的进行处理,避免并发的超卖问题;
执行函数main
var queueTrain = new QueueTestModule().InitQueue(); //抢到票的队列
var queueAll = new QueueTestModule().InitQueue(); //所有进入队列的用户队列
var tt = new QueueTestModule();
//一个并行线程进行执行抢票操作
Parallel.For(0, 150, (t, state) =>
{
Thread.Sleep(100);
Train train = new Train();
train.Name = "用户" + t;
var x = tt.Buy(queueAll, queueTrain, train);
if (x.Code == -1)
{
Console.WriteLine(x.Name + ":" + x.Msg);
//state.Break();
}
else
Console.WriteLine(x.Name + ":" + x.Msg + "还剩下:" + x.Cnt + "件");
});
//可以进行消化队列
while (queueTrain.Count != 0)
{
Console.WriteLine("当前队列剩余个数:" + queueTrain.Count);
queueTrain.Dequeue();
//Console.WriteLine("处理用户订单中:" + queueTrain.Dequeue());
}
Console.ReadKey();
//封装一个抢票方法
public class BuyTrainService
{
public static int cnt = 100;
public Queue InitQueue()
{
var tt = new Queue();
return tt;
}
object obj = new object();
public RetData Buy(Queue AllQueue, Queue tainQueue, Train tain)
{
try
{
AllQueue.Enqueue(tain);
if (AllQueue.Count > cnt)
{
return new RetData { Name = tain.Name, Code = -1, Msg = "商品抢光了", Cnt = 0 };
}
tainQueue.Enqueue(tain);
return new RetData { Name = tain.Name, Code = 1, Msg = "恭喜已抢到", Cnt = cnt - tainQueue.Count };
}
catch (Exception ex)
{
Console.WriteLine("异常:" + ex);
return new RetData { Name = tain.Name, Code = 1, Msg = "异常:" + ex, Cnt = cnt - tainQueue.Count };
}
}
public class RetData
{
public string Name { get; set; }
public int Code { get; set; }
public string Msg { get; set; }
public int Cnt { get; set; }
}
public class Train
{
public string Name { get; set; }
}
}