从CRUD到高并发架构:用C#实现秒杀系统的终极方案

开发 架构
引入监控系统,如Prometheus和Grafana,实时监控系统的各项指标,包括CPU使用率、内存占用、数据库连接数等。在系统中添加详细的日志记录,记录关键操作和异常信息,便于在出现问题时进行排查和分析。

在当今的互联网应用开发中,构建高并发系统是一项极具挑战性的任务。秒杀系统作为典型的高并发场景,对系统的性能、稳定性和可靠性提出了极高的要求。本文将带领大家从基础的CRUD操作开始,逐步深入到高并发架构的设计与实现,最终打造一个基于C#的高性能秒杀系统。

一、理解秒杀系统的业务需求 

1.1 业务场景

秒杀系统通常应用于电商平台、票务系统等场景,在特定时间点,大量用户同时抢购有限数量的商品或服务。例如,电商平台的限时抢购活动,用户在规定的几分钟内抢购特价商品;票务系统中热门演出或赛事门票的瞬间开售等。

1.2 核心需求

  • 高并发处理能力:能够应对瞬间涌入的大量请求,确保系统不崩溃、不卡顿。
  • 数据一致性:保证商品库存数量的准确性,避免超卖现象的发生。
  • 快速响应:用户操作能够得到及时反馈,提升用户体验。
  • 安全可靠:防止恶意攻击,如机器人刷单等行为。

二、技术选型与准备 

2.1 后端框架

我们选择ASP.NET Core作为后端开发框架。ASP.NET Core具有高性能、跨平台、依赖注入等特性,非常适合构建高并发的Web应用。通过NuGet包管理器,安装以下核心包:

  • Microsoft.AspNetCore.App:ASP.NET Core应用的基础包。
  • Microsoft.EntityFrameworkCore.SqlServer:用于连接和操作SQL Server数据库。

2.2 数据库

SQL Server作为关系型数据库,具备强大的数据管理和事务处理能力,适合存储商品信息、订单数据等。在appsettings.json文件中配置数据库连接字符串:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=YOUR_SERVER_NAME;Database=YOUR_DATABASE_NAME;User ID=YOUR_USERNAME;Password=YOUR_PASSWORD"
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

2.3 缓存

Redis作为内存缓存,具有极高的读写速度,能够有效减轻数据库压力。安装StackExchange.Redis包来操作Redis。在Startup.cs中配置Redis连接:

using StackExchange.Redis;

