運(yùn)維老兵對運(yùn)維中常見技術(shù)類問題剖析
今天準(zhǔn)備談下對于IT人員面對技術(shù)類問題分析和解決的一些思路和實(shí)踐總結(jié),在很早以前我就談到過,對于開發(fā)人員在后期需要的不是簡單的新業(yè)務(wù)功能的設(shè)計(jì)和開發(fā)能力,而是問題分析和解決能力。這類問題分析和解決本身又包括了兩個(gè)方面內(nèi)容:
其一是IT系統(tǒng)運(yùn)行類問題和故障的分析和解決;
其二是面對復(fù)雜業(yè)務(wù)問題時(shí)候?qū)⑵滢D(zhuǎn)化為技術(shù)解決方案能力。
在前面我講思維類文章的時(shí)候就專門談到IT人員應(yīng)該關(guān)注自己思維能力的提升,這個(gè)思維能力實(shí)際上包括了分析和認(rèn)知事物,獨(dú)立的問題分析和解決兩個(gè)層面的內(nèi)容。
對于第一個(gè)層面在IT領(lǐng)域更多的就是架構(gòu)設(shè)計(jì)的能力,將現(xiàn)實(shí)的業(yè)務(wù)需求和場景轉(zhuǎn)化為抽象的架構(gòu)設(shè)計(jì)語言和架構(gòu)模型的能力;
而第二個(gè)層面在IT領(lǐng)域里面即是面對問題或故障的時(shí)候進(jìn)行問題分析診斷,假設(shè)和驗(yàn)證,快速解決的能力。
而對我們當(dāng)前很多IT人員來說,實(shí)際上兩個(gè)方面的能力都欠缺,既不能獨(dú)立的進(jìn)行整體架構(gòu)設(shè)計(jì),對負(fù)責(zé)的業(yè)務(wù)進(jìn)行自頂向下,分而治之的建模和設(shè)計(jì)。也不能在面對生產(chǎn)環(huán)境關(guān)鍵故障或問題的時(shí)候快速定位,并找到根源快速解決。而是將自己大量的時(shí)間花費(fèi)在重復(fù)的事務(wù)性工作上,花費(fèi)在對各類新技術(shù)的狂熱追求上。
實(shí)際上自己也從不反對保持對新技術(shù)的學(xué)習(xí)興趣。但是任何新技術(shù),如果你實(shí)際的工作環(huán)境沒有實(shí)踐的機(jī)會,那么大量新技術(shù)下應(yīng)該出現(xiàn)的類似性能,安全,可靠性等問題你都無法真正得到實(shí)踐驗(yàn)證和解決。
在這種情況下對新技術(shù)也只能夠停留在理論階段而無太大意義。

對于問題分析和解決的核心邏輯,可以先參考我前面發(fā)布的文章:《問題分析和解決邏輯-麥肯錫七步成詩僅是開始》(http://blog.sina.com.cn/s/blog_493a84550102z8l9.html)?在前面這篇文章里面,結(jié)合麥肯錫問題分析七步法,對問題分析核心邏輯進(jìn)行了詳細(xì)的描述。
一、技術(shù)問題解決的關(guān)鍵點(diǎn)
我寫過不少的關(guān)于技術(shù)問題分析和診斷的文章,這些問題基本也是來源于真實(shí)的項(xiàng)目實(shí)踐。
即使到現(xiàn)在有些問題也沒有完全得到定位和最終解決,包括我們找了Oracle專家和顧問,也不是說馬上就能夠滿足我們解決掉該技術(shù)問題。
簡單來說,如果一個(gè)技術(shù)問題,你能夠直接快速的根據(jù)異?;騿栴}關(guān)鍵字在網(wǎng)上搜索到相關(guān)的答案,這種問題都談不上真正有挑戰(zhàn)的技術(shù)問題。
對于技術(shù)問題的解決,基于前面實(shí)踐的問題定位、分析和解決的思路,我還是想談下在解決技術(shù)問題中的一些關(guān)鍵點(diǎn)和思考邏輯方面的內(nèi)容。
這點(diǎn)相當(dāng)重要,任何知識庫,搜索都代替不了個(gè)人已有的知識經(jīng)驗(yàn)積累。

為什么說工作經(jīng)驗(yàn)很值錢?
往往就是因?yàn)槟阍谝粋€(gè)專業(yè)領(lǐng)域有大量實(shí)踐積累,大量問題分析解決經(jīng)驗(yàn)積累。這些經(jīng)驗(yàn)可以幫助你在遇到問題的時(shí)候快速地對問題進(jìn)行預(yù)判和定位,包括提出最可能的假設(shè)路徑。
當(dāng)前解決問題,很多都是非結(jié)構(gòu)化解決問題方法,即是優(yōu)先提出最可能的假設(shè),然后再去驗(yàn)證假設(shè)是否能夠真正解決問題。
那么有經(jīng)驗(yàn)的人往往就最容易提出最可能的假設(shè)路徑,而減少對各種不可能彎路的嘗試。一個(gè)問題本身有A到E五個(gè)獨(dú)立假設(shè)路徑,而最可能路徑是A,你解決問題速度慢的原因往往就是你最后才假設(shè)和嘗試到A路徑并解決問題,而有經(jīng)驗(yàn)的人往往一開始就選擇了假設(shè)A進(jìn)行驗(yàn)證。
要積累這種經(jīng)驗(yàn),必須在問題解決后及時(shí)復(fù)盤,將其抽象為經(jīng)驗(yàn)和方法論。
問題定位的重點(diǎn)就是縮小范圍和確定邊界。一個(gè)問題出現(xiàn)之后最重要的就是快速的定位。
比如一個(gè)業(yè)務(wù)系統(tǒng)查詢故障,要快速的定位是基礎(chǔ)設(shè)施資源的問題,還是數(shù)據(jù)庫和中間件的問題,還是說程序的問題。
如果是程序的問題,又需要馬上定位到究竟是前端的問題,還是邏輯層的問題或數(shù)據(jù)庫的問題。
只有快速的確定邊界和定位問題,才能夠有針對性的去解決問題。任何問題的定位都是追溯到引發(fā)問題的根源,而不是解決問題的表象,類似頭痛醫(yī)頭腳痛醫(yī)腳。
那么如何縮小范圍和快速的確定邊界?

