当前位置: 首页 > news >正文

记一次 .NET某网络边缘计算系统 卡死分析

一:背景

1. 讲故事

早就听说过有什么 网络边缘计算,这次还真给遇到了,有点意思,问了下 chatgpt 这是干嘛的 ?

网络边缘计算是一种计算模型,它将计算能力和数据存储位置从传统的集中式数据中心向网络边缘的用户设备、传感器和其他物联网设备移动。这种模型的目的是在接近数据生成源头的地方提供更快速的计算和数据处理能力,从而减少数据传输延迟并提高服务质量。网络边缘计算使得在设备本地进行数据处理和决策成为可能,同时也有助于减轻对中心数据中心的网络流量和负载。

看到.NET还有这样的应用场景还是挺欣慰的,接下来就来分析下这个dump到底是怎么回事?

二:WinDbg 分析

1. 为什么会卡死

不同程序的卡死有不同的分析方式,所以要先鉴别下程序的类型以及主线程的调用栈即可,参考如下:


0:000> !eeversion
5.0.721.25508
5.0.721.25508 @Commit: 556582d964cc21b82a88d7154e915076f6f9008e
Server mode with 64 gc heaps
SOS Version: 8.0.10.10501 retail build0:000> k# Child-SP          RetAddr               Call Site
00 0000ffff`e0dddac0 0000fffd`c194c30c     libpthread_2_28!pthread_cond_wait+0x238
...
18 (Inline Function) --------`--------     libcoreclr!RunMain::$_0::operator()::{lambda(Param *)#1}::operator()+0x14c [/__w/1/s/src/coreclr/src/vm/assembly.cpp @ 1536] 
19 (Inline Function) --------`--------     libcoreclr!RunMain::$_0::operator()+0x188 [/__w/1/s/src/coreclr/src/vm/assembly.cpp @ 1538] 
1a 0000ffff`e0dde600 0000fffd`c153e860     libcoreclr!RunMain+0x298 [/__w/1/s/src/coreclr/src/vm/assembly.cpp @ 1538] 
...
20 0000ffff`e0dded10 0000fffd`c1bf7800     libhostpolicy!corehost_main+0xc0 [/root/runtime/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp @ 409] 
21 (Inline Function) --------`--------     libhostfxr!execute_app+0x2c0 [/root/runtime/src/installer/corehost/cli/fxr/fx_muxer.cpp @ 146] 
22 (Inline Function) --------`--------     libhostfxr!<unnamed-namespace>::read_config_and_execute+0x3b4 [/root/runtime/src/installer/corehost/cli/fxr/fx_muxer.cpp @ 520] 
23 0000ffff`e0ddeeb0 0000fffd`c1bf6840     libhostfxr!fx_muxer_t::handle_exec_host_command+0x57c [/root/runtime/src/installer/corehost/cli/fxr/fx_muxer.cpp @ 1001] 
24 0000ffff`e0ddf000 0000fffd`c1bf4090     libhostfxr!fx_muxer_t::execute+0x2ec
25 0000ffff`e0ddf130 0000aaad`c9e1d22c     libhostfxr!hostfxr_main_startupinfo+0xa0 [/root/runtime/src/installer/corehost/cli/fxr/hostfxr.cpp @ 50] 
26 0000ffff`e0ddf200 0000aaad`c9e1d468     dotnet!exe_start+0x36c [/root/runtime/src/installer/corehost/corehost.cpp @ 239] 
27 0000ffff`e0ddf370 0000fffd`c1c63fe0     dotnet!main+0x90 [/root/runtime/src/installer/corehost/corehost.cpp @ 302] 
28 0000ffff`e0ddf3b0 0000aaad`c9e13adc     libc_2_28!_libc_start_main+0xe0
29 0000ffff`e0ddf4e0 00000000`00000000     dotnet!start+0x34

从卦中的指标来看,这是一个 Linux 上部署的 Web网站,既然是网站的卡死,那就要关注各个线程都在做什么。

2. 线程都在干嘛

以我多年的分析经验,绝大多数都是由于 线程饥饿 或者说 线程池耗尽 导致的,首先我们看下线程池的情况。


