一、高并发场景下的核心挑战
当你的网站突然被大量用户访问时,服务器可能会像早高峰的地铁站一样拥挤不堪。在.NET Core中处理高并发请求,首先要理解几个关键问题:
- 线程池饥饿:默认线程池可能无法快速响应突发流量,导致请求排队。
- 数据库连接瓶颈:频繁的数据库操作会让连接池迅速耗尽。
- 资源锁竞争:比如缓存或文件读写时的锁冲突。
举个实际例子:假设你有一个电商秒杀接口,代码如下(技术栈:.NET Core 6 + Dapper):
// 错误示例:同步阻塞导致线程池饥饿
public IActionResult KillProduct(int productId)
{
var stock = _db.QueryFirst<int>("SELECT Stock FROM Products WHERE Id = @Id", new { Id = productId });
if (stock <= 0) return BadRequest("已售罄");
// 同步更新数据库(阻塞线程)
_db.Execute("UPDATE Products SET Stock = Stock - 1 WHERE Id = @Id", new { Id = productId });
return Ok("抢购成功");
}
注释:这段代码在并发请求下会出现超卖和线程阻塞,就像收银员一个一个处理顾客结账,队伍越排越长。
二、异步编程与性能优化
.NET Core的异步特性是解决高并发的利器。重构上面的代码:
// 正确示例:异步非阻塞处理
public async Task<IActionResult> KillProductAsync(int productId)
{
// 异步查询(释放线程)
var stock = await _db.QueryFirstAsync<int>(
"SELECT Stock FROM Products WHERE Id = @Id",
new { Id = productId });
if (stock <= 0) return BadRequest("已售罄");
// 异步更新(使用乐观并发控制)
var rows = await _db.ExecuteAsync(
"UPDATE Products SET Stock = Stock - 1 WHERE Id = @Id AND Stock > 0",
new { Id = productId });
return rows > 0 ? Ok("抢购成功") : BadRequest("库存不足");
}
注释:通过async/await避免线程阻塞,SQL语句添加Stock > 0条件防止超卖。
关联技术:
- 使用
ValueTask替代Task减少内存分配 - 用
IAsyncEnumerable处理流式数据
三、分布式缓存与队列削峰
当单机性能达到瓶颈时,可以引入Redis和消息队列。以下是结合Redis的库存扣减方案:
// 使用Redis + StackExchange.Redis
public async Task<bool> DeductStockAsync(string productId)
{
var db = _redis.GetDatabase();
// Lua脚本保证原子性
var script = @"local stock = tonumber(redis.call('GET', KEYS[1]))
if stock > 0 then
redis.call('DECR', KEYS[1])
return 1
end
return 0";
var result = (int)await db.ScriptEvaluateAsync(
script,
new RedisKey[] { $"product:{productId}:stock" });
return result == 1;
}
注释:Lua脚本在Redis中原子执行,避免竞态条件,就像把抢购请求变成排队叫号。
四、微服务与水平扩展
当流量持续增长时,需要考虑:
- 容器化部署:通过Docker+Kubernetes实现自动扩缩容
- 数据库分库分表:例如使用ShardingCore对SQL Server分片
- 负载均衡:Nginx轮询分发请求到多个服务实例
// 在Startup.cs中配置限流(使用AspNetCoreRateLimit)
services.AddRateLimiter(options => {
options.AddPolicy("api", context =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: context.User.Identity?.Name,
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
}));
});
注释:这相当于给每个用户发一张"一分钟最多100次请求"的通行证。
五、实战注意事项
- 监控必不可少:集成Prometheus+Grafana监控QPS和延迟
- 熔断机制:Polly库实现服务降级
- 连接复用:如HttpClientFactory管理HTTP连接
// 使用Polly实现熔断
services.AddHttpClient<ProductService>()
.AddTransientHttpErrorPolicy(policy =>
policy.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))))
.AddCircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromSeconds(30));
注释:当连续失败5次后,系统会"休息"30秒,避免雪崩效应。
总结
处理高并发就像指挥交通,需要:
- 异步化避免堵塞(像设置多车道)
- 缓存和队列分流(像设置缓冲带)
- 水平扩展增加容量(像增开收费站)
关键是要根据业务场景选择合适的技术组合,并始终通过压力测试验证方案。
评论