比如我們假設(shè)一個(gè)最簡單的場景:問題的產(chǎn)生經(jīng)歷了A-》B的兩個(gè)過程。那么如何快速的確定問題是在A階段產(chǎn)生的還是在B階段產(chǎn)生的呢?
對于這個(gè)問題,我們有如下的問題定位方法和思路可以參考和借鑒:
替換法:比如將A替換為A1,如果問題消失,那么說明問題出在A階段;
斷點(diǎn)法:在A和B之間設(shè)置斷點(diǎn)監(jiān)控輸出,判斷A輸出是否正常;
假設(shè)法:假設(shè)A階段有問題,對A階段的參數(shù)進(jìn)行調(diào)整,觀察問題是否解決。
當(dāng)然還有其他很多的問題定位方法,但是對于所有問題定位和確定邊界的方法中,最有效的仍然是類似于快速查找中的二分法,通過二分法可以快速的幫助我們縮小范圍和定位問題。
我們進(jìn)一步對上面邏輯舉例說明,比如一個(gè)軟件應(yīng)用出現(xiàn)Bug的場景,如下圖:

可以看到看到要分析和定位Bug為何困難?
引入問題既可能是我們的輸入出現(xiàn)錯(cuò)誤,我們面對的軟硬件環(huán)境運(yùn)行狀態(tài)有問題,也可能是我們實(shí)際程序處理構(gòu)成出現(xiàn)問題。
即使你定位到程序處理問題,那么還可能是邏輯層,數(shù)據(jù)訪問層或數(shù)據(jù)庫多個(gè)點(diǎn)導(dǎo)致的問題。

要明白,任何你遇到過的技術(shù)問題,往往都有前人遇到過,踩過坑,并總結(jié)和分享到互聯(lián)網(wǎng)上面。因此善用互聯(lián)網(wǎng)和搜索引擎,進(jìn)行基于問題關(guān)鍵字的技術(shù)檢索仍然是解決技術(shù)問題的關(guān)鍵途徑。
即使搜索引擎沒有幫助我們解決最終的問題,往往也會幫助我們在搜索的過程中學(xué)習(xí)與該技術(shù)問題相關(guān)的很多知識。
要搜索,一個(gè)重點(diǎn)就是選擇搜索的關(guān)鍵字,對于關(guān)鍵字的選擇沒有一次就選擇準(zhǔn)確的話,自己就要多次嘗試和迭代,直到能夠準(zhǔn)確的描述問題為止,同時(shí)在搜索的過程中搜索的答案往往也可以幫助你進(jìn)一步的細(xì)化關(guān)鍵字。
比如對于系統(tǒng)運(yùn)行故障或問題,對于關(guān)鍵字的描述,應(yīng)該包括:
從數(shù)據(jù)庫,中間件和業(yè)務(wù)系統(tǒng)錯(cuò)誤日志中提取關(guān)鍵字信息;
從你產(chǎn)生問題的環(huán)境,背景,場景中增加縮小檢索范圍的關(guān)鍵字信息;
從搜索到的網(wǎng)頁中挖掘更加有意義的描述類似問題的關(guān)鍵字信息。
同時(shí)對于搜索而言,特別是技術(shù)問題的搜索,有官方知識庫的要優(yōu)先搜索官方的知識庫:比如對于Oracle產(chǎn)品相關(guān)的技術(shù)問題,我們也會先搜索Oracle官方的Support網(wǎng)站,同時(shí)搜索類似StackOverFlow網(wǎng)站,這些網(wǎng)站往往有更加全部的技術(shù)問題解決文章。
搜索技術(shù)文章,那么國外的技術(shù)網(wǎng)站相對來說更加全面,而對于百度這塊相對弱,很多國外技術(shù)網(wǎng)站內(nèi)容甚至都搜索不到,這時(shí)候可以嘗試Google或Bing搜索。
在前期我們實(shí)施Oracle SOA項(xiàng)目的時(shí)候,遇到了將服務(wù)封裝和注冊接入到OSB后,客戶端消費(fèi)和調(diào)用服務(wù)出現(xiàn)消息報(bào)文內(nèi)容被截?cái)嗟膯栴}。
由于該問題出現(xiàn)概率不高,并且消費(fèi)端系統(tǒng)本身有重試機(jī)制,也暫時(shí)不影響到具體的OSB服務(wù)運(yùn)行和使用。雖然到現(xiàn)在為止,造成該問題的原因究竟是客戶端服務(wù)器配置,負(fù)載均衡,網(wǎng)絡(luò),報(bào)文本身,OSB套件本身的Bug缺陷等哪方面還沒有最終確認(rèn),但是整個(gè)問題排查和分析過程還是有意義的。
在問題排查和分析過程中,對于各類超時(shí)時(shí)間的含義,OSB的一些關(guān)鍵配置,報(bào)文解析,Http Post報(bào)文發(fā)送長短連接,Tomcat的一些配置都進(jìn)行了了解,同時(shí)通過該問題的分析,也發(fā)現(xiàn)了在技術(shù)問題分析過程中的一些問題,供后面在分析問題中借鑒和參考。