public void ConfigureServices(IServiceCollection services)
{
    var redis = ConnectionMultiplexer.Connect("YOUR_REDIS_SERVER:6379");
    services.AddSingleton<IConnectionMultiplexer>(redis);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

三、从CRUD到高并发架构的演进 

3.1 基础CRUD操作

首先,创建商品和订单的实体类,使用Entity Framework Core进行数据库的CRUD操作。

  • 商品实体类:
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Stock { get; set; }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 订单实体类:
public class Order
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public string UserId { get; set; }
    public DateTime OrderTime { get; set; }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 数据访问层(DAL)示例:
public class ProductRepository
{
    private readonly YourDbContext _context;

    public ProductRepository(YourDbContext context)
    {
        _context = context;
    }

    public async Task<Product> GetProductById(int id)
    {
        return await _context.Products.FindAsync(id);
    }

    public async Task UpdateProductStock(int id, int newStock)
    {
        var product = await _context.Products.FindAsync(id);
        product.Stock = newStock;
        await _context.SaveChangesAsync();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

3.2 引入缓存优化

在高并发场景下,频繁访问数据库会导致性能瓶颈。通过引入Redis缓存,将热门商品信息缓存起来,减少数据库查询次数。

  • 缓存获取商品信息示例:
public async Task<Product> GetProductByIdFromCache(int id)
{
    var redis = _connectionMultiplexer.GetDatabase();
    var productJson = await redis.StringGetAsync($"product:{id}");
    if (!string.IsNullOrEmpty(productJson))
    {
        return JsonConvert.DeserializeObject<Product>(productJson);
    }
    var product = await _productRepository.GetProductById(id);
    if (product != null)
    {
        await redis.StringSetAsync($"product:{id}", JsonConvert.SerializeObject(product));
    }
    return product;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

3.3 高并发架构设计

为了应对高并发,我们采用以下架构设计:

  • 负载均衡:使用Nginx作为负载均衡器,将请求均匀分配到多个后端服务器实例上,避免单点服务器过载。
  • 消息队列:引入RabbitMQ作为消息队列,将订单创建等操作异步化。当用户下单时,先将订单信息发送到消息队列,由专门的消费者服务从队列中取出订单信息进行处理,这样可以削峰填谷,减轻数据库的压力。
  • 分布式锁:为了保证商品库存的一致性,使用Redis分布式锁。在进行库存扣减操作前,先获取分布式锁,确保同一时间只有一个线程能够操作库存。
public async Task<bool> TryAcquireLockAsync(string lockKey, string requestId, TimeSpan expirationTime)
{
    var redis = _connectionMultiplexer.GetDatabase();
    return await redis.StringSetAsync(lockKey, requestId, expirationTime, When.NotExists);
}

public async Task ReleaseLockAsync(string lockKey, string requestId)
{
    var redis = _connectionMultiplexer.GetDatabase();
    var script = @"
        if redis.call('GET', KEYS[1]) == ARGV[1] then
            return redis.call('DEL', KEYS[1])
        else
            return 0
        end
    ";
    await redis.ScriptEvaluateAsync(
        LuaScript.Prepare(script),
        new RedisKey[] { lockKey },
        new RedisValue[] { requestId });
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

四、实现秒杀系统的核心逻辑 

4.1 秒杀接口设计

创建一个API接口来处理秒杀请求。在接口中,先从缓存中获取商品信息,检查库存是否充足,然后尝试获取分布式锁进行库存扣减操作,最后将订单信息发送到消息队列。

[ApiController]
[Route("[controller]")]
public class SeckillController : ControllerBase
{
    private readonly IProductService _productService;
    private readonly IOrderService _orderService;
    private readonly IDistributedLockService _lockService;

    public SeckillController(
        IProductService productService,
        IOrderService orderService,
        IDistributedLockService lockService)
    {
        _productService = productService;
        _orderService = orderService;
        _lockService = lockService;
    }

    [HttpPost]
    public async Task<IActionResult> Seckill([FromBody] SeckillRequest request)
    {
        var product = await _productService.GetProductByIdFromCache(request.ProductId);
        if (product == null || product.Stock <= 0)
        {
            return BadRequest("商品已售罄");
        }

        var lockKey = $"seckill:{request.ProductId}";
        var requestId = Guid.NewGuid().ToString();
        if (!await _lockService.TryAcquireLockAsync(lockKey, requestId, TimeSpan.FromSeconds(10)))
        {
            return BadRequest("系统繁忙,请稍后重试");
        }

        try
        {
            var success = await _productService.DecreaseStock(product.Id);
            if (success)
            {
                var order = new Order
                {
                    ProductId = product.Id,
                    UserId = request.UserId,
                    OrderTime = DateTime.Now
                };
                await _orderService.AddOrderToQueue(order);
                return Ok("秒杀成功");
            }
            else
            {
                return BadRequest("商品已售罄");
            }
        }
        finally
        {
            await _lockService.ReleaseLockAsync(lockKey, requestId);
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.

4.2 订单处理与库存管理

订单处理服务从消息队列中消费订单信息,将订单数据持久化到数据库。库存管理服务负责在接收到库存扣减请求时,确保库存数量的准确性。

  • 订单处理服务示例:
public class OrderService : IOrderService
{
    private readonly IModel _rabbitMqModel;
    private readonly YourDbContext _context;

    public OrderService(IModel rabbitMqModel, YourDbContext context)
    {
        _rabbitMqModel = rabbitMqModel;
        _context = context;
    }

    public async Task AddOrderToQueue(Order order)
    {
        var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(order));
        _rabbitMqModel.BasicPublish(
            exchange: "",
            routingKey: "order_queue",
            basicProperties: null,
            body: body);
    }

    public async Task ProcessOrders()
    {
        var consumer = new EventingBasicConsumer(_rabbitMqModel);
        consumer.Received += async (model, ea) =>
        {
            var body = ea.Body.ToArray();
            var orderJson = Encoding.UTF8.GetString(body);
            var order = JsonConvert.DeserializeObject<Order>(orderJson);
            _context.Orders.Add(order);
            await _context.SaveChangesAsync();
            _rabbitMqModel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
        };
        _rabbitMqModel.BasicConsume(
            queue: "order_queue",
            autoAck: false,
            consumer: consumer);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 库存管理服务示例:
public class ProductService : IProductService
{
    private readonly ProductRepository _productRepository;
    private readonly IConnectionMultiplexer _connectionMultiplexer;

    public ProductService(
        ProductRepository productRepository,
        IConnectionMultiplexer connectionMultiplexer)
    {
        _productRepository = productRepository;
        _connectionMultiplexer = connectionMultiplexer;
    }

    public async Task<bool> DecreaseStock(int productId)
    {
        var product = await _productRepository.GetProductById(productId);
        if (product == null || product.Stock <= 0)
        {
            return false;
        }
        product.Stock--;
        await _productRepository.UpdateProductStock(productId, product.Stock);
        var redis = _connectionMultiplexer.GetDatabase();
        await redis.StringSetAsync($"product:{productId}", JsonConvert.SerializeObject(product));
        return true;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

五、系统测试与优化 

5.1 性能测试

使用工具如JMeter对秒杀系统进行性能测试,模拟大量并发用户请求,观察系统的响应时间、吞吐量等指标。根据测试结果,调整系统参数,如缓存过期时间、消息队列的并发消费者数量等。

5.2 安全优化

防止恶意攻击是秒杀系统的重要环节。通过设置验证码、限制单个IP的请求频率等方式,阻止机器人刷单行为。同时,对用户输入进行严格的参数校验,防止SQL注入等安全漏洞。

5.3 监控与日志

引入监控系统,如Prometheus和Grafana,实时监控系统的各项指标,包括CPU使用率、内存占用、数据库连接数等。在系统中添加详细的日志记录,记录关键操作和异常信息,便于在出现问题时进行排查和分析。

通过以上步骤,我们成功地从基础的CRUD操作构建出了一个具备高并发处理能力的秒杀系统。在实际应用中,还需要根据业务需求和系统运行情况不断进行优化和完善,以确保系统的稳定性和可靠性。希望本文能为你在构建高并发系统的道路上提供有价值的参考。

责任编辑:武晓燕 来源: 程序员编程日记
相关推荐

2024-07-03 11:01:55

2020-10-14 07:20:53

高并发

2024-08-01 11:38:40

2018-09-15 04:59:01

2025-02-20 00:01:00

2025-03-03 07:00:00

C#分布式缓存高并发

2021-08-26 08:24:33

高并发秒杀系统

2025-03-27 00:14:10

2022-08-26 10:24:48

架构Golang

2021-06-23 06:48:42

秒杀Java电商

2025-03-10 06:00:00

2020-04-13 08:33:39

高并发秒杀系统

2009-06-16 14:43:23

大型网站系统架构

2019-02-15 10:11:23

2019-10-30 16:54:08

golangredis数据库

2010-11-08 10:20:18

2022-03-18 09:11:56

高并发抢购系统架构

2024-11-25 09:10:03

点赞
收藏

51CTO技术栈公众号