.net 高并发服务性能瓶颈排查处理

背景

我用.net8 开发的数采和实时数据库产品在一个实际项目中遇到了性能瓶颈,表现为性能监视下网络发送流量出现断流和尖峰的情况,两个进程之间的通讯在断流时会有几秒钟的阻塞,我开始以为是网络的问题,实际是短时大量的Task阻塞了线程池,使得新的请求被迫排队等待,再就是内存开销大造成GC压力较大。

应用场景介绍

我的数采服务要从EMQX订阅数据后写入到实时数据库,数据量大约17000点/秒,每个点约30个字节,也就是510Kb数据,实际流量看数据格式,从EMQX过来的JSON格式,流量要大好几倍。

实时数据库要对外提供实时监视和历史曲线,访问频率为90次/秒,实时调用的响应很快,历史数据读取要慢一些,下面是10分钟的调用统计(Excel数据透视表)。

使用dotnet-counters+deepseek 诊断问题

可以下载一个离线dotnet-counters放到服务器的C:\Windows\System32下,然后在dos窗口中执行如下命令,注意process-id换一下

复制代码
dotnet-counters collect --process-id 12345 --format csv --output counter.csv

由于我的问题每分钟出现一两次,我抓了约1分钟数据后,直接丢给deepseek(网页)帮我分析

优化一:减少内存压力

进程收发数据时往往要new 一些集合对象,通讯结束后这些对象就不用了,使用Microsoft.Extensions.ObjectPool 在需要集合对象时Get,使用完Return可以降低这部分内存的频繁分配,结合应用场景介绍里的实时调用有多频繁,你就知道内存分配下降多明显了。

再就是,服务内部实现时,尽量避免把集合放在方法的返回值,而是作为参数,这样外部代码才能使用ObjectPool来降低集合内存分配。更进一步,还可以不适用集合,而是传入一个Action,每次迭代时调用这个Action。

复制代码
//不好的做法
public List<T> Method<T> Read(DateTime start, DateTime end)
{
    var result = new List<T>();
    //...
    return result;
}

//较好做法
public void Method<T> Read(List<T> result, DateTime start, DateTime end)
{
    //...
}

//推荐做法
public void Method<T> Read(Action<T> action, DateTime start, DateTime end)
{
    //...
}

优化二:减少大对象分配(LOH)

单个对象超过 85,000 字节会进入 LOH,LOH 不自动压缩(除非显式压缩或 Full GC),大对象分配失败会触发更昂贵的 Full GC。我在采集到数据后,会通过gRPC批量发送给Tagdb,之前设置的批上限是10000条,约300Kb,超过了85Kb,达到批量上限时发送和接收都会使用LOH,把这个改成2000条了。

优化三:并发使用Parallel.ForEach 而不是Task\[\]

在进行了前2个优化问题还在,deepseek提示线程池排队严重,"在 11:01:49 出现 ThreadPool Queue Length = 101,表明瞬时请求超过处理能力",我根据它的建议,把代码中所有Task\[\]都改成了Parallel.ForEach,options参数传入 new ParallelOptions{MaxDegreeOfParallelism = Environment.ProcessorCount},这样可以限制并发的Task数量。

我看了日志,对于脚本计算,一批数据有可能触发100个以上,之前的逻辑会一次创建这么多Task,然后WaitAll,这会使线程池排队。

优化四:自适应内存缓存

通过日志发现,某些特定功能需要定时读取超过1小时的历史数据,而我的实时数据库只在内存缓存最近1小时的历史数据,这导致较大的IO开销和CPU压力(要解压缩),我重新设计了缓存机制,根据读取来动态分配缓存,超过有效期后清除缓存,定时清理缓存中的旧数据,这样可以适应多变的用户读取行为,使服务的性能越来越好,也降低了默认1小时缓存所有点的内存占用。

效果总结

通过1周的分析和优化,网络流量规律多了,内存占用也降低了不少。

相关推荐
夜之眷属13 小时前
JVM实战:服务器堆外内存去哪了(NMT实测)
java·服务器·jvm·后端·性能优化
小羊没烦恼!15 小时前
Windows Azure Platform体验(1):Windows Azure
java·大数据·后端·python·flask·word·.net
打工仔折腾 AI1 天前
Docker镜像分层与卷挂载到底怎么工作:一次文件系统层面的实测分析
运维·人工智能·后端·python·docker·容器·性能优化
Cx330❀1 天前
Qt 多线程深度解析:从底层原理到 UI 线程与同步实战
开发语言·qt·ui·搜索引擎·性能优化·图形渲染
cyf312 天前
RDMA 写操作带宽性能建模与参数敏感性分析
性能优化·rdma·rocev2
夜之眷属2 天前
记一次战斗服务器 CPU 打满 100% 且“无法恢复“的排查
java·linux·运维·服务器·后端·性能优化
CSharp精选营2 天前
IIS 后面挂 Kestrel,这 3 个配置错了就 502
kestrel·.net·iis配置
鑫 源2 天前
C#源码开源, 获取macOS 电池数据,包括iOS (xamarin开发)
c#·.net·xamarin
余槐i2 天前
WAL 下写并发仍是 1:SQLite 单写者模型实测与绕开方案
数据库·python·性能优化·sqlite·wal
晚安日记wanna2 天前
MySQL 回表为什么这么慢?5 种优化手段逐个拆解
数据库·mysql·性能优化