客戶端發(fā)送報(bào)文到服務(wù)器端接收報(bào)文,當(dāng)前現(xiàn)象是客戶端Log日志報(bào)文是完整的,而OSB上Log的日志報(bào)文不完整。
那么究竟是客戶端,服務(wù)端,還是網(wǎng)絡(luò)傳輸過程中出現(xiàn)的問題?這個(gè)問題邊界的確定相當(dāng)重要。
實(shí)際上在幾天的問題分析和排查中對于問題邊界一直沒有最終確認(rèn),導(dǎo)致問題也一直沒有很肯定的得到定位究竟在哪里,也導(dǎo)致最終問題沒有得到明確的解決和排查。
比如上面說的消息報(bào)文不完整這個(gè)問題,要確定邊界實(shí)際上常規(guī)思路也就兩種,
一種就是修改程序代碼進(jìn)行更加詳細(xì)的日志記錄;
另外一種就是增加Trace監(jiān)控。
比如該問題可以在客戶端進(jìn)行Http或TCP Trace,同時(shí)在服務(wù)器端也進(jìn)行Http TCP Trace,通過兩邊的Trace信息才能夠最終確定問題的邊界在哪里。
但是在生產(chǎn)環(huán)境很難這樣去做,
一個(gè)是接口服務(wù)調(diào)用并發(fā)量大導(dǎo)致Trace日志的量也巨大,而且不止這一個(gè)接口服務(wù)在調(diào)用;
一個(gè)是協(xié)調(diào)的需要配合的資源也太多,很難去聯(lián)合排查和跟蹤。
故障的復(fù)現(xiàn)是我們分析和定位問題的一個(gè)基礎(chǔ),問題如果隨機(jī)偶然發(fā)生往往是最難解決的。當(dāng)你面對問題的時(shí)候,你需要定位,那就需要問題能夠復(fù)現(xiàn)才方便不斷地去Debug或Trace。

在該問題的解決過程中,由于該異常是偶然出現(xiàn),不定時(shí)而且是沒有規(guī)律出現(xiàn),所以也給我們排查問題造成了很大的麻煩。
雖然在問題排查過程中,我將出現(xiàn)問題的異常日志,和前后正常的實(shí)例都進(jìn)行了導(dǎo)出分析,對出現(xiàn)問題的服務(wù)器Server節(jié)點(diǎn)、調(diào)用方、調(diào)用時(shí)間段都進(jìn)行了分析,但是沒有明顯的發(fā)現(xiàn)出現(xiàn)問題的規(guī)律究竟在哪里。
同時(shí)該問題具有很高的隨機(jī)性,往往是第一次調(diào)用不成功,但是同樣的報(bào)文在第二次或第三次就調(diào)用成功,同時(shí)每次對于報(bào)文的截?cái)嚅L度都不相同。這導(dǎo)致很難分析具體什么場景下調(diào)用不成功。
即由于問題不能在特定的輸入條件下重現(xiàn),導(dǎo)致我們很難對問題進(jìn)行進(jìn)一步的分析和定位,也導(dǎo)致我們很難去進(jìn)行特定的跟蹤和邊界確定。同時(shí)也很難在測試環(huán)境對該問題進(jìn)行進(jìn)一步的分析,和各種參數(shù)條件修改后的測試和驗(yàn)證。
即由于問題不能在特定的輸入條件下重現(xiàn),導(dǎo)致我們很難對問題進(jìn)行進(jìn)一步的分析和定位,也導(dǎo)致我們很難去進(jìn)行特定的跟蹤和邊界確定。同時(shí)也很難在測試環(huán)境對該問題進(jìn)行進(jìn)一步的分析,和各種參數(shù)條件修改后的測試和驗(yàn)證。
以上都導(dǎo)致問題很難快速定位和分析,只能夠大范圍的場景+異常的關(guān)鍵字搜索,然后搜索到相關(guān)的可能解決方案后,一個(gè)個(gè)的去嘗試看是否能夠解決。
但是,這種方式帶來巨大的問題,即:
由于測試環(huán)境問題不復(fù)現(xiàn),我們無法在測試環(huán)境做這個(gè)事情。那么搜索到的解決方案驗(yàn)證只能夠在生產(chǎn)環(huán)境做,但是生產(chǎn)環(huán)境根據(jù)規(guī)定是絕對不允許隨意去修改配置和調(diào)整參數(shù)的。
這也正是我們看到很多大型IT項(xiàng)目上線,往往會預(yù)留3個(gè)月左右的試運(yùn)行期間的原因,在試運(yùn)行期間生產(chǎn)環(huán)境的日常運(yùn)維和配置修改不會嚴(yán)格受控管理,也方便及時(shí)分析和解決問題。
由于項(xiàng)目采用的Oracle SOA Suite 12c套件產(chǎn)品,當(dāng)前在國內(nèi)并沒有大范圍的應(yīng)用,所以如果用百度搜索基本搜索不到有用信息,改用Google或Bing很多信息也無法搜索到。
因此在該問題的排查過程中,我們基本都在Oracle Support網(wǎng)站進(jìn)行了所有相關(guān)知識點(diǎn)的排查,同時(shí)選擇各類關(guān)鍵字進(jìn)行搜索引擎的搜索,其中包括了:
Weblogic Tomcat Post Timeout KeepAliveOSB-;
長連接 超時(shí) OSB-382030;
Failed to parse XML text等。
但是并沒有搜索到完全一致的場景。
對于一個(gè)最相似的關(guān)于Failed to parse XML document的場景,我們進(jìn)行了相關(guān)的調(diào)整,即將KeepAlive設(shè)置為False,同時(shí)對Post Timeout設(shè)置為120秒,但是仍然出現(xiàn)在120秒超時(shí)時(shí)間到達(dá)后任何沒有Post到完整的請求而導(dǎo)致超時(shí)的問題。
由于無法搜索到完全類似的場景,也導(dǎo)致我們很難根據(jù)網(wǎng)上給出的方法進(jìn)行進(jìn)一步測試和驗(yàn)證。并且Oracle顧問對于該問題也只能給出進(jìn)行Tcp Trace的無用建議。

