實(shí)現(xiàn)有效 APM 策略所面臨的挑戰(zhàn):
- 代碼依賴
- 過(guò)度或不必要的日志
- 同步與鎖
- 潛在數(shù)據(jù)庫(kù)問(wèn)題
- 潛在的基礎(chǔ)架構(gòu)問(wèn)題
1、代碼依賴
開發(fā)程序是一項(xiàng)具有挑戰(zhàn)性的工作。你不僅要為了滿足商業(yè)需求而建立程序邏輯,還要選擇最合適的代碼庫(kù)和工具來(lái)幫助你。你能想象自己創(chuàng)建所有的日志管理代碼,XML 和 JSON 解析邏輯,或所有的序列化庫(kù)么?你當(dāng)然可以編寫代碼來(lái)完成這些事,但是諸多開源開發(fā)者團(tuán)隊(duì)已經(jīng)做好了這些事情,你又何必親力親為呢?此外,如果你正在與第三方系統(tǒng)集成,你會(huì)自己讀完專有的通信協(xié)議規(guī)范,還是購(gòu)買供應(yīng)商提供的庫(kù)幫你完成呢?
我相信你會(huì)同意:如果有人已經(jīng)解決了你的問(wèn)題,使用他的解決辦法會(huì)比自己想辦法解決效率更高。如果這是一個(gè)已經(jīng)被許多公司采用的開源項(xiàng)目,那么很可能它已經(jīng)經(jīng)過(guò)完備的測(cè)試,文檔充足,而且你應(yīng)該找得到許多使用教程。
然而,使用依賴庫(kù)是有危險(xiǎn)的。你需要回答以下問(wèn)題:
- 這個(gè)庫(kù)真的寫得很好并且已經(jīng)充分測(cè)試了嗎?
- 你是否用與眾多公司一樣的方式使用這個(gè)庫(kù)?
- 你的使用方式是否正確?
請(qǐng)確保在選擇外部庫(kù)之前進(jìn)行一些調(diào)查,如果你對(duì)某個(gè)庫(kù)的性能有什么疑問(wèn),那就進(jìn)行一些性能測(cè)試。開源項(xiàng)目很好的地方在于你可以訪問(wèn)它們的全部源代碼以及測(cè)試套件和構(gòu)建流程。下載它們的源代碼,執(zhí)行編譯過(guò)程,并查看測(cè)試結(jié)果。如果你看到很高的測(cè)試覆蓋率,那么就可以比沒有測(cè)試案例時(shí)信心百倍!
最后,確保正確地使用依賴庫(kù)。如果正確使用,ORM 工具的確能夠大大提高性能。ORM 工具的問(wèn)題在于,如果你不花時(shí)間去學(xué)習(xí)如何正確地使用它,你就會(huì)輕易的砸自己腳,破壞自己的應(yīng)用性能。關(guān)鍵就在于如果不花時(shí)間學(xué)習(xí)這些工具,本應(yīng)幫助你的工具反而會(huì)傷害你。
2、過(guò)度或不必要的日志
日志記錄是調(diào)試工具庫(kù)里的強(qiáng)大武器,可以幫助你識(shí)別應(yīng)用執(zhí)行過(guò)程中在特定時(shí)間內(nèi)可能發(fā)生的異常。當(dāng)錯(cuò)誤發(fā)生時(shí),捕捉錯(cuò)誤信息并收集盡可能多的上下文信息是非常重要的。然而,簡(jiǎn)潔地捕捉錯(cuò)誤條件和過(guò)度記錄之間是有差別的。
最普遍的兩個(gè)問(wèn)題就是:
- 多級(jí)別異常日志
- 錯(cuò)誤配置生產(chǎn)日志級(jí)別
異常日志能幫助你了解應(yīng)用程序中發(fā)生的問(wèn)題,因而非常重要。但一個(gè)常見的問(wèn)題是,應(yīng)用程序所有層級(jí)的異常都進(jìn)行記錄。例如,你的某個(gè)數(shù)據(jù)訪問(wèn)對(duì)象捕獲到一個(gè)數(shù)據(jù)庫(kù)異常,并將該異常傳達(dá)到服務(wù)層。服務(wù)層可能會(huì)捕捉該異常,并將其傳達(dá)到網(wǎng)絡(luò)層。如果我們?cè)跀?shù)據(jù)層、服務(wù)層和網(wǎng)絡(luò)層上都記錄該異常,那么我們對(duì)此相同的錯(cuò)誤條件就有三條堆棧記錄。這會(huì)導(dǎo)致寫入日志文件的額外負(fù)擔(dān),還會(huì)使日志文件充滿冗余信息。但這個(gè)問(wèn)題非常普遍,我敢斷言,如果你檢查自己的日志文件,你很可能會(huì)發(fā)現(xiàn)多個(gè)這樣的例子。
生產(chǎn)應(yīng)用中常見的另一個(gè)大的日志問(wèn)題與日志級(jí)別有關(guān)。.NET 日志記錄器定義了以下日志記錄級(jí)別(.NET TraceLevel 與 log4net 中的命名會(huì)有所不同,但絕對(duì)相似):
- Off
- Fatal
- Error
- Warning
- Info
- Verbose / Debug
在生產(chǎn)應(yīng)用程序中,你應(yīng)該只記錄 error 或 fetal 級(jí)別的日志語(yǔ)句,在更寬松的環(huán)境中,捕捉 warning 甚至 info 級(jí)別的日志信息也完全可以,但是一旦應(yīng)用投入生產(chǎn)環(huán)境,用戶負(fù)載將迅速填滿日志并使應(yīng)用程序陷入癱瘓。如果你不經(jīng)意地將生產(chǎn)環(huán)境下的應(yīng)用日志級(jí)別設(shè)為 debug,應(yīng)用的響應(yīng)時(shí)間比正常情況下高兩或三倍都不奇怪!
3、同步與鎖
有時(shí)候,你想確保應(yīng)用代碼中每次只有一個(gè)線程執(zhí)行一段代碼子集。 例如,讀取單線程規(guī)則執(zhí)行組件之類的共享軟件資源,以及文件句柄或網(wǎng)絡(luò)連接之類的共享基礎(chǔ)架構(gòu)資源。.NET 框架提供了許多不同類型的同步策略,包括鎖/監(jiān)視器、進(jìn)程間互斥,和讀/寫鎖這類的專用鎖。
不管你為什么要同步代碼或者選擇什么機(jī)制實(shí)現(xiàn)代碼同步,都會(huì)導(dǎo)致一個(gè)問(wèn)題:那就是有部分代碼一次只能由一個(gè)線程執(zhí)行。 設(shè)想去超市,只有一個(gè)收銀員在工作:許多人進(jìn)入商店,瀏覽商品,將商品放進(jìn)購(gòu)物車?yán)铮骋粫r(shí)候,他們不得不排隊(duì)以進(jìn)行支付。在這個(gè)例子中,購(gòu)物是多線程的,每個(gè)人都代表一個(gè)線程。然而結(jié)賬是單線程的,這意味著每個(gè)人都要花費(fèi)排隊(duì)付款的時(shí)間。這個(gè)過(guò)程如圖1所示。
圖1:線程同步
我們有七個(gè)線程,都需要訪問(wèn)一段同步代碼塊,所以它們依次獲得權(quán)限訪問(wèn)該代碼塊,執(zhí)行其功能,然后繼續(xù)。
在圖2中總結(jié)了線程同步的過(guò)程。
圖2 線程同步過(guò)程
首先,為特定的對(duì)象(System.Object 派生)創(chuàng)建鎖,意味著當(dāng)一個(gè)線程試圖進(jìn)入同步代碼塊時(shí)必須獲取該同步對(duì)象的鎖。如果該鎖可用,則該線程被授予執(zhí)行同步代碼的權(quán)限。在圖2中的例子中,當(dāng)?shù)诙€(gè)線程到達(dá)時(shí),第一個(gè)線程已經(jīng)占有了該鎖,所以第二個(gè)線程被強(qiáng)制等待,直到第一個(gè)線程執(zhí)行完畢。當(dāng)?shù)谝粋€(gè)線程執(zhí)行結(jié)束時(shí),會(huì)釋放該鎖,然后第二個(gè)線程被授予訪問(wèn)權(quán)限。
正如你可能猜測(cè)到的,線程同步將給 .NET 應(yīng)用帶來(lái)一個(gè)極大的挑戰(zhàn)。我們?cè)O(shè)計(jì)應(yīng)用程序時(shí),希望其能支持?jǐn)?shù)十個(gè)甚至數(shù)百個(gè)同步請(qǐng)求,但線程同步會(huì)把所有處理這些請(qǐng)求的線程串行化,導(dǎo)致性能瓶頸!
解決的辦法有兩種:
- 仔細(xì)檢查同步的代碼,以確定是否存在其他可行辦法
- 限制同步代碼塊的范圍
有時(shí)候,你要訪問(wèn)必須同步的共享資源,但很多時(shí)候,你可以用完全避免同步的方法重新解決該問(wèn)題。例如,我們之前使用的規(guī)則過(guò)程引擎有單線程的要求,因此拖慢了程序中所有請(qǐng)求的執(zhí)行速度。這顯然是一個(gè)設(shè)計(jì)上的缺陷,我們可以用一個(gè)可以并行工作的庫(kù)取代之。你需要問(wèn)自己是否有更好的選擇:如果你在往一個(gè)本地文件系統(tǒng)寫入信息,你是否可以把信息發(fā)送給某項(xiàng)服務(wù),再由該服務(wù)將信息存儲(chǔ)到數(shù)據(jù)庫(kù)中?你是否可以將對(duì)象設(shè)為不可變,從而無(wú)論是否有多線程訪問(wèn)它都沒關(guān)系?等等,等等…
對(duì)于那些必須要同步的代碼段,請(qǐng)合理地選擇鎖。你的目標(biāo)是將同步代碼塊隔離以滿足最低限度的同步要求。通常最好是定義一個(gè)特定的對(duì)象進(jìn)行同步,而不是對(duì)包含同步代碼的對(duì)象進(jìn)行同步,因?yàn)槟憧赡軙?huì)在不經(jīng)意間拖慢該對(duì)象的其他交互。最后,考慮使用讀/寫鎖,而不是標(biāo)準(zhǔn)的鎖,這樣,你可以在資源只進(jìn)行同步變化時(shí),允許讀操作。
4.潛在的數(shù)據(jù)庫(kù)問(wèn)題
幾乎所有的內(nèi)容應(yīng)用最終都會(huì)涉及到向/從數(shù)據(jù)庫(kù)或文檔存儲(chǔ)儲(chǔ)存/檢索數(shù)據(jù)。因此,數(shù)據(jù)庫(kù)、數(shù)據(jù)庫(kù)查詢,以及存儲(chǔ)過(guò)程調(diào)優(yōu)對(duì)應(yīng)用程序的性能來(lái)說(shuō)是最重要的。
程序架構(gòu)師/開發(fā)人員和數(shù)據(jù)庫(kù)架構(gòu)師/開發(fā)人員之間有一個(gè)哲學(xué)性的劃分。應(yīng)用程序架構(gòu)師傾向于認(rèn)為所有的業(yè)務(wù)邏輯都應(yīng)該駐留在應(yīng)用程序中,數(shù)據(jù)庫(kù)應(yīng)該只提供訪問(wèn)數(shù)據(jù)的通道。另一方面,數(shù)據(jù)庫(kù)架構(gòu)師更傾向于認(rèn)為將業(yè)務(wù)邏輯推到數(shù)據(jù)庫(kù)中更有益提高性能。這個(gè)劃分的答案很可能就是介于兩者之間。
作為一個(gè)應(yīng)用程序架構(gòu)師,我傾向于將更多的業(yè)務(wù)邏輯應(yīng)用在程序當(dāng)中,但我完全承認(rèn)數(shù)據(jù)庫(kù)架構(gòu)師能更好的理解數(shù)據(jù)和與數(shù)據(jù)交互的最佳方式。我認(rèn)為,這兩個(gè)群體之間的協(xié)同合作才能產(chǎn)生最佳的解決方案。但是,不管你傾向于哪一方,請(qǐng)確保你的數(shù)據(jù)庫(kù)架構(gòu)師檢查你的數(shù)據(jù)模型,所有的查詢語(yǔ)句和存儲(chǔ)過(guò)程,他們都有豐富的知識(shí)幫助你以最佳的方式來(lái)調(diào)整和配置數(shù)據(jù)庫(kù),他們有大量的工具可以為你調(diào)整查詢語(yǔ)句。例如,有一些工具可用于 SQL 調(diào)優(yōu),遵循以下這些步驟:
- 分析 SQL 語(yǔ)句
- 確定查詢的執(zhí)行計(jì)劃
- 利用人工智能生成備選的 SQL 語(yǔ)句
- 確定所有備選方案的執(zhí)行計(jì)劃
- 提出最佳的查詢方式來(lái)完成目標(biāo)
當(dāng)我在寫數(shù)據(jù)庫(kù)查詢代碼時(shí),我使用了這類工具,并在高負(fù)載情況下量化了結(jié)果,一些細(xì)微的調(diào)整和優(yōu)化,都能導(dǎo)致極大的性能提升。
5、潛在的基礎(chǔ)架構(gòu)問(wèn)題
之前提過(guò),.NET 應(yīng)用運(yùn)行在分層的環(huán)境中,其層級(jí)結(jié)構(gòu)如圖3所示:
圖3.NET分層執(zhí)行模型
你的應(yīng)用程序運(yùn)行在 ASP.NET 或是 Windows Forms 容器中,使用 ADO 庫(kù)與運(yùn)行在 CLR 內(nèi)部的數(shù)據(jù)庫(kù)交互,而 CLR 運(yùn)行在操作系統(tǒng)中,操作系統(tǒng)又運(yùn)行在硬件里。而該硬件又與其他包含不同技術(shù)堆棧的硬件通過(guò)網(wǎng)絡(luò)相連。在你的應(yīng)用與外部環(huán)境之間,以及在應(yīng)用的組件之間,通常有多個(gè)負(fù)載平衡器。我們還有 API 管理服務(wù)以及多級(jí)緩存。所有這一切,都是為了說(shuō)明,基礎(chǔ)構(gòu)造數(shù)量龐雜,都可能影響應(yīng)用程序的性能!
因此,你必須細(xì)致地調(diào)整基礎(chǔ)架構(gòu)。檢查你的應(yīng)用組件和數(shù)據(jù)庫(kù)所運(yùn)行的操作系統(tǒng)和硬件設(shè)備,以確保它們的最佳表現(xiàn)。測(cè)量服務(wù)器之間的網(wǎng)絡(luò)延遲,并確保你有足夠的帶寬來(lái)滿足應(yīng)用程序之間的交互。檢查緩存,確保較高的緩存命中率。分析負(fù)載平衡器的行為以確保請(qǐng)求很快地分發(fā)到所有可用的服務(wù)器。總之,你需要全面檢查應(yīng)用程序的性能,既包括應(yīng)用業(yè)務(wù)交易也包括支持它們的基礎(chǔ)架構(gòu)。
您可能感興趣的文章:- Asp.net 網(wǎng)站性能優(yōu)化二則分享
- ASP.NET性能優(yōu)化之構(gòu)建自定義文件緩存
- .NET獲取枚舉DescriptionAttribute描述信息性能改進(jìn)的多種方法
- 分享提高ASP.NET Web應(yīng)用性能的技巧