一:背景

1. 讲故事

这个月初,有位朋友wx上找到我,说他的api过一段时间后,就会出现只有请求,没有响应的情况,截图如下:

从朋友的描述中看样子程序是被什么东西卡住了,这种卡死的问题解决起来相对简单,接下来我就用 windbg 给大家分析一下。

二:Windbg 分析

1. Request 请求正在干嘛?

既然朋友说 api 有 request 无 response,那怎么去验证朋友的话对不对呢?我们都知道 .NET 用 HttpContext 来表示一个请求,言外之意就是可以去抓 HttpContext 下的时长属性,Netext 中有一个 !whttp 命令可以帮助我们。

0:000> !whttp
HttpContext    Thread Time Out Running  Status Verb     Url
000000563bf803b0   42 00:01:50 00:01:24    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=x-HN
000000563bf84660   -- 00:01:50 Finished    200 GET      http://xxx.com:30003/
000000563c4a0470   51 00:01:50 00:00:12    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=xxx2C
00000056bbf63590   30 00:01:50 00:02:41    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=xxx-B2C
00000056bc82a038   -- 00:01:50 Finished    200 GET      http://localhost:30003/
00000056bc84a3e8   44 00:01:50 00:00:51    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=x
00000056bc8671c8   46 00:01:50 00:00:45    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=xxx-B2C
000000573bf44698   35 00:01:50 00:02:39    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=x
000000573bf483c0   33 00:01:50 00:02:41    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=x-HN
000000573bf97e80   40 00:01:50 00:02:32    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=ZJB2C
000000573c583b08   -- 00:01:50 Finished    200 GET      http://localhost:30003/
000000573c589ec8   -- 00:01:50 Finished    200 GET      http://xxx.com:30003/Wms/xxx/xxx/xxx
000000573c760e28   -- 00:01:50 Finished    200 POST     http://xxx.com:30003/Wms/xxx/xxx/xxx
000000573c95f990   48 00:01:50 00:00:31    200 POST     http://xxx.com:30003/Wms/Common/xxx?xxx=xxx&xxx=x-HN
00000057bbf4f8e8   31 00:01:50 00:02:12    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=x
00000057bc080340   50 00:01:50 00:00:19    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=x
000000583c4aee80   43 00:01:50 00:01:11    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=xxx2B
000000583c4d0c50   53 00:01:50 00:00:01    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=xxx2B
00000058bbf8f1a0   34 00:01:50 00:02:22    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=xxx2B
000000593bfe1758   41 00:01:50 00:01:22    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=xxx2C
000000593c892160   -- 00:01:50 Finished    200 GET      http://xxx.com:30003/Wms/xxx/xxx/xxxJob
000000593ca813b0   45 00:01:50 00:00:30    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=xxx-HN
000000593caa45d8   -- 00:01:50 Finished    200 GET      http://xxx.com:30003/
00000059bc1ad808   32 00:01:50 00:01:45    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=xxx-B2C
00000059bc1c3d70   36 00:01:50 00:01:29    200 POST     http://xxx.com:30003/Wms/xxx/xxx?xxx=xxx&xxx=x25 HttpContext object(s) found matching criteria

Running 列可以看到大多请求都已经达到1分钟以上,这也验证了朋友所说的卡死问题,按照经验,可以取 Running 列中最大的 httpContext 所在的线程,也就是上面的 3033 号线程, 看看它们都在干什么?

2. 探究 Running 最长线程

接下来切到 3033 号线程,看看它们的线程栈。