在原來問題的分析和解決中,由于搜索引擎往往會給出完全類似的場景,我們只需要根據(jù)搜索引擎給出的排查思路對問題進(jìn)行排查即可。
因此解決起來效率很高,對于具體底層原理性的內(nèi)容我們并不需要掌握和了解,只需要能夠選擇合適的關(guān)鍵字,通過搜索引擎搜索到最合適的內(nèi)容然后進(jìn)行排查即可。
但是這次問題,特殊點(diǎn)就在于搜索引擎根本無法給出完全類似的文章。這就導(dǎo)致需要基于問題提出各種合理的假設(shè),并對假設(shè)進(jìn)行逐一驗(yàn)證。
那么如何提出合理的假設(shè)?
這里就涉及到對于TCP底層協(xié)議,各個(gè)超時(shí)值的含義和原理,Tomcat Server的參數(shù)配置,OSB代理服務(wù)的解析過程,Weblogic的關(guān)鍵參數(shù)配置和含義,負(fù)載均衡策略,乃至Docker容器和IP映射等很多內(nèi)容都有技術(shù)積累才可能提出最合理的思路。
比如在排查過程中,我會想到是否需要將Tomcat 的MaxPostSize值調(diào)大的假設(shè),但是該異常是Tomcat向Weblogic Server進(jìn)行Post請求發(fā)送數(shù)據(jù),對于Tomcat 的MaxPostSize根本不會影響到這個(gè)Post請求,而只有Weblogic上的Post Size才會有影響。這個(gè)假設(shè)本身就不合理。而要快速的判斷這些假設(shè)不合理,你就必須提前有這些關(guān)鍵的基礎(chǔ)技術(shù)知識和背景積累。
包括對于Keep Alive長連接,Keep Alive的Time out超時(shí)時(shí)間設(shè)置是否會對該服務(wù)異常調(diào)用操作影響,實(shí)際上由于對Keep Alive長連接和各類Timeout的具體含義理解的并不深入,也使得很難判定究竟是否有影響,也只能是注意嘗試去排除可能性,這些也都導(dǎo)致了很難快速的定位出問題根源究竟在哪里。
這個(gè)也是解決服務(wù)接口問題的一個(gè)關(guān)鍵影響。對于接口服務(wù)運(yùn)行問題,往往涉及到業(yè)務(wù)系統(tǒng)消費(fèi)方,業(yè)務(wù)系統(tǒng)提供方,OSB服務(wù)總線,網(wǎng)絡(luò),負(fù)載均衡設(shè)備等多個(gè)相關(guān)因素和廠商。
對于一個(gè)問題的排查往往需要協(xié)調(diào)多方的資源在約定的時(shí)間相互配合才能夠完成,這些都直接導(dǎo)致排查難度很大,很難依靠個(gè)人一方力量就完成。
在原來類似大項(xiàng)目實(shí)施過程中,也經(jīng)常會遇到這些接口問題的分析和排查,往往也都是問題造成嚴(yán)重影響后,各方才會真正重視該問題,并各自協(xié)調(diào)資源形成聯(lián)合的問題排查團(tuán)隊(duì)進(jìn)行問題分析和排查,最終才能夠解決問題。
雖然截止現(xiàn)在問題沒有得到最終解決,但是整個(gè)分析過程仍然有意義,特進(jìn)行本文總結(jié)。
二、問題復(fù)盤-文件句柄打開過多
在前面已經(jīng)談到,問題分析解決后需要及時(shí)復(fù)盤,對于問題復(fù)盤不是簡單的問題解決總結(jié),而是對整個(gè)問題分析思考過程進(jìn)行梳理,包括在問題解決中究竟踩了哪些坑,走了哪些彎路,這些經(jīng)驗(yàn)教訓(xùn)對后續(xù)問題的解決有哪些參考意義等。
問題描述:服務(wù)器響應(yīng)很慢,服務(wù)調(diào)用出現(xiàn)超時(shí),接著查詢相關(guān)的錯(cuò)誤日志信息。在錯(cuò)誤日志信息里面包括了IO Exception的too many open files信息,也包括了socket receive time out的socket連接超時(shí)的信息。
在拿到這個(gè)問題后,由于原來也出現(xiàn)過服務(wù)響應(yīng)慢和調(diào)用超時(shí)的問題,所以首先排查的是應(yīng)用服務(wù)器本身的健康狀況,因此開始用jstat檢查服務(wù)器本身的cpu和內(nèi)存的使用情況,經(jīng)過檢查服務(wù)器本身完全正常。
在檢查這個(gè)后接著檢查數(shù)據(jù)庫連接池和線程池的情況,經(jīng)過檢查雖然有排隊(duì)情況,但是連接池本身都還有大量剩余,也不存在連接池超了的情況。
在這個(gè)檢查完后回到問題錯(cuò)誤日志,由于當(dāng)前有兩個(gè)錯(cuò)誤,即:
問題A文件打開過多,問題B服務(wù)連接超時(shí),那現(xiàn)在有一個(gè)關(guān)鍵問題就是究竟是A問題導(dǎo)致了問題B,還是B問題導(dǎo)致了問題A,還是A和B本身就是在同一時(shí)間導(dǎo)致的兩個(gè)本身不相關(guān)的問題,在這個(gè)時(shí)候其實(shí)并沒有完全肯定的結(jié)論。
這就導(dǎo)致我們需要從兩條問題路徑去查找問題的根源,然后再進(jìn)行總結(jié)和收斂。
要知道對于問題B連接超時(shí),Oracle官方的Support網(wǎng)站知識庫包括問題解決的6到7個(gè)場景的排查,整個(gè)排除起來是相當(dāng)困難的。
而且該問題是老服務(wù)器出現(xiàn)的新問題,而不是完全新增加服務(wù)器出現(xiàn)的問題。那必須就要考慮是否和新部署和上線的服務(wù)和應(yīng)用有關(guān)系。
現(xiàn)在回到文件打開數(shù)過多的問題,經(jīng)過基本問題查看,發(fā)現(xiàn)的就是文件句柄打開太多,那么我們要做的就是對新增加的修改和變更進(jìn)行查看,還是否存在文件句柄沒有關(guān)閉的情況。
經(jīng)過代碼的Review我們沒有發(fā)現(xiàn)這種情況。那接著很自然就是要進(jìn)一步去定位和分析究竟哪些文件句柄打開沒有關(guān)閉?
而查這個(gè)問題的方法是lsof進(jìn)行l(wèi)og數(shù)據(jù),有發(fā)現(xiàn)我們的hpunix小型機(jī)居然這個(gè)命令無法使用,沒有辦法我們先單純的調(diào)高的最大文件打開數(shù)現(xiàn)狀但是問題依然存在。
注意在這個(gè)時(shí)候我們停在這里了,沒有進(jìn)一步去想如何解決這個(gè)問題,而轉(zhuǎn)到去分析服務(wù)超時(shí)問題。
注:當(dāng)我們在進(jìn)行問題分析診斷的時(shí)候,選擇的沒有問題的標(biāo)準(zhǔn)解決路徑,不要輕易因?yàn)樽璧K而放棄,你會發(fā)現(xiàn)最終你又會回到這個(gè)關(guān)鍵路徑上。
在面對服務(wù)超時(shí)的問題的時(shí)候,我們又走了彎路,即直接根據(jù)metalink排查方式對各個(gè)問題場景進(jìn)行分析和排除,對中間件的參數(shù)和設(shè)置進(jìn)行了大量的修改,但是最終該問題還是沒有解決。
之所以說走了彎路的原因主要在于沒有很好地去分析當(dāng)前服務(wù)器出現(xiàn)服務(wù)超時(shí)的具體原因,對應(yīng)當(dāng)前場景沒有做具體的分析。
所以后續(xù)還是回到當(dāng)前場景的分析,在當(dāng)前服務(wù)器我們新增加部署了哪些服務(wù),這些服務(wù)是否需要逐個(gè)進(jìn)行排除,我們實(shí)際的服務(wù)運(yùn)行是都超時(shí),還是個(gè)別服務(wù)超時(shí)?這個(gè)服務(wù)超時(shí)究竟是發(fā)生在哪邊?具體的邊界在哪里這些問題都需要進(jìn)一步搞清楚才能有后續(xù)行動。
1)邊界確認(rèn):不是所有服務(wù)調(diào)用都超時(shí)
第一個(gè)情況就是不是所有的服務(wù)都出現(xiàn)超時(shí)的問題,主要出現(xiàn)超時(shí)的服務(wù)都是某種類型的服務(wù),然后我們對超時(shí)的服務(wù)和服務(wù)超時(shí)日志進(jìn)行排除,包括具體的防火墻設(shè)置,長事務(wù)運(yùn)行的服務(wù)本身等。
2)邊界確認(rèn):不止是新增加的服務(wù)超時(shí),老服務(wù)也超時(shí)
現(xiàn)超時(shí)的服務(wù)并不是都是新增加的服務(wù),也包括了已經(jīng)有的老的服務(wù)運(yùn)行,確實(shí)是一個(gè)很奇怪的現(xiàn)象。在服務(wù)超時(shí)設(shè)置參數(shù)都進(jìn)行調(diào)整后,繼續(xù)觀察服務(wù)器的健康狀況和錯(cuò)誤日志記錄,發(fā)現(xiàn)繼續(xù)出現(xiàn)too many files open的錯(cuò)誤。
在這個(gè)時(shí)候發(fā)現(xiàn)還是得回到too many file open這個(gè)異常日志上進(jìn)行進(jìn)一步的分析原因。

