Freigeben über


活用 .NET Reflection 機制 來完成動態 Performance Counter 建構管理

在 .NET 應用程式中, 如何來偵測系統的效能 ? 用什麼樣的架構 ? 什麼樣的元件 ?

這個問題其實是很有趣的, 快速整理的一下手邊的經驗與資源. 與各位夥伴分享

----

方法其實很多, 過去有許多很有意思的文章.. 以及範例. 內容都在討論 .NET 應用程式中透過 Performance Counter 來預先做好 效能偵測器 !

做法其實很簡單 ! 透過 .NET Framework 中 System.Diagnostics 元件庫裏頭的相關 Performance Counter, 其實你可以在你的應用程式中 埋下許多重要的 偵測指標.

不過, 如果真要量產在你的軟體架構中, 各位資深的設計夥伴大概就會想到, 這個 API 其實還可以在包裝一下, 成為更好的共用元件與模組 !. 而我本人也相當同意這個想法 ~

兩年前, 為了這個想法, 我也花了不少力氣 整理了一些東西, 其中不少設計, 是來自 偉大的 Open Source 領域, 這裡要特別和各位談一篇文章, 因為我的版本後來就是由這裏開始改起~~~

Using custom attributes to add performance counters to your application By Duncan Edwards Jones .

Duncan 的 VB.NET 1.1 範例元件中, 導入了 Reflection 的概念, 這和我的想法不謀而合~. 只是 他的設計方法 到最後 和我的需求, 卻是有點差異的 ~

我的目標是 讓我的 開發團隊, 在我的架構下, 可以很簡單的寫程式. 完全不用考慮到 要寫死一堆為了做好 Performance Counter 的程式碼. 簡單到什麼程度呢 ? 在當時, 我想到一個好方法, 透過 .NET 1.1 最酷的 Attribute 標籤. 以及 動態的 Reflection 機制 ! 如下範例 :

[PerformanceCounterCategory("MyBusinessApplication",

"My Business Application Performance Counter Category")]

public class SomeBusinessSerivceProvider {

 

[PerformanceCounter("HowFastWeMakeTheMoney",

"Count how long we fill an big order",

PerformanceCounterType.RateOfCountsPerSecond32,

PerformanceCounterAttribute.PerformanceCounterUpdateMethod.Update_Increment)]

[PerformanceCounter("HowManyBigOrderWeHave",

"Count how many big order we have",

PerformanceCounterType.NumberOfItems32, 

PerformanceCounterAttribute.PerformanceCounterUpdateMethod.Update_Increment)]

public string PlaceBigOrder(object po) {

            BigOrder bigPO = new BigOrder(po);

            bigPO.Save();

            return bigPO.PoNumber;

        }

    }

這個範例中, 我有個重要的商業物件, 我非常關心這個商業物件的商業行為 "PlaceBigOrder"

  • 是否有穩定的在作業 ! 下大單的動作 可千萬不能當機 !!!
  • 效能好不好啊, 下個大單到底要花多少時間

因此 透過架構與設計好的元件, 負責開發的人員 只要在 Class 宣告上 定義 "某一個 Performance Counter Category", 並且在關心的 Method 上加上 我要追蹤的 "Counter" 有哪些 ! 其他就交給 我這個架構師所設計的 魔術元件

系統上線時 ! 我可以在 作業系統的 Performance Counter 上欣賞, 我的系統穩定的運作~ 我的 大單不斷的敲進來 ~~~ 讚.

