阿裡簡訊回執.net sdk的bug導致生產服務cpu 100%排查

来源:https://www.cnblogs.com/huangxincheng/archive/2020/05/26/12966717.html
-Advertisement-
Play Games

一:背景 1. 講故事 去年阿裡聚石塔上的所有isv簡訊通道全部對接阿裡通信,我們就做了對接改造,使用阿裡提供的.net sdk。 網址:https://help.aliyun.com/document_detail/114480.html 同事當時使用的是ons-.net v1.1.3版本,程式上 ...


一:背景

1. 講故事

去年阿裡聚石塔上的所有isv簡訊通道全部對接阿裡通信,我們就做了對接改造,使用阿裡提供的.net sdk

網址:https://help.aliyun.com/document_detail/114480.html

同事當時使用的是ons-.net v1.1.3版本,程式上線後若幹天就會有一次程式崩潰現象,當時也沒特別在意,以為是自己代碼或者環境出了什麼問題,索性就加了一個檢測程式,如果檢測到sdk程式退出就自動重啟,就這樣先糊弄著,直到有一天伺服器告警,那個程式CPU居然飆到100%,伺服器可是16核128G的哦。。。

二:分析問題

1. 抓dump文件

情況比較緊急,馬上給程式發送Ctrl+C命令讓程式退出,結果又退出不了,奇葩。。。為了分析問題抓了一個dump下來,然後強制kill掉程式。

2. 查看線程池以及各個線程正在做什麼?


0:000> !tp
CPU utilization: 100%
Worker Thread: Total: 0 Running: 0 Idle: 0 MaxLimit: 32767 MinLimit: 16
Work Request in Queue: 0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 1 Free: 1 MaxFree: 32 CurrentLimit: 1 MaxLimit: 1000 MinLimit: 16

CPU utilization: 100% 上看,果然cpu100%了,發現 Worker Thread 沒有Running 線程,可能是因為執行了Ctrl+C都銷毀了,接下來用 ~*e !clrstack 把所有的托管線程棧打出來。


0:000> ~*e !clrstack
OS Thread Id: 0x1818 (0)
Unable to walk the managed stack. The current thread is likely not a 
managed thread. You can run !threads to get a list of managed threads in
the process
Failed to start stack walk: 80070057

從輸出結果看,沒有任何托管線程,唯一的那個線程0還不是還托管線程,然後改成 ~*e !dumpstack把非托管線程棧找出來。


0:000> ~*e !dumpstack
OS Thread Id: 0x1818 (0)
Current frame: ntdll!ZwRemoveIoCompletion+0x14
Child-SP         RetAddr          Caller, Callee
000000637323ef40 00007ff8327bac2f KERNELBASE!GetQueuedCompletionStatus+0x3f, calling ntdll!ZwRemoveIoCompletion
000000637323efa0 00007ff81b9c8a00 ONSClient4CPP!metaq_juce::URL::launchInDefaultBrowser+0x273d0, calling kernel32!GetQueuedCompletionStatus
000000637323f090 00007ff81ba3eb0a ONSClient4CPP!ons::Message::getMsgBody+0x5a8a, calling ONSClient4CPP!metaq_juce::URL::launchInDefaultBrowser+0x1f100
000000637323f140 00007ff81ba3f084 ONSClient4CPP!ons::Message::getMsgBody+0x6004, calling ONSClient4CPP!ons::Message::getMsgBody+0x5800
000000637323f280 00007ff81ba233b4 ONSClient4CPP!ons::ONSFactoryProperty::setSendMsgTimeout+0xa6b4, calling ONSClient4CPP!ons::ONSFactoryProperty::setSendMsgTimeout+0xa5d0
000000637323f2b0 00007ff81ba11b43 ONSClient4CPP!ons::ONSFactoryAPI::~ONSFactoryAPI+0x153
000000637323f310 00007ff81ba12d64 ONSClient4CPP!ons::SendResultONS::operator=+0xc44, calling ONSClient4CPP!ons::ONSFactoryAPI::~ONSFactoryAPI+0x10
000000637323f460 00007ff81ba83eb4 ONSClient4CPP!ons::Message::getStoreTimestamp+0xf484, calling ONSClient4CPP!ons::Message::getStoreTimestamp+0xf1c4
000000637323f630 00007ff8356f7d94 ntdll!RtlExitUserProcess+0xb4, calling ntdll!LdrShutdownProcess
000000637323f690 00007ff832777c23 KERNELBASE!CtrlRoutine+0xa3
000000637323f780 00007ff834df8364 kernel32!BaseThreadInitThunk+0x14, calling kernel32!WriteConsoleOutputW+0x530

