【編者按】本文系國內(nèi) ITOM 管理平臺 OneAPM 翻譯自 Steven Haines 的文章瓮顽。Steven Haines 是 Pisksel 技術架構師,目前在奧蘭多迪士尼樂園工作。他是在線教育網(wǎng)站 geekcap.com 的創(chuàng)始人本缠,著有上百篇 Java 相關的文章以及三本 Java 著作:《Java 2 From Scratch》《Java 2 Primer Plus》以及《Pro Java EE Performance Management and Optimization》
實現(xiàn)有效 APM 策略所面臨的挑戰(zhàn):
- 代碼依賴
- 過度或不必要的日志
- 同步與鎖
- 潛在數(shù)據(jù)庫問題
- 潛在的基礎架構問題
1、代碼依賴
開發(fā)程序是一項具有挑戰(zhàn)性的工作。你不僅要為了滿足商業(yè)需求而建立程序邏輯器仗,還要選擇最合適的代碼庫和工具來幫助你。你能想象自己創(chuàng)建所有的日志管理代碼童番,XML 和 JSON 解析邏輯精钮,或所有的序列化庫么?你當然可以編寫代碼來完成這些事剃斧,但是諸多開源開發(fā)者團隊已經(jīng)做好了這些事情轨香,你又何必親力親為呢?此外幼东,如果你正在與第三方系統(tǒng)集成臂容,你會自己讀完專有的通信協(xié)議規(guī)范,還是購買供應商提供的庫幫你完成呢根蟹?
我相信你會同意:如果有人已經(jīng)解決了你的問題脓杉,使用他的解決辦法會比自己想辦法解決效率更高。如果這是一個已經(jīng)被許多公司采用的開源項目简逮,那么很可能它已經(jīng)經(jīng)過完備的測試球散,文檔充足,而且你應該找得到許多使用教程散庶。
然而蕉堰,使用依賴庫是有危險的凌净。你需要回答以下問題:
- 這個庫真的寫得很好并且已經(jīng)充分測試了嗎?
- 你是否用與眾多公司一樣的方式使用這個庫嘁灯?
- 你的使用方式是否正確泻蚊?
請確保在選擇外部庫之前進行一些調(diào)查,如果你對某個庫的性能有什么疑問丑婿,那就進行一些性能測試性雄。開源項目很好的地方在于你可以訪問它們的全部源代碼以及測試套件和構建流程。下載它們的源代碼羹奉,執(zhí)行編譯過程秒旋,并查看測試結果。如果你看到很高的測試覆蓋率诀拭,那么就可以比沒有測試案例時信心百倍迁筛!
最后,確保正確地使用依賴庫耕挨。如果正確使用细卧,ORM 工具的確能夠大大提高性能。ORM 工具的問題在于筒占,如果你不花時間去學習如何正確地使用它贪庙,你就會輕易的砸自己腳,破壞自己的應用性能翰苫。關鍵就在于如果不花時間學習這些工具止邮,本應幫助你的工具反而會傷害你。
2奏窑、過度或不必要的日志
日志記錄是調(diào)試工具庫里的強大武器导披,可以幫助你識別應用執(zhí)行過程中在特定時間內(nèi)可能發(fā)生的異常。當錯誤發(fā)生時埃唯,捕捉錯誤信息并收集盡可能多的上下文信息是非常重要的撩匕。然而,簡潔地捕捉錯誤條件和過度記錄之間是有差別的墨叛。
最普遍的兩個問題就是:
- 多級別異常日志
- 錯誤配置生產(chǎn)日志級別
異常日志能幫助你了解應用程序中發(fā)生的問題滑沧,因而非常重要。但一個常見的問題是巍实,應用程序所有層級的異常都進行記錄。例如哩牍,你的某個數(shù)據(jù)訪問對象捕獲到一個數(shù)據(jù)庫異常棚潦,并將該異常傳達到服務層。服務層可能會捕捉該異常膝昆,并將其傳達到網(wǎng)絡層丸边。如果我們在數(shù)據(jù)層叠必、服務層和網(wǎng)絡層上都記錄該異常,那么我們對此相同的錯誤條件就有三條堆棧記錄妹窖。這會導致寫入日志文件的額外負擔纬朝,還會使日志文件充滿冗余信息。但這個問題非常普遍骄呼,我敢斷言共苛,如果你檢查自己的日志文件,你很可能會發(fā)現(xiàn)多個這樣的例子蜓萄。
生產(chǎn)應用中常見的另一個大的日志問題與日志級別有關隅茎。.NET 日志記錄器定義了以下日志記錄級別(.NET TraceLevel 與 log4net 中的命名會有所不同,但絕對相似):
Off
Fatal
Error
Warning
Info
Verbose / Debug
在生產(chǎn)應用程序中嫉沽,你應該只記錄 error 或 fetal 級別的日志語句辟犀,在更寬松的環(huán)境中,捕捉 warning 甚至 info 級別的日志信息也完全可以绸硕,但是一旦應用投入生產(chǎn)環(huán)境堂竟,用戶負載將迅速填滿日志并使應用程序陷入癱瘓。如果你不經(jīng)意地將生產(chǎn)環(huán)境下的應用日志級別設為 debug玻佩,應用的響應時間比正常情況下高兩或三倍都不奇怪出嘹!
3、同步與鎖
有時候夺蛇,你想確保應用代碼中每次只有一個線程執(zhí)行一段代碼子集疚漆。
例如,讀取單線程規(guī)則執(zhí)行組件之類的共享軟件資源刁赦,以及文件句柄或網(wǎng)絡連接之類的共享基礎架構資源娶聘。.NET 框架提供了許多不同類型的同步策略,包括鎖/監(jiān)視器甚脉、進程間互斥丸升,和讀/寫鎖這類的專用鎖。
不管你為什么要同步代碼或者選擇什么機制實現(xiàn)代碼同步牺氨,都會導致一個問題:那就是有部分代碼一次只能由一個線程執(zhí)行狡耻。
設想去超市,只有一個收銀員在工作:許多人進入商店猴凹,瀏覽商品夷狰,將商品放進購物車里,但某一時候郊霎,他們不得不排隊以進行支付沼头。在這個例子中,購物是多線程的,每個人都代表一個線程进倍。然而結賬是單線程的土至,這意味著每個人都要花費排隊付款的時間。這個過程如圖1所示猾昆。
![不可忽視的 .NET 應用5大性能問題](https://blog.appdynamics.com/wp-content/uploads/2015/06/screen_shot_2015-06-15_at_10.37.46_am.png)
圖1:線程同步
我們有七個線程陶因,都需要訪問一段同步代碼塊,所以它們依次獲得權限訪問該代碼塊垂蜗,執(zhí)行其功能楷扬,然后繼續(xù)。
在圖2中總結了線程同步的過程么抗。
![不可忽視的 .NET 應用5大性能問題](https://blog.appdynamics.com/wp-content/uploads/2015/06/screen_shot_2015-06-15_at_10.37.58_am.png)
圖2 線程同步過程
首先毅否,為特定的對象(System.Object 派生)創(chuàng)建鎖,意味著當一個線程試圖進入同步代碼塊時必須獲取該同步對象的鎖蝇刀。如果該鎖可用螟加,則該線程被授予執(zhí)行同步代碼的權限。在圖2中的例子中吞琐,當?shù)诙€線程到達時捆探,第一個線程已經(jīng)占有了該鎖,所以第二個線程被強制等待站粟,直到第一個線程執(zhí)行完畢黍图。當?shù)谝粋€線程執(zhí)行結束時奴烙,會釋放該鎖助被,然后第二個線程被授予訪問權限切诀。
正如你可能猜測到的,線程同步將給 .NET 應用帶來一個極大的挑戰(zhàn)幅虑。我們設計應用程序時丰滑,希望其能支持數(shù)十個甚至數(shù)百個同步請求,但線程同步會把所有處理這些請求的線程串行化褒墨,導致性能瓶頸!
解決的辦法有兩種:
- 仔細檢查同步的代碼擎宝,以確定是否存在其他可行辦法
- 限制同步代碼塊的范圍
有時候,你要訪問必須同步的共享資源绍申,但很多時候圃庭,你可以用完全避免同步的方法重新解決該問題。例如,我們之前使用的規(guī)則過程引擎有單線程的要求剧腻,因此拖慢了程序中所有請求的執(zhí)行速度涂屁。這顯然是一個設計上的缺陷,我們可以用一個可以并行工作的庫取代之拆又。你需要問自己是否有更好的選擇:如果你在往一個本地文件系統(tǒng)寫入信息,你是否可以把信息發(fā)送給某項服務栈源,再由該服務將信息存儲到數(shù)據(jù)庫中?你是否可以將對象設為不可變甚垦,從而無論是否有多線程訪問它都沒關系?等等艰亮,等等…
對于那些必須要同步的代碼段挣郭,請合理地選擇鎖。你的目標是將同步代碼塊隔離以滿足最低限度的同步要求兑障。通常最好是定義一個特定的對象進行同步,而不是對包含同步代碼的對象進行同步逞怨,因為你可能會在不經(jīng)意間拖慢該對象的其他交互先蒋。最后,考慮使用讀/寫鎖竞漾,而不是標準的鎖,這樣业岁,你可以在資源只進行同步變化時,允許讀操作棍好。
4.潛在的數(shù)據(jù)庫問題
幾乎所有的內(nèi)容應用最終都會涉及到向/從數(shù)據(jù)庫或文檔存儲儲存/檢索數(shù)據(jù)。因此借笙,數(shù)據(jù)庫、數(shù)據(jù)庫查詢盗痒,以及存儲過程調(diào)優(yōu)對應用程序的性能來說是最重要的低散。
程序架構師/開發(fā)人員和數(shù)據(jù)庫架構師/開發(fā)人員之間有一個哲學性的劃分。應用程序架構師傾向于認為所有的業(yè)務邏輯都應該駐留在應用程序中熔号,數(shù)據(jù)庫應該只提供訪問數(shù)據(jù)的通道稽鞭。另一方面,數(shù)據(jù)庫架構師更傾向于認為將業(yè)務邏輯推到數(shù)據(jù)庫中更有益提高性能朦蕴。這個劃分的答案很可能就是介于兩者之間祠乃。
作為一個應用程序架構師,我傾向于將更多的業(yè)務邏輯應用在程序當中亮瓷,但我完全承認數(shù)據(jù)庫架構師能更好的理解數(shù)據(jù)和與數(shù)據(jù)交互的最佳方式。我認為蚓胸,這兩個群體之間的協(xié)同合作才能產(chǎn)生最佳的解決方案除师。但是,不管你傾向于哪一方汛聚,請確保你的數(shù)據(jù)庫架構師檢查你的數(shù)據(jù)模型,所有的查詢語句和存儲過程叹哭,他們都有豐富的知識幫助你以最佳的方式來調(diào)整和配置數(shù)據(jù)庫痕貌,他們有大量的工具可以為你調(diào)整查詢語句。例如舵稠,有一些工具可用于 SQL 調(diào)優(yōu)入宦,遵循以下這些步驟:
- 分析 SQL 語句
- 確定查詢的執(zhí)行計劃
- 利用人工智能生成備選的 SQL 語句
- 確定所有備選方案的執(zhí)行計劃
- 提出最佳的查詢方式來完成目標
當我在寫數(shù)據(jù)庫查詢代碼時室琢,我使用了這類工具,并在高負載情況下量化了結果汹忠,一些細微的調(diào)整和優(yōu)化雹熬,都能導致極大的性能提升谣膳。
5、潛在的基礎架構問題
之前提過继谚,.NET 應用運行在分層的環(huán)境中,其層級結構如圖3所示:
![不可忽視的 .NET 應用5大性能問題](https://blog.appdynamics.com/wp-content/uploads/2015/06/screen_shot_2015-06-15_at_10.38.11_am.png)
圖3.NET分層執(zhí)行模型
你的應用程序運行在 ASP.NET 或是 Windows Forms 容器中芽世,使用 ADO 庫與運行在 CLR 內(nèi)部的數(shù)據(jù)庫交互诡壁,而 CLR 運行在操作系統(tǒng)中,操作系統(tǒng)又運行在硬件里旺矾。而該硬件又與其他包含不同技術堆棧的硬件通過網(wǎng)絡相連夺克。在你的應用與外部環(huán)境之間,以及在應用的組件之間铺纽,通常有多個負載平衡器。我們還有 API 管理服務以及多級緩存陷寝。所有這一切融撞,都是為了說明,基礎構造數(shù)量龐雜尝偎,都可能影響應用程序的性能鹏控!
因此肤寝,你必須細致地調(diào)整基礎架構。檢查你的應用組件和數(shù)據(jù)庫所運行的操作系統(tǒng)和硬件設備缘揪,以確保它們的最佳表現(xiàn)义桂。測量服務器之間的網(wǎng)絡延遲,并確保你有足夠的帶寬來滿足應用程序之間的交互慷吊。檢查緩存,確保較高的緩存命中率急鳄。分析負載平衡器的行為以確保請求很快地分發(fā)到所有可用的服務器堰酿。總之触创,你需要全面檢查應用程序的性能,既包括應用業(yè)務交易也包括支持它們的基礎架構顺饮。
OneAPM 助您輕松鎖定 .NET 應用性能瓶頸凌那,通過強大的 Trace 記錄逐層分析,直至鎖定行級問題代碼帽蝶。以用戶角度展示系統(tǒng)響應速度,以地域和瀏覽器維度統(tǒng)計用戶使用情況佃乘。想閱讀更多技術文章驹尼,請訪問 OneAPM 官方博客。
本文轉自 OneAPM 官方博客