0:000> ~30s
ntdll!NtWaitForSingleObject+0xa:
00007ffd`b81f024a c3              ret
0:030> !clrstack
OS Thread Id: 0x29d0 (30)Child SP               IP Call Site
0000005acc3ac590 00007ffdb81f024a [PrestubMethodFrame: 0000005acc3ac590] xxx.xxx.RedisConnectionHelp.get_Instance()
0000005acc3ac850 00007ffd4dd78911 xxx.xxx.RedisCache..ctor(Int32, System.String)
0000005acc3ac8c0 00007ffd4dd78038 xxx.xxx.CacheByRedis.HashGet[[System.__Canon, mscorlib]](System.String, System.String, Int32)
0000005acc3ac968 00007ffdabef1f7c [StubHelperFrame: 0000005acc3ac968]
0000005acc3ac9c0 00007ffd4dd77f18 xxx.xxx.Cache.xxx.GetCacheNotAreaDataEntity[[System.__Canon, mscorlib]](System.String, System.String, System.String)...0:030> ~33s
ntdll!NtWaitForMultipleObjects+0xa:
00007ffd`b81f07ba c3              ret
0:033> !clrstack
OS Thread Id: 0x3ad4 (33)Child SP               IP Call Site
0000005accabae90 00007ffdb81f07ba [GCFrame: 0000005accabae90]
0000005accabafb8 00007ffdb81f07ba [HelperMethodFrame_1OBJ: 0000005accabafb8] System.Threading.Monitor.ObjWait(Boolean, Int32, System.Object)
0000005accabb0d0 00007ffdaac60d64 System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken)
0000005accabb160 00007ffdaac5b4bb System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken)
0000005accabb1d0 00007ffdab5a01d1 System.Threading.Tasks.Task.InternalWait(Int32, System.Threading.CancellationToken)
0000005accabb2a0 00007ffdab59cfa7 System.Threading.Tasks.Task`1[[System.__Canon, mscorlib]].GetResultxxx(Boolean)
0000005accabb2e0 00007ffd4d8d338f xxx.Config.xxx.Config`1[[System.__Canon, mscorlib]].GetConfig(xxx.Config.Model.ConfigListener, System.Func`2<xxx.Config.Request.GetConfigRequest,System.Threading.Tasks.Task`1<System.String>>)
0000005accabb340 00007ffd4d8d2f40 xxx.Config.xxx.Config`1[[System.__Canon, mscorlib]].get_Item(System.String, System.String)
0000005accabb3c0 00007ffd4dd78f7f xxx.Util.BaseConfig.get_GetRedisConn()
0000005accabb440 00007ffd4dd78e9c xxx.xxx.RedisConnectionHelp.GetConnectionString()
0000005accabb4a0 00007ffd4dd789cb xxx.xxx.RedisConnectionHelp..cctor()
0000005accabb940 00007ffdabef6953 [GCFrame: 0000005accabb940]
0000005accabc5b0 00007ffdabef6953 [PrestubMethodFrame: 0000005accabc5b0] xxx.xxx.RedisConnectionHelp.get_Instance()
0000005accabc870 00007ffd4dd78911 xxx.xxx.RedisCache..ctor(Int32, System.String)
0000005accabc8e0 00007ffd4dd78038 xxx.xxx.CacheByRedis.HashGet[[System.__Canon, mscorlib]](System.String, System.String, Int32)
0000005accabc988 00007ffdabef1f7c [StubHelperFrame: 0000005accabc988]
0000005accabc9e0 00007ffd4dd77f18 xxx.Core.Cache.xxx.GetCacheNotAreaDataEntity[[System.__Canon, mscorlib]](System.String, System.String, System.String)
...

上面的信息不难发现 30 号线程正卡在 RedisConnectionHelp.get_Instance() 处,33 号线已经进入了 RedisConnectionHelp.get_Instance() 方法中,最后在 GetConfig() 处等待 Result 的结果,按经验来说,30 号线程看样子正在锁等待, 33 号正在等待异步结果,接下来的突破点就是探究下 RedisConnectionHelp.Instance 处代码。

3. 寻找问题代码

接下来用反编译工具 ILSpy 找到问题代码。

public static class RedisConnectionHelp
{public static ConnectionMultiplexer Instance{get{if (_instance == null){lock (Locker){if (_instance == null || !_instance.IsConnected){_instance = GetManager();}}}return _instance;}}
}

30 号线程果然是卡在 Locker 处,接下来深挖下 33 号线程所执行的 GetManager() 方法,简化后代码如下:

public T this[string dataId, string key = ""]
{get{try{string config = GetConfig(configListener, new NacosConfigClient(Base.NacosConfiguration).DoGetConfigAsync);return JsonConvert.DeserializeObject<T>(config);}catch (Exception ex){return default(T);}}
}private string GetConfig(ConfigListener listener, Func<GetConfigRequest, Task<string>> action)
{var text2 = action(new GetConfigRequest{DataId = listener.DataId,Group = listener.Group,Tenant = text}).Result;return text2;
}internal async Task<string> DoGetConfigAsync(GetConfigRequest request)
{IRestResponse restResponse = await HttpUtil.Request(currentServerAddr, Method.GET, request.ParamValues(), xxx);return restResponse.Content;
}

可以看到代码卡在了 Result 上无限期等待,到这里我就想到了 同步上下文 ,我看他这个程序是 .NET 4.8 下的 ASP.NET MVC,记得不错上下文应该是 AspNetSynchronizationContext,具体死锁原因可参见我的这篇文章:一句 Task.Result 就死锁, 这代码还怎么写?,解决办法大概有四种。

  1. 使用 .ConfigureAwait(false)

  2. 改成全异步

  3. 用 Task 再包一层。

  4. 改成全同步

三:总结

其实本次事故主要还是因为在 同步代码 中做了 异步代码.Result 导致的死锁问题,有非常多的文章都在抨击这种现象,在 asp.net core 中已经移除了这种同步上下文的大坑,给到朋友的建议是改成全同步,死锁问题也随之消失。

哈哈,真替朋友开心!

和我一起来分析某药品仓储管理系统 卡死现象相关推荐

  1. 新能源汽车现状与问题分析——以比亚迪仓储管理系统为例

    新能源汽车现状与问题分析--以比亚迪仓储管理系统为例 引言:目前,世界各国都在大力发展新能源汽车,而新能源汽车在我国也被作为一项战略产业.发展新能源汽车,是我国迈向汽车强国的必由之路.我国发展新能源汽 ...

  2. 2022-2028年中国仓储管理系统行业市场深度分析及投资前景展望报告

    报告类型:产业研究 报告格式:电子版.纸介版 出品单位:智研咨询-产业信息网 仓储管理系统(WMS)是一个实时的计算机软件系统,它能够按照运作的业务规则和运算法则,对信息.资源.行为.存货和分销运作进 ...

  3. Node.js仓储管理系统的设计与实现 计算机毕设源码24296

    摘 要 本论文主要论述了如何使用Node.js语言开发一个仓储管理系统,系统采取Mysql作为后台数据的主要存储单元,运用软件工程原理和开发方法,采用node.js的koa技术构建,实现本系统的全部功 ...

  4. 中国wms仓储管理系统行业发展状况分析?

    一.仓储管理系统行业概况 在已经建立仓储管理信息系统的企业中,大多数建立仓储管理信息系统的企业都认为系统不能针对自身行业的特点进行有效的仓储管理,甚至还有很多的企业目前都没有建立仓储管理信息系统的计划 ...

  5. [附源码]java毕业设计药品销售管理系统

    项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclis ...

  6. 干货 | 深入仓储管理系统你需要了解的15件事

    PMCAFF(www.pmcaff.com):互联网产品社区,是百度,腾讯,阿里等产品经理的学习交流平台.定期出品深度产品观察,互联产品研究首选. 外包大师(www.waibaodashi.com): ...

  7. 企业实施WMS仓储管理系统需要规避哪些风险

    随着信息化技术的发展,各行业开始引入了WMS仓储管理系统来加强对仓库的信息化管理.它让我们从原来的人工搜索拣货到转变为现在的定位取货,不仅提高了工作效率,降低了错误率,而且还控制了成本,增强仓库管理. ...

  8. [原创]服装鞋帽企业配送中心优化方案 WMS仓储管理系统

    A 精品鞋业公司是 香港某 上市公司在中国设立的全资公司, 2004 年登陆中国市场,经过多年的精心布局, 业务范围遍及全国.在产品销售过程中,面对客户不断增长的服务需求, 该公司 除了在新产品的研发 ...

  9. WMS仓储管理系统在各种行业中,都有哪些作用

    由计算机控制的仓库管理系统的目的是独立实现仓储管理各种功能:收货.在正确的地点存货.存货管理.订单处理.分拣和配送控制.WMS仓储管理系统将关注的焦点集中于对仓储执行的优化和有效管理,同时延伸到运输配 ...

最新文章

  1. 【图论】用一道题从本质上讲清楚Floyd算法
  2. c语言对n个数选择排序_选择排序法 -- C语言
  3. Andy's First Dictionary
  4. SpringBootApplication注解
  5. springboot 获取客户端ip_JAVA如何获取客户端IP地址和MAC地址
  6. 10 个在线正则表达式测试网站。
  7. 收件服务器主机名未响应,邮箱收件服务器主机名是什么
  8. 性能计时器监测服务器性能瓶颈
  9. Android:图解四种启动模式 及 实际应用场景解说
  10. 多项logistic回归系数解释_逻辑回归logistic(含python代码)
  11. linux 最常用的指令- [readelf][objdump] 读取elf 文件系列
  12. Word中如何输入花体数学字符
  13. 华为 核心网产品线 是干嘛的
  14. Electron 初体验,用 js 搭建桌面应用程序
  15. 如何让暴风影音播放flv文件
  16. Android开源项目合集
  17. ubuntu 开启grub选择界面
  18. [生成对抗网络GAN入门指南](10)InfoGAN: Interpretable Representation Learning by Information Maximizing GAN
  19. HTML【表单和输入(按钮+登陆框)+框架】
  20. 【Memento模式】C++设计模式——备忘录模式

热门文章

  1. 35.使用拦截器实现权限验证
  2. Tiny模板语言(VelocityPlus)初步入门
  3. CSS3中弹性盒布局的最新版
  4. android视图工具,android studio的HierarchyViewer工具如何知道android屏幕的视图属性
  5. 在FC中如何获取fcdot文件
  6. Linux的学习思路
  7. canvas特效代码详解(2)
  8. Comparable、Iterator接口和Collections类的实现方法
  9. 十六、Struts2文件上传与下载
  10. [tools]notepad++当前文件路径不是工作路径