上圖中, 綠色線條 表示 目前我已經累積了多少大單跑進來了~ 而紅色的線條更重要了, 首先他等於是系統的"心跳"! 如果他不跳了 表示系統可能出現嚴重停擺或當機 !!! 其次, 如果沒有心跳了 表示我的 大單 $$ 就不進來了 !!! :(

很酷的概念.. 很不錯的設計架構吧 ~ 接下來欣賞一下. 包裝過後的 魔術 元件使用方式!

SomeBusinessSerivceProvider target = new SomeBusinessSerivceProvider();

actual = target.PlaceBigOrder(po);   // 透過我的商業物件服務 下單         PerformanceCounterUtilities.UpdatePerformanceCounter(target);

只剩下一行程式碼, 就可以把你的 商業物件, 動態 Reflect 出是否有需要 Performance Counter 的服務 ~ 如果要 這個 PerformanceCounterUtilities 物件會幫你自動的建立與產生!

各位可以去下載上述文章覆上的範例

我所異動的部分, 其實是 刪除很多 以及加上了一些我要的部分

其中最重要的是

Public Overloads Shared Sub UpdatePerformanceCounter(ByVal oInput As Object)

Dim ObjectType As Type = oInput.GetType
Dim CounterCategory As PerformanceCounterCategoryAttribute = Attribute.GetCustomAttribute(ObjectType, GetType(PerformanceCounterCategoryAttribute))

If CounterCategory Is Nothing Then
If PerformanceCounterUtilitiesTracing.TraceWarning Then
Trace.WriteLine("The object passed in does have a <PerformaceCounterCategoryAttribute()> attribute", ObjectType.ToString)
End If
Debug.Fail(" The object passed in does have a <PerformaceCounterCategoryAttribute()> attribute ")
Exit Sub
End If

'\\ Get the property attributes
Dim ObjectProperties() As PropertyInfo = ObjectType.GetProperties(BindingFlags.Instance Or BindingFlags.Public)
Dim ObjectMethods() As MethodInfo = ObjectType.GetMethods(BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic)

Dim PerformanceCounterAttributes As New Collection

For Each pi As MethodInfo In ObjectMethods
If pi.DeclaringType Is ObjectType Then
Dim pc() As PerformanceCounterAttribute = pi.GetCustomAttributes(GetType(PerformanceCounterAttribute), False)

For Each Item As PerformanceCounterAttribute In pc
PerformanceCounterAttributes.Add(New MethodOrPropertyToCounterAttributeLink(Item, pi), item.Name)
Next

End If
Next

For Each pi As PropertyInfo In ObjectProperties
If pi.DeclaringType Is ObjectType Then
Dim pc() As PerformanceCounterAttribute = pi.GetCustomAttributes(GetType(PerformanceCounterAttribute), False)
If pc.Length > 0 Then
PerformanceCounterAttributes.Add(New MethodOrPropertyToCounterAttributeLink(pc.GetValue(0), pi), pi.Name)
End If
End If
Next

If Not PerformanceCounterCategory.Exists(CounterCategory.Name) Then
RebuildPerformanceCounterCategory(oInput)
End If

Dim Category As New PerformanceCounterCategory
Category.CategoryName = CounterCategory.Name

For Each Counter As MethodOrPropertyToCounterAttributeLink In PerformanceCounterAttributes
Try
If Category.CounterExists(Counter.Attribute.Name) Then
Dim CounterInstance As New PerformanceCounter(CounterCategory.Name, Counter.Attribute.Name, "")
CounterInstance.ReadOnly = False
If Counter.Attribute.UpdateMethod = PerformanceCounterAttribute.PerformanceCounterUpdateMethod.Update_Increment Then
CounterInstance.Increment()
ElseIf Counter.Attribute.UpdateMethod = PerformanceCounterAttribute.PerformanceCounterUpdateMethod.Update_IncrementBy Then
CounterInstance.IncrementBy(RawValueFromProperty(Counter.linkInfo, oInput))
Else
CounterInstance.RawValue = RawValueFromProperty(Counter.linkInfo, oInput)
End If
CounterInstance.Close()
Else
If PerformanceCounterUtilitiesTracing.TraceError Then
Trace.WriteLine("Counter instance does not exist", Counter.Attribute.Name)
End If
Debug.Fail("Counter instance does not exist")
End If
Catch ex As Exception
If PerformanceCounterUtilitiesTracing.TraceError Then
Trace.WriteLine(ex.ToString, Counter.Attribute.Name)
End If
Debug.Fail("Counter instance correctly not created")
End Try
Next

End Sub

原先範例中PropertyToCounterAttributeLink物件我一點也不喜歡, 所以改為更 一般性的

Private Class MethodOrPropertyToCounterAttributeLink
Public Attribute As PerformanceCounterAttribute
Public linkInfo As MemberInfo

Public Sub New(ByVal AttributeIn As PerformanceCounterAttribute, ByVal mInfo As MemberInfo)
Attribute = AttributeIn
linkInfo = mInfo
End Sub
End Class

下載 Sample Code. (Ps. 因為某些原因,這元件已經一段時間沒 update, 我只把它 upgrade 到.NET 2.0 中, 並且只有改了我在 Test Case 中有驗證過的 API, 其他的 API 一定會有 bug 等需要調整, 要靠你自行修正:) 因為 這也是 Open Source 囉)