0:000> !t
ThreadCount:      365
UnstartedThread:  0
BackgroundThread: 354
PendingThread:    0
DeadThread:       10
Hosted Runtime:   noLock  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception0    1    31eaf 0000AAADF267C600  2020020 Preemptive  0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn 
...423  363    36d30 0000FFDDB4000B20  1020220 Preemptive  0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn (Threadpool Worker) 424  364    36d31 0000FFDDA8000B20  1020220 Preemptive  0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn (Threadpool Worker) 425  365    36d32 0000FFDDAC000B20  1020220 Preemptive  0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn (Threadpool Worker) 0:000> !tp
Using the Portable thread pool.CPU utilization:  9%
Workers Total:    252
Workers Running:  236
Workers Idle:     13
Worker Min Limit: 64
Worker Max Limit: 32767Completion Total:   0
Completion Free:    0
Completion MaxFree: 128
Completion Current Limit: 0
Completion Min Limit:     64
Completion Max Limit:     1000

从卦中看当前有 365 个托管线程,这个算多吗?对于64core 来说,这个线程其实算是正常,训练营里的朋友都知道,server版的gc仅gc线程就有 64*2=128 个,接下来再看一个指标就是当前是否存在任务积压? 可以使用 !ext tpq 命令,参考输出如下:


0:000> !ext tpq
global work item queue________________________________local per thread work items_____________________________________

从卦中看当前没有任务积压,这就有点反经验了。

3. 真的不是线程饥饿吗

最后一招比较彻底,就是看各个线程栈都在做什么,可以使用 ~*e !clrstack 命令。

这不看不知道,一看吓一跳,有 193 个线程在 Task.Result 上等待,这玩意太经典了,然后从上面的调用栈 UIUpdateTimer_Elapsed 来看,貌似是一个定时器导致的,接下来我就好奇这代码是怎么写的?

分析上面的代码之后,我发现它是和 Linux Shell 窗口进行命令交互,不知道为何 Shell 没有响应导致代码在这里卡死。

4. 为什么线程池没有积压

相信有很多朋友对这个反经验的东西很好奇为什么请求没有积压在线程池,其实这个考验的是你对 PortableThreadPool 的底层了解,这里我就简单说一下吧。

  1. 在 ThreadPool 中有一个 GateThread 线程是专门给线程池动态注入线程的,参考代码如下:

private static class GateThread
{private static void GateThreadStart(){while (true){bool wasSignaledToWake = DelayEvent.WaitOne((int)delayHelper.GetNextDelay(tickCount));WorkerThread.MaybeAddWorkingWorker(threadPoolInstance);}}
}
  1. 一旦有人调用了 Task.Result 代码,内部会主动唤醒 DelayEvent 事件,告诉 GateThread 赶紧通过 MaybeAddWorkingWorker 方法给我注入新的线程,参考代码如下:

private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken)
{bool flag3 = ThreadPool.NotifyThreadBlocked();}
internal static bool NotifyThreadBlocked()
{if (UsePortableThreadPool){return PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked();}return false;
}
public bool NotifyThreadBlocked()
{GateThread.Wake(this);
}

上面这种主动唤醒的机制是 C# 版 PortableThreadPool 做的优化来缓解线程饥饿的,这里有一个重点就是它只能缓解,换句话说如果上游太猛了还是会有请求积压的,但为什么这里没有积压呢? 很显然上游不猛呗,那如何眼见为实呢? 这就需要看 timer 的周期数即可,到当前的线程栈上给扒出来。