要詳細(xì)分析必須有詳細(xì)的log日志以進(jìn)行定位,到這個(gè)時(shí)候發(fā)現(xiàn)還是必須首先得安裝上lsof組件,接著就是查找資料重新在hpunix上安裝上lsof組件,組件安裝后進(jìn)行詳細(xì)的lsof日志。
然后每1個(gè)小時(shí)左右新取一次lsof的數(shù)據(jù),通過對lsof數(shù)據(jù)的分析發(fā)現(xiàn)確實(shí)存在文件句柄不斷的增加而無法釋放的情況。
到了這一步后出現(xiàn)兩個(gè)小問題分支:
一個(gè)是對lsof數(shù)據(jù)發(fā)現(xiàn)對oracle數(shù)據(jù)庫連接使用的是1524后門端口,開始懷疑是這個(gè)端口使用有問題,但是后面否定了該假設(shè);
那還是回到文件句柄問題上。
那這個(gè)問題的關(guān)鍵就是:究竟是哪些文件句柄不算在增加?
后面對lsof日志進(jìn)行詳細(xì)分析,發(fā)現(xiàn)了總是60多個(gè)文件句柄不斷的在增加和打開,這些文件句柄反復(fù)打開多次,而且file inode值也是一樣的,到了這個(gè)時(shí)候關(guān)鍵的想法就是如何通過file inode查找到具體是哪些文件?
因?yàn)樵趯?shí)際的日志記錄中只有文件的路徑而沒有文件的名稱,一時(shí)在這個(gè)地方停滯。
到了這一步,沒有辦法,要做的還是詳細(xì)的研究lsof日志中的每一個(gè)字段的具體含義,找尋如何通過file inode找尋到具體文件的方法。
那先想到的就是文件系統(tǒng)中的文件本身是否有inode的說法,后面看到通過ls命令是可以查找到每個(gè)文件的文件句柄的。那自然我們可以對所有的文件通過ls查找并導(dǎo)出inode信息,然后和lsof中的文件句柄進(jìn)行比對找到具體的文件。
根據(jù)該思路我們到處所有文件屬性信息進(jìn)行比對,最終找到文件句柄不斷打開的文件是哪些。
一看到這些文件在不斷的打開,問題根源點(diǎn)基本就找到了,這些文件都是和我們的底層一個(gè)服務(wù)組件有關(guān)系的,那么接著看打開這些文件的方法,在打開這些文件的使用完成后釋放及時(shí)進(jìn)行了資源的釋放。
經(jīng)過源代碼分析,發(fā)現(xiàn)了具體的問題就是文件不斷的打開,但是不是手工對文件句柄進(jìn)行關(guān)閉。
但是我們的生產(chǎn)環(huán)境為何沒有出現(xiàn)通用的問題,這個(gè)是和saxReader類處理文件的方法是有關(guān)系的,saxReader類對文件是會進(jìn)行關(guān)閉,但是具體時(shí)間我們并不清楚。
那生產(chǎn)環(huán)境為何沒有出現(xiàn)同樣的問題,是否是在fgc的時(shí)候會進(jìn)行回收,這個(gè)也只是一個(gè)假設(shè),暫時(shí)沒有做進(jìn)一步的驗(yàn)證,但是至少分析出對于文件的大量持續(xù)打開肯定是需要修改的。
在對代碼進(jìn)行修改后重新部署,部署完成后進(jìn)行觀察,沒有再進(jìn)一步出現(xiàn)過too many files的文件IO異常。但是接著還是繼續(xù)出現(xiàn)一些服務(wù)超時(shí)異常。
但是任何服務(wù)超時(shí)異常都不會再引起too many files打開的異常,突然發(fā)現(xiàn)剛開始出現(xiàn)的問題A和問題B,實(shí)質(zhì)是反應(yīng)出了我們當(dāng)前應(yīng)用存在兩個(gè)偏獨(dú)立的問題,雖然兩個(gè)問題間可能存在相互影響,但是問題本身偏獨(dú)立,都有各自的問題導(dǎo)致根源。
在進(jìn)一步分析服務(wù)超時(shí)問題的時(shí)候,我們對服務(wù)調(diào)用調(diào)用詳細(xì)日志數(shù)據(jù)進(jìn)行分析,發(fā)現(xiàn)大多數(shù)服務(wù)都是正常的,而僅僅是個(gè)別服務(wù)出現(xiàn)了服務(wù)調(diào)用超時(shí)的問題,那么接著還是對單獨(dú)的個(gè)別服務(wù)進(jìn)行原因查找。
由于是個(gè)別服務(wù)的問題,我們完全可以懷疑是服務(wù)提供方系統(tǒng)出現(xiàn)了問題,那么就需要對服務(wù)提供方提供的服務(wù)能力進(jìn)行原因定位和查找,最終找到了一個(gè)原因即對方的操作導(dǎo)致了對方數(shù)據(jù)庫出現(xiàn)死鎖而服務(wù)一直處于等待和鎖定狀態(tài),基于這個(gè)假設(shè)我們后續(xù)進(jìn)行了證實(shí)確實(shí)是該原因。
到這一步基本所有的問題根源點(diǎn)和原因都基本確認(rèn)清楚,通過該次問題定位,分析和解決,進(jìn)一步完善了對應(yīng)服務(wù)應(yīng)用性能和問題定位的分析和解決方法,從CPU內(nèi)存再到IO,從服務(wù)異常日志再到服務(wù)詳細(xì)的調(diào)用日志信息,基本形成了一個(gè)完整的診斷方法。
三、問題復(fù)盤-服務(wù)調(diào)用超時(shí)
最近跟蹤OSB服務(wù)運(yùn)行超時(shí),發(fā)現(xiàn)一個(gè)很奇怪的現(xiàn)場,即在調(diào)用業(yè)務(wù)系統(tǒng)的時(shí)候出現(xiàn)1500秒超時(shí)返回的情況。而在OSB本身做服務(wù)封裝設(shè)置的時(shí)候,我們會設(shè)置兩個(gè)時(shí)間,如下:
Socket Read Time out : 該超時(shí)設(shè)置為600s;
Connectoin Time out:連接超時(shí)設(shè)置為30s。
也就是說實(shí)際在OSB配置里面并沒有出現(xiàn)過1500秒超時(shí)的任何配置情況。
后面詢問業(yè)務(wù)系統(tǒng),得到的答復(fù)是業(yè)務(wù)系統(tǒng)那邊有5分鐘,即300s的超時(shí)設(shè)置。但是即使如此,也應(yīng)該是返回300s的超時(shí)錯(cuò)誤,而不是1500s。
一開始我們始終在分析,是否是300s超時(shí),重試了5次,導(dǎo)致看到的最終超時(shí)設(shè)置是1500s,因此我們將OSB的所有配置參數(shù)又全部檢查了一遍,結(jié)果沒有檢查到任何的有5分鐘的超時(shí)配置項(xiàng),同時(shí)也沒有檢查到有任何的重試次數(shù)是5次的檢查項(xiàng)。
在OSB業(yè)務(wù)服務(wù)配置的時(shí)候,確實(shí)可以配置重試,但是我們當(dāng)前的設(shè)置為:
最大重試次數(shù)為 0;
是否支持應(yīng)用程序重試,這個(gè)是true。
但是既然最大重試次數(shù)為0,即使后面的這個(gè)checkbox為true,也不應(yīng)該去進(jìn)行重試。
因?yàn)閺恼{(diào)用其它的業(yè)務(wù)系統(tǒng)的接口服務(wù)返回情況來看,都沒有發(fā)生過相應(yīng)的重試操作。同時(shí)后續(xù)在和業(yè)務(wù)系統(tǒng)測試的過程中,將該checkbox取消選擇,同樣會發(fā)生1500秒的錯(cuò)誤,因此暫時(shí)確定和該參數(shù)關(guān)系并不大。
后面詳細(xì)查看日志,會發(fā)現(xiàn)整體過程為:
2018-10-24 11:25:38開啟調(diào)用
Oct 24, 2018 11:35:49,172 AM GMT+08:00 報(bào)600s掛起
Oct 24, 2018 11:50:46,140 AM GMT+08:00 報(bào)Connection Reset
在600s即到了我們設(shè)置的Read out timeout的實(shí)際,會報(bào)出如下異常信息:
WatchRule: (log.severityString == 'Error') and ((log.messageId == 'WL-000337') or (log.messageId == 'BEA-000337'))
?WatchData: MESSAGE = [STUCK] ExecuteThread: '7' for queue: 'weblogic.kernel.Default (self-tuning)' has been busy for "610" seconds working on the request "Workmanager: SBDefaultResponseWorkManager, Version: 0, Scheduled=false, Started=true, Started time: 610222 ms
?", which is more than the configured time (StuckThreadMaxTime) of "600" seconds in "server-failure-trigger". Stack trace:
? ? java.net.SocketInputStream.socketRead0(Native Method)
這個(gè)異常是和600s超時(shí)相關(guān)的一個(gè)異常信息,即Socket Read timeout,同時(shí)在報(bào)出這個(gè)異常信息后,該線程在60秒后又進(jìn)行了一次重試,即:
<[STUCK] ExecuteThread: '7' for queue: 'weblogic.kernel.Default (self-tuning)' has been busy for "670" seconds working on the request "Workmanager: SBDefaultResponseWorkManager, Version: 0, Scheduled=false, Started=true, Started time: 670227 ms
?", which is more than the configured time (StuckThreadMaxTime) of "600" seconds in "server-failure-trigger". Stack trace:
而后續(xù)在日志中搜索不到線程7的相關(guān)記錄,同時(shí)在間隔900秒后出現(xiàn)Connection Reset的報(bào)錯(cuò)。
javax.ws.rs.ProcessingException: java.net.SocketException: Connection reset
at org.glassfish.jersey.client.internal.HttpUrlConnector$3.run(HttpUrlConnector.
即初步分析很可能的原因是服務(wù)調(diào)用本身在5分鐘在業(yè)務(wù)系統(tǒng)端超時(shí)了,但是業(yè)務(wù)系統(tǒng)端沒有對連接進(jìn)行處理或關(guān)閉,導(dǎo)致在OSB側(cè)這個(gè)連接被并沒有感知到。
因此一直等待到600s的時(shí)候出現(xiàn)超時(shí),而這個(gè)時(shí)候超時(shí)本身不是檢測到業(yè)務(wù)系統(tǒng)端出現(xiàn)問題的超時(shí),或其它原因?qū)е逻@個(gè)thread被stuck,連接被掛起。因此又等待了900秒后出現(xiàn)了連接重置。
基于上面的分析我們進(jìn)一步查找900s相關(guān)的設(shè)置,在Weblogic的DataSource連接池里面有一個(gè)900s收縮頻率的設(shè)置,該900s的含義為:在收縮為滿足需要而增大了的連接池前需等待的秒數(shù)。如果設(shè)置為 0,則將禁用收縮。而現(xiàn)在這個(gè)值我們設(shè)置為900s。
進(jìn)一步查找資料,找到進(jìn)一步信息為:
在Weblogic Server日志中可以觀察到大量的Connection for pool "SCDS" closed信息,表示系統(tǒng)在某一時(shí)刻會批量關(guān)閉一批連接,一般斷掉物理連接會這么做(WebLogic 配置池收縮也會這么做,如果未配置的話默認(rèn)為900s檢查一次,從您的配置文件發(fā)現(xiàn)未配置池收縮)。從線程名稱看,是應(yīng)用程序的線程關(guān)閉了連接。
即在600s連接出現(xiàn)掛起的時(shí)候,一直等待900s,在weblogic連接池檢查和收縮的時(shí)候才將該連接正式關(guān)閉和回收掉,從而返回Connection Reset的錯(cuò)誤。
該假設(shè)還沒有得到進(jìn)一步的驗(yàn)證,但是從整個(gè)過程和日志分析來看,基本能夠說得通。
在該問題分析中,我們最大的一個(gè)錯(cuò)誤分析就是根據(jù)300s和1500s想當(dāng)然的判斷是重試了5次導(dǎo)致,而一直在查找為何會進(jìn)行重試,而對重試配置進(jìn)行檢查和驗(yàn)證。
簡單來說就是前面的假設(shè)本身就是錯(cuò)誤的,但是驗(yàn)證假設(shè)上走太多彎路。因此還得重新回到問題本身。
前面分析了在600秒出現(xiàn)線程阻塞和掛起的時(shí)候,再等待了900秒出現(xiàn)連接超時(shí),因此從時(shí)間上看是1500秒超時(shí)。
為了印證這個(gè)假設(shè),我們將Read Time out的時(shí)間修改為400秒,那么就應(yīng)該是1300秒報(bào)出服務(wù)超時(shí)的異常錯(cuò)誤,但是最終測試的結(jié)果仍然是1500秒超時(shí)。
因此前面這個(gè)假設(shè)不成立。
對于該超時(shí),在OSB集群側(cè)沒有任何5分鐘超時(shí)的設(shè)置,而檢查F5負(fù)載均衡的超時(shí)配置文檔可以看到,F(xiàn)5負(fù)載均衡設(shè)備上有一個(gè)idle time out的超時(shí)設(shè)置,默認(rèn)就是300秒。
任何問題的診斷分析,往往無法提出明確合理的假設(shè)時(shí)候,仍然需要回歸到問題產(chǎn)生過程和鏈路,然后通過分而治之的方式確定具體的問題點(diǎn)和邊界。
因此為了解決該問題,首先還是要確定是否和負(fù)載均衡設(shè)備有關(guān)系。對于當(dāng)前的服務(wù)調(diào)用,需要通過經(jīng)過ESB服務(wù)集群,業(yè)務(wù)系統(tǒng)的服務(wù)集群,才能夠完成。如下:

即整個(gè)服務(wù)請求的調(diào)用過程是為1->2->3->4的順序,需要同時(shí)經(jīng)過1和3兩個(gè)負(fù)載均衡設(shè)備。那么整個(gè)服務(wù)調(diào)用超時(shí)就和1,2,3,4四個(gè)節(jié)點(diǎn)的配置都會相關(guān)。
因此為了進(jìn)一步進(jìn)行驗(yàn)證,我們嘗試對如下路徑直接調(diào)用以排查問題:
不走業(yè)務(wù)系統(tǒng)的負(fù)載均衡 1-2-4路徑調(diào)用
在該模式下通過管控系統(tǒng)和通過SOAPUI分別進(jìn)行調(diào)用測試。發(fā)現(xiàn)整體調(diào)用能夠成功,有成功的實(shí)例返回。
對于通過管控調(diào)用的時(shí)候會出現(xiàn)有重試的現(xiàn)象,但是通過SOAPUI調(diào)用的時(shí)候沒有發(fā)現(xiàn)重試。
其次對于客戶端調(diào)用仍然會出現(xiàn)5分鐘調(diào)用超時(shí),返回Connection Reset的錯(cuò)誤。但是這個(gè)時(shí)候?qū)嶋H上服務(wù)仍然還運(yùn)行,即2-4的連接仍然在運(yùn)行并能夠成功運(yùn)行完,因此可以看到成功的服務(wù)運(yùn)行實(shí)例數(shù)據(jù)。
不走ESB和業(yè)務(wù)系統(tǒng)兩邊的集群,走2-4直接進(jìn)行接口服務(wù)調(diào)用
在該模式下,我們通過SOAPUI對接口服務(wù)進(jìn)行調(diào)用測試,能夠成功調(diào)用,有成功的實(shí)例返回,同時(shí)對于客戶端也可能得到成功的返回信息。
即既有成功的實(shí)例,客戶端也返回成功。即我們希望達(dá)到的一個(gè)結(jié)果。
走兩邊的集群模式 1-2-3-4路徑進(jìn)行調(diào)用
這個(gè)即是最初的調(diào)用模式,我們還是使用SOAPUI進(jìn)行調(diào)用,發(fā)現(xiàn)會出現(xiàn)調(diào)用重試,同時(shí)最終服務(wù)運(yùn)行失敗,報(bào)1500秒的超時(shí)錯(cuò)誤。
在客戶端也會報(bào)出連接超時(shí)錯(cuò)誤。即和我們最初看的現(xiàn)象是一致的。但是具體為何會發(fā)起5分鐘后的重試,以及是否該重試是由負(fù)載均衡設(shè)備發(fā)起的暫時(shí)不明確。
在負(fù)載均衡上面,我們看到有tcp_tw_recycle參數(shù)配置,但是暫時(shí)不確定自動觸發(fā)重試是否和該參數(shù)的設(shè)置有關(guān)系,從網(wǎng)上文章來看是不建議對該參數(shù)配置進(jìn)行啟用。
該超時(shí)問題經(jīng)過分析,基本確定是負(fù)載均衡的超時(shí)設(shè)置引起的。因此解決就簡單的,即對兩個(gè)集群對應(yīng)的負(fù)載均衡超時(shí)設(shè)置都進(jìn)行調(diào)整,同時(shí)確保該超時(shí)時(shí)間>OSB服務(wù)配置中的Read Time out時(shí)間即可。
最終問題得以解決。
四、JVM內(nèi)存溢出問題分析
網(wǎng)上有很多關(guān)于Java JVM內(nèi)存溢出的問題和解決方案,實(shí)際上對于這類問題已經(jīng)屬于一種很常見的問題,已經(jīng)形成了一種標(biāo)準(zhǔn)的問題解決和診斷方法論。
最佳方法就是按照這個(gè)步驟去診斷,而不是靠你自己經(jīng)驗(yàn)去提出各種假設(shè),因?yàn)樵谶@種情況下你提出的假設(shè)很可能都是瞎猜,反而浪費(fèi)了大量時(shí)間。
既對于問題在你沒有足夠經(jīng)驗(yàn)的時(shí)候一定遵循通用方法論步驟去解決。
再回到內(nèi)存溢出問題,這個(gè)問題通用步驟如下:

到了這里,我們基本回歸到通用問題解決方法論。
由于是生產(chǎn)環(huán)境問題,而且由于是商用產(chǎn)品,我無法在測試環(huán)境重現(xiàn),也無法進(jìn)行靜態(tài)代碼檢查。因此首先還是需要進(jìn)行Java GC的內(nèi)存回收日志分析。
對于JVM內(nèi)存溢出問題詳細(xì)分析可以參考:《從表象到根源-一個(gè)軟件系統(tǒng)JVM內(nèi)存溢出問題分析解決全過程》。(http://blog.sina.com.cn/s/blog_493a84550102z8m1.html)
五、業(yè)務(wù)系統(tǒng)性能問題分析診斷
如果一個(gè)業(yè)務(wù)系統(tǒng)上線前沒有性能問題,而在上線后出現(xiàn)了比較嚴(yán)重的性能問題,那么實(shí)際上潛在的場景主要來自于以下幾個(gè)方面:
業(yè)務(wù)出現(xiàn)大并發(fā)的訪問,導(dǎo)致出現(xiàn)性能瓶頸;
上線后的系統(tǒng)數(shù)據(jù)庫數(shù)據(jù)日積月累,數(shù)據(jù)量增加后出現(xiàn)性能瓶頸;
其它關(guān)鍵環(huán)境改變,比如我們常說的網(wǎng)絡(luò)帶寬影響。
正是由于這個(gè)原因,當(dāng)我們發(fā)現(xiàn)性能問題的時(shí)候,首先就需要判斷是單用戶非并發(fā)狀態(tài)下本身就有性能問題,還是說在并發(fā)狀態(tài)才存在性能問題。
對于單用戶性能問題往往比較容易測試和驗(yàn)證,對于并發(fā)性能問題我們可以在測試環(huán)境進(jìn)行加壓測試和驗(yàn)證,以判斷并發(fā)下的性能。

對于詳細(xì)的業(yè)務(wù)系統(tǒng)性能問題分析和診斷可以參考:《業(yè)務(wù)系統(tǒng)性能問題診斷和優(yōu)化分析》。(http://blog.sina.com.cn/s/blog_493a84550102z8t2.html)
以上是對于技術(shù)類問題分析和解決的一些思考和總結(jié),供大家參考。
- END -
?推薦閱讀? 系統(tǒng)架構(gòu)性能優(yōu)化思路 Kubernetes 的這些原理,你一定要了解 tcpdump抓包利器:從網(wǎng)絡(luò)獲取原始數(shù)據(jù) 入侵Linux服務(wù)器,黑客慣用手法:提權(quán) Kubernetes主流網(wǎng)絡(luò)方案:Flannel 網(wǎng)絡(luò)分析 大白話理解Session和Cookie是什么? 一文搞懂藍(lán)綠發(fā)布、灰度發(fā)布和滾動發(fā)布
點(diǎn)亮,服務(wù)器三年不宕機(jī)

