记一次 .NET某旅行社旅店管理系统 卡死分析

打印 上一主题 下一主题

主题 1441|帖子 1441|积分 4323

一:背景

1. 讲故事

年初有位朋友找到我,说他们的管理系统不相应了,让我帮助看下到底咋回事? 手上也有dump,那就来分析吧。
二:为什么没有相应

1. 线程池队列有积压吗?

朋友的系统是一个web系统,那web系统的无相应我们起首要关注的就是 线程池,利用 !sos tpq 命令,参考输出如下:
  1. 0:000> !sos tpq
  2. global work item queue________________________________
  3. 0x00000004010774C0 Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT<Microsoft.AspNetCore.Hosting.HostingApplication+Context>
  4. 0x0000000401077808 Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT<Microsoft.AspNetCore.Hosting.HostingApplication+Context>
  5. ....
  6. 0x000000030239DD78 Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT<Microsoft.AspNetCore.Hosting.HostingApplication+Context>
  7. 0x000000030239E0C0 Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT<Microsoft.AspNetCore.Hosting.HostingApplication+Context>
  8. local per thread work items_____________________________________
  9. 0x0000000100A46410 System.Threading.Tasks.Task<System.Threading.Tasks.Task>
  10. ...
  11. 0x000000010133F8C0 System.Threading.Tasks.Task<System.Threading.Tasks.Task>
  12.     2 Work  Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext+<>c.<WaitOnTasks>b__123_1
  13.     4 Work  Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionContext+<>c.<WaitOnTasks>b__123_0
  14.   266 Work  Microsoft.AspNetCore.SignalR.HubConnectionContext.AbortConnection
  15. ----
  16.   272
复制代码
从卦中可以看到确实存在线程池积压的情况,那为什么会有积压呢?条件反射告诉我,是不是因为锁的原因,利用 !syncblk 观察。
  1. 0:000> !syncblk
  2. Index         SyncBlock MonitorHeld Recursion Owning Thread Info          SyncBlock Owner
  3. -----------------------------
  4. Total           468
  5. CCW             0
  6. RCW             0
  7. ComClassFactory 0
  8. Free            120
复制代码
从卦中看和锁没半毛钱关系,那就只能深入各个消费线程,看看这些线程为什么这么不给力。。。。
2. 线程都在干什么

要想观察各个线程都在做什么,可以用 ~*e !clrstack 观察各个线程调用栈,输出的调用栈太长,但仔细观察之后,发现很多线程都停顿在 TryGetConnnection 上,截图如下:

从卦中可以看到大概有143个线程卡在 TryGetConnection 上,不知道 Connection 为啥取不到了,接下来观察问题代码,简化后如下:
  1.         private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObjectsTimeout, bool allowCreate, bool onlyOneCheckConnection, DbConnectionOptions userOptions, out DbConnectionInternal connection)
  2.         {
  3.             DbConnectionInternal dbConnectionInternal = null;
  4.             Transaction transaction = null;
  5.             if (HasTransactionAffinity)
  6.             {
  7.                 dbConnectionInternal = GetFromTransactedPool(out transaction);
  8.             }
  9.             if (dbConnectionInternal == null)
  10.             {
  11.                 Interlocked.Increment(ref _waitCount);
  12.                 do
  13.                 {
  14.                     num = WaitHandle.WaitAny(_waitHandles.GetHandles(allowCreate), (int)waitForMultipleObjectsTimeout);
  15.                 } while (dbConnectionInternal == null);
  16.             }
  17.         }
  18.         private DbConnectionInternal GetFromTransactedPool(out Transaction transaction)
  19.         {
  20.             transaction = ADP.GetCurrentTransaction();
  21.             DbConnectionInternal dbConnectionInternal = null;
  22.             if (null != transaction && _transactedConnectionPool != null)
  23.             {
  24.                 dbConnectionInternal = _transactedConnectionPool.GetTransactedObject(transaction);
  25.                 //....
  26.             }
  27.             return dbConnectionInternal;
  28.         }
  29.       
  30.        internal DbConnectionInternal GetTransactedObject(Transaction transaction)
  31.         {
  32.             lock (_transactedCxns)
  33.             {
  34.                 flag = _transactedCxns.TryGetValue(transaction, out value);
  35.             }
  36.             if (flag)
  37.             {
  38.                 lock (value)
  39.                 {
  40.                     int num = value.Count - 1;
  41.                     if (0 <= num)
  42.                     {
  43.                         dbConnectionInternal = value[num];
  44.                         value.RemoveAt(num);
  45.                     }
  46.                 }
  47.             }
  48.             return dbConnectionInternal;
  49.         }
复制代码
从上面的卦中数据可知三点信息:

  • 100 _totalObjects 当前的线程池存着100个Connection。
  • 0 _count 当前100个Connection全部耗尽。
  • 143 _waitCount 表示当前有 143 个线程在获取 Connection 上进行等待。
3. 池中之物都去了哪里

要想找到这个答案,继续观察线程栈,比如搜刮TDS传输层方法 Microsoft.Data.SqlClient.TdsParserStateObject.TryReadByte ,可以看到刚好是 100 个,上层大多是 xxxx.GetRoomNosInDltParams 方法,截图如下:

挖掘各个线程栈,大概都是下面的sql,格式化如下:
[code]SELECT [HotelId],        [RoomId],        [SubRoomId],        [SupplierId],        [RoomNo],        [RoomCategory],        [SellPrice],        [RoomNoState],        [CheckInDate]FROM [xxx]WHERE ((((((([RoomId] IN (673,674)) AND( [SellPrice] > @SellPrice1 ))        AND ( [CheckInDate] >= @CheckInDate2 ))        AND ( [CheckInDate]
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小小小幸运

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表