從非托管調用棧來看,其中KERNELBASE!CtrlRoutine 表明主線程接受到了Ctrl+C命令, 從棧頂發現貌似不能退出的原因是主線程被 ONSClient4CPP 接管,而且這個C++正在做遠程連接再等待網路IO返回,但這種會把16核cpu打滿應該不太可能,這個問題貌似到這裡就卡住了。

三: 重啟程式發現問題依舊

1. 抓dump文件

很開心的是程式重新啟動後,過了兩分鐘CPU又在飆升,這次學乖了,等CPU到了60,70%的時候抓dump文件。

2. 繼續排查


0:000> .time
Debug session time: Fri Apr 17 17:36:50.000 2020 (UTC + 8:00)
System Uptime: 355 days 5:33:48.092
Process Uptime: 0 days 0:02:11.000
  Kernel time: 0 days 0:03:31.000
  User time: 0 days 0:13:22.000


0:000> !tp
CPU utilization: 59%
Worker Thread: Total: 3 Running: 0 Idle: 3 MaxLimit: 32767 MinLimit: 16
Work Request in Queue: 0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 2 Free: 2 MaxFree: 32 CurrentLimit: 2 MaxLimit: 1000 MinLimit: 16

從上面代碼可以看到,進程啟動了2分11秒,這次cpu利用率是59%,抓的有點早,不過沒關係,先看一下Threads情況。


0:000> !threads
ThreadCount:      25
UnstartedThread:  0
BackgroundThread: 8
PendingThread:    0
DeadThread:       16
Hosted Runtime:   no
                                                                                                        Lock  
       ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1  cdc 0000022bb9f53220    2a020 Preemptive  0000022BBBFACCE8:0000022BBBFADFD0 0000022bb9f27dc0 1     MTA 
   2    2  3dc 0000022bb9f7f9f0    2b220 Preemptive  0000000000000000:0000000000000000 0000022bb9f27dc0 0     MTA (Finalizer) 
   3    4 296c 0000022bb9fe97b0  102a220 Preemptive  0000000000000000:0000000000000000 0000022bb9f27dc0 0     MTA (Threadpool Worker) 
XXXX    5    0 0000022bb9ffc5a0  1039820 Preemptive  0000000000000000:0000000000000000 0000022bb9f27dc0 0     Ukn (Threadpool Worker) 
XXXX    6    0 0000022bd43938c0  1039820 Preemptive  0000000000000000:0000000000000000 0000022bb9f27dc0 0     Ukn (Threadpool Worker) 
.............................................................................
 163   24 29e8 0000022bd4898650  1029220 Preemptive  0000022BBC102108:0000022BBC103FD0 0000022bb9f27dc0 0     MTA (Threadpool Worker) 
 164   25 2984 0000022bd489d470  1029220 Preemptive  0000022BBC0EA2D0:0000022BBC0EBFD0 0000022bb9f27dc0 0     MTA (Threadpool Worker) 

好家伙,才2分11秒,托管線程ThreadCount: 25就死了DeadThread: 16個,而且從threads列表中看,windbg給的最大編號是164,說明當前有 (164+1) - 25 =142 個非托管線程,應該就是阿裡的ONSClient4CPP開啟的,為什麼開啟這麼多線程,這就是一個很值得關註的問題了,接下來還是用 ~*e !dumpstack 把所有線程的托管和非托管線程棧打出來,由於信息太多,我就截幾張圖。


個人猜測,純技術討論:


圖1:

從堆棧上看,有105個線程卡在 ntdll!ZwRemoveIoCompletion+0x14 這裡,而且從 ONSClient4CPP!metaq_juce::URL::launchInDefaultBrowser+0x23072 中看,貌似阿裡開了一個瀏覽器內核,用內核來發送數據,估計這裡併發閾值開的還挺大的,咨詢了下同事是前面有一家大客戶發了很多的簡訊,估計是大量的回持積壓,這個C# sdk進行了瘋狂讀取,這個跟CPU暴漲應該有脫不了的關係。

圖2:

從檢索上看有28個線程貌似正在臨界區等待鎖,CPU高的一個經典案例就是當很多線程在臨界區等待的時候,當某一個正在臨界區中的線程離開後,這28個線程的調度競搶也是CPU高的一個原因。

個人水平有限,進一步挖非托管堆目前還沒這個技術(┬_┬) 。。。

四: 解決方案

這種SDK的問題還能有什麼解決方案,能想到的就是去官網找下可有最新版:

可以看到最新版的 ons-.net v1.1.4 中提到的優化點:優化消息拉取流程,避免特殊情況下拉取異常造成的消息堆積

果然用了最新版的sdk就可以了,

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • akka 2.6.x正式發佈以來已經有好一段時間了。核心變化是typed-actor的正式啟用,當然persistence,cluster等模塊也有較大變化。一開始從名稱估摸就是把傳統any類型的消息改成強類型消息,所以想拖一段時間看看到底能對我們現有基於akka-classic的應用軟體有什麼深層 ...
  • 由於開發中發現以前的Activex控制項功能不夠用,沒辦法需要下載源碼重新增加功能。。。。 這個項目最開始也不我寫的,而我也是個小白,花了半天改好了代碼,然後打包用了一天半。-0- 各種百度,各種找。。。。準備用 InstallShield 2015打包,結果怎麼下打包好了就提示啥啥啥試用版。。。。最 ...
  • 格式轉換convert:轉換圖像的模式transpose:轉換圖像的格式convert之前已經使用過了,這裡就簡單演示一下transpose的作用,transpose主要傳入一些Image中的常量: from PIL import Image# 打開圖像im = Image.open('nnz.jp ...
  • 關於組件 現在前端幾大輪子全面組件化。組件讓我們可以對常用的功能進行封裝,以便復用。組件這東西對於搞.NET的同學其實並不陌生,以前ASP.NET WebForm的用戶控制項其實也是一種組件。它封裝html代碼,封裝業務邏輯,對外提供屬性事件等信息,它完完全全就是個組件,只是用戶控制項跑在服務端,而現在 ...
  • VS這麼強大的IDE,功能很多,有時候想知道某些功能藏在哪,是挺難找的。但是可以根據關鍵字搜索這些功能的駐扎點,在右上角的搜索框(Ctrl+Q)。 比如想知道下麵的計算解決方案的代碼度量的操作位置。 按enter鍵也可以,點擊那個搜索小圖標也可以,既能搜代碼,也能搜IDE功能。 假如想知道vs的顏色 ...
  • 在.NET中,我們可以通過Task.WhenAll用來等待多個任務。任務完成之後,我們可以使用await等待他們來獲取結果。 Task<int> task1 = Task.Run(() => 1); Task<string> task2 = Task.Run(() => "hello"); awai ...
  • 當用戶嚮應用程式發出請求時,伺服器將解析該請求,生成響應,然後將結果發送給客戶端。用戶可能會在伺服器處理請求的時候中止請求。就比如說用戶跳轉到另一個頁面中獲取說關閉頁面。在這種情況下,我們希望停止所有正在進行的工作,以浪費不必要的資源。例如我們可能要取消SQL請求、http調用請求、CPU密集型操作 ...
  • 一.相關介紹 Dockerfile:關於Dockerfile的使用說明,我在文章《讓.NetCore程式跑在任何有docker的地方》中有說到,這裡不在贅述,需要的可以先看下,本文主要介紹Jenkinsfile結合dockerfile配合使用,自動構建.NetCore應用程式。 Jenkinsfil ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...