0:417> !DumpObj /d 0000ffee380757f8
Name:        System.Timers.Timer
MethodTable: 0000fffd4ab24030
EEClass:     0000fffd4ad6e140
Size:        88(0x58) bytes
File:        /home/user/env/dotnet/shared/Microsoft.NETCore.App/5.0.7/System.ComponentModel.TypeConverter.dll
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
0000fffd4c947498  400001c        8 ...ponentModel.ISite  0 instance 0000000000000000 _site
0000000000000000  400001d       10 ....EventHandlerList  0 instance 0000000000000000 _events
0000fffd479195d8  400001b       98        System.Object  0   static 0000000000000000 s_eventDisposed
0000fffd47926f60  400000e       40        System.Double  1 instance 3000.000000 _interval
0000fffd4791fb10  400000f       48       System.Boolean  1 instance                1 _enabled
0000fffd4791fb10  4000010       49       System.Boolean  1 instance                0 _initializing
0000fffd4791fb10  4000011       4a       System.Boolean  1 instance                0 _delayedEnable
0000fffd4ab241d8  4000012       18 ...apsedEventHandler  0 instance 0000ffee3807aae8 _onIntervalElapsed
0000fffd4791fb10  4000013       4b       System.Boolean  1 instance                1 _autoReset
0000fffd4c944ea0  4000014       20 ...SynchronizeInvoke  0 instance 0000000000000000 _synchronizingObject
0000fffd4791fb10  4000015       4c       System.Boolean  1 instance                0 _disposed
0000fffd49963e28  4000016       28 ...m.Threading.Timer  0 instance 0000ffee38098dc8 _timer
0000fffd48b90a30  4000017       30 ...ing.TimerCallback  0 instance 0000ffee3807aaa8 _callback
0000fffd479195d8  4000018       38        System.Object  0 instance 0000ffee38098db0 _cookie

从卦中看当前是 3s 为一个周期,这就能解释为什么线程池没有积压的底层原因了。

三:总结

这个卡死事故还是蛮好解决的,如果有一些经验直接用dotnet-counter也是能搞定的,重点在于这是一个 Linux的dump,同时又是 .NET上的一个很好玩的场景,故此分享出来。

相关文章:

  • GPT提示词模板
  • 出现 ValueError: attempted relative import beyond top-level package 解决方法
  • 【DevOps】Java内存分配与JVM参数详解
  • Selenium 扩展工具中哪些可以查看返回的各种格式的文件
  • 3D slicer
  • Lumion专业3D渲染三维场景设计软件下载,Lumion强大的三维渲染软件
  • 目标检测中的mAP计算:深入解析与实践指南
  • openwrt 23.05.2 稳定版本 导入树莓派4B
  • 科比老大职业生涯数据预测(基于随机森林模型)
  • 7_1_SVPWM概述
  • 如何在Ubuntu 14.04上安装和配置Postfix作为仅发送的SMTP服务器
  • 【你真的了解double和float吗】
  • 目标检测中损失函数的精妙作用:精确度与鲁棒性的双重保障
  • 九浅一深Jemalloc5.3.0 -- ⑨浅*gc
  • 如何在Laravel中实现数据验证:确保数据准确性的最佳实践
  • 345-反转字符串中的元音字母
  • input的行数自动增减
  • iOS 颜色设置看我就够了
  • JAVA之继承和多态
  • js ES6 求数组的交集,并集,还有差集
  • Laravel5.4 Queues队列学习
  • Service Worker
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • 二维平面内的碰撞检测【一】
  • 关于字符编码你应该知道的事情
  • 前端技术周刊 2019-01-14:客户端存储
  • 原生 js 实现移动端 Touch 滑动反弹
  • !$boo在php中什么意思,php前戏
  • (1)bark-ml
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (Java)【深基9.例1】选举学生会
  • (LeetCode) T14. Longest Common Prefix
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (void) (_x == _y)的作用
  • (八十八)VFL语言初步 - 实现布局
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (三)c52学习之旅-点亮LED灯
  • (图)IntelliTrace Tools 跟踪云端程序
  • (转)VC++中ondraw在什么时候调用的
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • *setTimeout实现text输入在用户停顿时才调用事件!*
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET IoC 容器(三)Autofac
  • .net mvc actionresult 返回字符串_.NET架构师知识普及
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • @hook扩展分析
  • [android] 看博客学习hashCode()和equals()
  • [AutoSar]BSW_Memory_Stack_004 创建一个简单NV block并调试
  • [BZOJ4010]菜肴制作
  • [C#7] 1.Tuples(元组)
  • [HNCTF 2022 WEEK2]easy_include 文件包含遇上nginx