<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    放翁(文初)的一畝三分地

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      210 隨筆 :: 1 文章 :: 320 評論 :: 0 Trackbacks
     

    應用架構設計“防火”經驗分享

    Author : 岑文初(淘寶花名:放翁)

    Email: fangweng@taobao.com

    Blog: http://blog.csdn.net/cenwenchu79

    Date: 2009-08-26

             剛從阿軟到淘寶不久,現在主要負責TOP平臺的技術框架設計,同時要肩負“救火”和“防火”的工作,也需要培養團隊的同學能夠有“防火”意識,減少“救火”次數,因此今天下午花了一點時間,也沒于寫任何的PPT,就直接將自己想的起來的一些自己認為應用架構設計“防火”知識做了一下事例分享,這里也想記錄下來給更多的同學分享一下,當然很多都是老生常談的常識,但是有時候不經意就會忘記一些血的教訓。

    資源是有限的

    著火點:

    系統設計的時候總是估摸不到會有大數據量從遠端傳輸過來(例如處理Http請求時,對于大附件內容的處理,全部裝載到內存,結果資源耗盡。從搜索引擎或者DB或者緩存里面拉數據,沒有分頁,結果內存被吃盡。Socket無限建立連接,結果linux的文件句柄被耗盡。)

    防火點:

    對業務場景中資源的分配與申請需要做到上限控制,以及達到上限以后的邏輯處理(排隊,丟棄,告警)。可以采取一些滑動窗口設計來將不需要過多處理的內容分段直接送入下一個處理管道中。

    依賴是未知的

    著火點:

    事務中嵌入對于第三方系統的請求(例如在數據庫操作時去發送郵件或者緩存獲取內容,結果連接池資源被Hold,導致系統不可用)。默認依賴系統會給出結果,如果出現異常就反復重試,結果對方被壓垮,自己也犧牲。

             防火點:

             對于第三方系統的依賴能夠異步的就采用異步方式,能夠從主流程中剝離的就剝離。同時設計好容錯的機制,采用本地時效性緩存減少對對方的壓力和依賴。最重要的就是注意系統間的死鎖,申請了一套資源處理業務邏輯,結果由于遠端系統的不可用,導致本地資源的無法釋放,最后擊垮自己系統。

    線程安全與性能

             著火點:

             對于線程不安全的對象處理一定要小心,否則業務出現異常的地方其實已經離設計出現問題的地方十萬八千里,問題時常成為靈異問題,解決只有靠經驗。

             防火點:

             首先對于自己設計的類和方法需要注釋是否是線程安全的。同時明確類的使用場景,對線程安全及高性能作判斷,因為采用線程安全的對象一定會有性能損耗。最近給同學寫的一個Http消息的Lazy獲取參數,就是線程不安全的類,但是這個類只會保存在ThreadLocal中,因此不存在問題。很直觀的一點判斷是否線程安全,就看看你設計的類里面的成員變量在多線程操作時候是否會有并發問題,例如一個普通的Map,多個線程操作就會導致結果的不可估量性。

    資源釋放

             著火點:

             正常邏輯都會將IO流關閉,Socket關閉,但是異常拋出時候,沒有走到資源釋放的流程中,產生了資源泄露問題。另外,資源中可能會有內嵌資源,當內部資源被外部的對象引用,則釋放將不成功,內部資源依然會泄露。一些需要顯式回收的資源(例如ThreadLocal),如果不回收,那么下次線程被操作系統重用,則會出現莫名其妙的問題(Java的線程創建和使用依賴于操作系統的實現)。

             防火點:

             Finally的處理。需要釋放的資源要做深度檢查。需要顯式回收的資源要確保使用完畢以后被回收(異常情況也需要考慮)。

    創建與復用

             著火點:

             在以前設計Cache客戶端的時候,有同學給我建議說我對于字節數組利用可以采取復用的方式,這樣可以減少對象的申請。但是做了一下測試,這樣的重復利用其實效果不像想象的那么好,甚至還不如直接創建。

    防火點:

    Java的垃圾收集器已經在性能上有了很大的提高,同時對于對象的復用需要考慮對象復用前的初始化或者是內容重置,這些得成本及復雜度可能遠遠要高于復用帶來的優勢,因此需要根據具體的業務場景選擇復用和創建。當然對于稀缺資源采用池的方式是最好的。

    字符串處理,日志級別的選擇

             著火點:

             這兩個是小問題,但是會帶來大麻煩。首先字符串的累加是老生常談的問題,但是很多新手不以為然,當你是一個高速運轉的系統時,你就會發現1ms的延時在上千萬次調用下回被無限放大,10byte的申請,在上千萬次的請求下會帶來GC多次的操作(帶來的短暫處理停滯直接影響系統的可用性)。日志級別的隨意性會導致線上環境日志迅速膨脹,出錯難以查找,影響系統的效率。(log4j優化的再好也是要寫文件的,雖然是異步刷頁)

             防火點:

             謹慎處理字符串拼接,選擇線程安全或者不安全的兩個StringBuilderStringBuffer。日志盡量區分清楚,debugInfo,前者純粹調試,可以有海量信息,Info一般用于系統或者模塊的狀況報告。Warn通常不建議使用了。Error就把你需要的關鍵信息都打出來。附帶這里說一下對于日期對象的處理,在傳輸和保存的過程中,建議都還是采用long型,可以很好的提高性能及滿足國際化的需求。

    原子操作與并發控制

             著火點:

             對于本地的對象操作通常情況下通過鎖機制保證并發的一致性,當在設計一個對于資源訪問控制的策略時,例如集群應用處理某人每天發送短信1000條,這時候計數器保存在遠端的集中式緩存中,采用getput方式就會有并發問題,因為在應用獲得到999這個計數器值的時候,也許正有10000個請求也獲得了這個值,這樣原有的控制就失效了。

             防火點:

             其實就是一個原子操作的支持,本地數據可以通過鎖來達到原子操作,遠程依賴就需要對方系統提供原子操作接口來實現高并發下的業務處理,例如Memcached Cache提供的incr decr。結合黑名單策略,計數器可以發揮很多用途,包括及時監控告警等。

    接口實現與松耦合

             著火點:

             沒有接口提供,團隊間合作困難,無法Mock,相互之間進度影響很大。同時業務實現的修改直接影響業務調用方,使得雙方耦合性很強,系統不穩定性被放大。

    防火點:

    對外提供的服務,或者模塊間交互的服務都需要接口化。框架性代碼需要在模塊載入時考慮是否需要接口化定義,以便在不同環境可以切換不同實現提供對特殊場景的支持,同時也可以將具體實現延后交給使用者實現,使得框架更加靈活。Jdk對于xml的解析就是最好的范例。

    靈活性和性能和可維護性的折中

             著火點:

             最近看了一些同學的代碼,看到大量的使用了反射,攔截器等。但是在線上環境運行過程中就發現對于一些攔截器的配置疏漏導致系統性能大幅度降低。對于幾十個spring文件,有誰能夠很清楚和直觀的了解到這些看似靈活和無侵入性的設計。

             防火點:

             對于業務邏輯不復雜,同時場景不多變的流程采用簡單的實現,不要追求花哨的靈活性,帶來的只會是可讀性,可維護性,可用性的降低。

    要有分布式和并發的觀念,但是不要本末倒置

             著火點:

             有些同學在做設計的時候考慮的很清晰,但是就是沒有考慮集群部署的情況,結果系統上線以后出現了無法集群部署的問題。并發情況的設計也一樣,僅僅在滿足業務需求以后,對于多用戶并發操作的考慮缺失,導致系統流程錯誤。

             防火點:

             設計的時候需要適度考慮這些問題,但是是在滿足現有業務邏輯的前提下,不要為了追求分布式而分布式。

    便利性的函數與性能的沖突

             著火點:

             首先申明的是這點適用范圍有限(高速運轉的模塊)。對于String,Date等對象的便利性函數,例如正則匹配,分割,Format等等其實都會有不少的性能損耗。例如你只是需要判斷文件名最后的后綴是否滿足需求,采用了正則匹配,結果發現性能在高速運轉的情況下大大下降。

             防火點:

             高速運轉的模塊盡量采用原始方式或者半原始方式。例如上面說到的文件后綴,就用stringendwith來判斷。對于一些字符串的替換,能夠用字符串拼接就拼接。對于一些字節流的處理也可以自己根據需求來訂制的寫。總的一句話,能夠滿足的就用最低成本的方法。

    防止系統設計的完備性成為攻擊或者壓力的瓶頸

             著火點:

             在很多設計的時候,對于一些系統設計講究比較完美。例如對于對象的查詢會分本地緩存,集中緩存,DB三個階段。當對方攻擊采用不存在的資源名稱時候,這種分階段的設計反而會增加系統負荷。

             防火點:

             簡化流程的分支和層次,對于消耗性資源的訪問盡量減少或者沒有(采用黑名單本地緩存或者集中緩存的方式),同時改PullPush方式,通過控制數據變更點來通知相關系統,而非輪詢獲取更新狀態。

    多級緩存和異步緩解異構系統的瓶頸

             著火點:

             有時候設計系統時,服務提供方向我們許諾說對方系統如何高效和健壯,但是當頻繁訪問產生網絡風暴的時候,我們發現原來帶寬和網絡IO本身都會成為瓶頸。

             防火點:

    對于第三方系統的依賴,要做到松耦合就要從流程的異步化來實現。同時通過緩存的使用來達到,系統的高效性和降低對于第三方系統的依賴程度。這樣可以大大降低系統的瓶頸。

    運行期白盒化,模塊可重置

             著火點:

             系統運行起來以后就無法在知道內部的狀態,也無法對問題組件進行單獨處理,造成線上環境的不可知性和無法部分修復。不得不停機重起和看日志。

             防火點:

             模塊設計過程中考慮運行期可觀測和可重置,提高系統的模塊化程度及健壯性。

    站在用戶角度設計接口,提升系統可用性

             著火點:

             總是從自身業務體系和架構去考慮如何設計對外接口,但是發現最后用戶使用的很別扭,同時由于需求不能直接被滿足,會多次反復調用接口,導致自身系統的壓力增大。例如對于一個狀態的檢查接口,是否提供一個狀態變更通知接口就會極大降低輪詢的壓力

             防火點:

             需要從客戶角度考慮問題,設計接口,防止需求和實現脫節,導致系統壓力增加。

    懶惰有時候是件好事

             著火點:

             業務流程中很多耗時的操作在流程編排方面沒有考慮清楚,當耗時工作做完以后,發現不符合最基本的交驗,這樣就會導致系統無謂的增加了開銷。對于需要申請的資源,考慮處理流程的階段,階段性申請要優于一次申請(不過需要注意死鎖)。

             防火點:

             流程編排需要合理性,盡量將耗時的工作放到合理的位置,同時做好基礎的防攻擊輕量前端屏障邏輯,提高系統的健壯性。

    主流程和副流程隔離

             著火點:

             SIP早先的日志分析模塊中有分析日志,備份,發郵件,更新系統緩存,操作數據庫等多種操作,但是一股腦兒都被放到一個流程中,結果當郵件沒有發成功導致整個流程的失敗。

             防火點:

             把真正的主流程梳理出來,同時對于一些副流程可以考慮采用后臺異步方式完成,提高系統穩定性。

    模塊間接口交互,控制資源直接操作入口

             著火點:

             對于數據庫中的資源任何模塊不區分范圍都可以訪問,最后導致數據結構變更困難,業務對象管理混亂,模塊無法剝離獨立。

             防火點:

             模塊化設計的基本思想,模塊間通過接口交互獲得其他模塊管轄的數據,接口方式屏蔽了對于后端實現及業務對象的依賴。

    學習份外的事情,配置決定成敗

             著火點:

             沒有SA就高不定環境,也無法了解操作系統的配置與Web容器的配置對于應用的影響。沒有DBA就無法確定如何寫SQL避免一些簡單的耗時查詢。沒有測試同學就無法作壓力測試,無法了解當前系統性能。

             防火點:

             多學多問,多了解一些其他崗位的內容,才能夠更加全面的掌握好架構設計。

    不要迷信

             著火點:

             總是看到新技術如何有優點,但是看不到它的成熟度。總是聽到很多經驗之談,但是從來沒有真的比較過。結果就是別人說什么就是什么,系統地穩定性和可用性基于Google出來的結果。

             防火點:

             需要聽取各種意見和經驗,同時用測試結果說明問題的結果,看代碼說明結果背后的問題。這樣才會走得更加踏實,學的更加實際。其實技術發展來說,真正的基礎性內容還是有限的,而且各種技術都是觸類旁通。分布式,不論是文件系統,DB,緩存都會遇到分布式的共性問題(負載均攤,容錯,數據復制,動態擴容等等),在結合一些文件系統,DB,緩存的自身特質。因此扎扎實實學好基礎,了解Http協議,了解七層通信協議,了解文件系統設計,了解MapReduce思路,了解結構化,半結構化(bigmap),非結構化存儲的要點,就會不會讓自己迷失在技術宣傳中。

             明天晚上去北京參加系統架構師會議,到時候會和大家分享一下TOP的一些商業和技術上的心得,準備得很倉促,但是個人覺得分享就在于自己將自己知道的說出來,時間不長,45分鐘,能講的也不多,但是如果對于淘寶開放平臺有興趣的同學可以來聽一下。這里也算是做個廣告,不過不要期望過高,免得失望也大^_^。五年沒有來北京了,首都應該也變化不小了。

    posted on 2009-08-27 00:59 岑文初 閱讀(3183) 評論(5)  編輯  收藏

    評論

    # re: 應用架構設計“防火”經驗分享 2009-08-27 09:12 popoer
    總結得真好!  回復  更多評論
      

    # re: 應用架構設計“防火”經驗分享[未登錄] 2009-08-27 09:21 paul
    看了這篇文章很受啟迪!如果沒個tip能配上具體實例說明就更好了  回復  更多評論
      

    # re: 應用架構設計“防火”經驗分享 2009-08-27 13:02 王兵
    好文章 多謝前輩分享  回復  更多評論
      

    # re: 應用架構設計“防火”經驗分享 2009-08-30 20:03 fl1429
    很好的文章。。學習了!  回復  更多評論
      

    # re: 應用架構設計“防火”經驗分享[未登錄] 2009-09-03 12:21 cauherk
    我來談談我的看法
    1、資源是有限的
    無論是何種方式獲得數據,最好還是采用分頁。如果萬一一個bug的出現,導致成上100萬的數據查詢出來,不光是一個java進程down掉的問題,可能整片都會出問題。
    事情的發生往往都是滾雪球的效應。
    2、依賴是未知的
    對于慢速系統或者不可靠系統、未知系統,做好做成異步。
    對于優先級很高的調用,采用異步通知或者等待的模式。對于優先級很低的調用,直接采用異步,無須等待,通過監控和告警監控。
    CPU的速率很快,磁盤的轉速很慢,一個依賴于文件系統的系統永遠的速度只能達到磁盤響應的速度。
    3、線程安全與性能
    對于查詢很多,寫入很少的,還是直接采用HashMap,寫入的時候采用lock鎖
    對于寫多,讀少的,采用ConcurrentHashMap
    如果僅僅是一個線程操作這個數據,根本沒有爭用的情況,直接使用HashMap
    ThreadLocal這樣的對象,盡量不要開放給普通開發人員使用;這個對象的在線程的執行最前面,加上一個設置為空的操作,盡量減少ThreadLocal重復利用的問題。
    4、資源釋放
    原則是那里獲得那里釋放。
    無論已經做了連接池的連接還是IO對象,全部采用那里獲得那里釋放的原則。
    同時,也要考慮釋放多個資源,如果第一個出現異常,后面中斷的問題。
    5、創建與復用
    對于采用分代并行垃圾回收算法的jdk,小對象的頻繁創建和消耗對于性能的損耗幾乎看不見。
    對于沒有采用分代垃圾算法的jdk,特別是IBM的jdk,小心造成垃圾碎片,引起OOM。
    對于java的nio的網絡通訊層面,ByteBuffer是一個能極大的減少內存創建的類,可以做到zero copy對象到網絡層,極大的提高通訊的性能。
    6、字符串處理,日志級別的選擇
    對于JDK1.5或者以上版本,使用+連接多個字符串,jdk編譯器會自動采用StringBuilder進行優化,可以反編譯看。很多文章或者書上寫到,必須采用StringBuffer的方式,
    其實可以不用考慮,采用+的方式效率反而比采用StringBuffer高,StringBuffer的每個方法都采用了鎖。
    日志級別在上線初期,可能會是info,甚至debug。在穩定一段時間后,一定要恢復到采用error級別。
    我們全部采用日志輸出到cronolog的方式。
    很多地方寫日志的方法很不正確。
    log.debug("測試日志輸出"+System.getProperties());
    最好改成這樣
    if(log.isDebugEnable()){
    log.debug("測試日志輸出"+System.getProperties());
    }
    按照java的習慣,即使日志不輸出,但是字符串還是要拼接的,也對性能有損耗。
    7、原子操作與并發控制
    對于集群的并發控制,要么采用數據庫,要么采用memcached。當然memcached性價比遠遠優于數據庫。
    對于一個進程內部的并發,jdk1.5提供了很多很好使用的類。
    比如:AtomicLong,Lock等等
    8、靈活性和性能和可維護性的折中
    spring是個好東西,看到使用幾十個甚至上百個spring的配置文件,甚至在不同的目錄或者jar里面,以后的維護是件及其麻煩的事情。
    為什么不能將這樣配置統一到數據庫中,進行統一的管理和配置。
    9、要有分布式和并發的觀念,但是不要本末倒置
    這句話沒錯,但是要求所有的開發人員都要知道這么多細枝末節的東西,甚至要關系主機、數據庫和網絡環境豈不是增大了出現問題的可能性。
    開發人員需要投入絕大多數精力在業務上,而不是耗在無謂的系統怎么樣部署、怎么樣分布式上面。
    框架就是來支持這個的,給出一套開發規范,在框架上做出不符合這樣規范的寫法的錯誤捕獲和提示,都是可以實施的。
    10、便利性的函數與性能的沖突
    SimpleDateFormat就是一個典型的例子。jdk的代碼是寫的不錯,但是也有寫的很爛的代碼。
    對于時間和日期的處理,我們一直都采用joda-time,性能好的很多。
    11、多級緩存和異步緩解異構系統的瓶頸
    還是上面的話,凡是不能控制的系統,就當成不信任的系統,盡量的異步,盡量的資源限制。
    12、運行期白盒化,模塊可重置
    模塊可重置,這個是個高科技,不知道采用什么辦法做到的。
    如果是采用傳統的ejb,支持熱下線和熱上線,做做demo可以,生產系統放到一邊去。
    如果采用OSGi,本人沒接觸過。
    去年本人維護一個非常高可用的系統也對模塊可重置想過很多,一直沒有什么真正可在生產環境實施的方案。
    13、懶惰有時候是件好事
    這句話非常的好,加上一個限定條件就更好了,在有利工作的情況下懶惰。
    比如說:重復性的工作,懶得的人就會想出很多辦法提高效率。

    本人(msn:cauherk@hotmail.com)去年參與了5000萬用戶的系統的架構、設計和開發,采用了純J2EE,集群、分布式、分庫(橫向和縱向)、分表技術實施。
    平均每天的業務量達到了400萬筆交易。非常想和同道人一起討論架構和技術問題。





      回復  更多評論
      


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 巨胸狂喷奶水视频www网站免费| 黄色成人网站免费无码av| 亚洲精品无码高潮喷水A片软| 国产亚洲成av人片在线观看| 免费在线观看毛片| 2021国内精品久久久久精免费| 亚洲成av人片在线天堂无| 亚洲欧洲尹人香蕉综合| 亚洲电影一区二区三区| 久久伊人亚洲AV无码网站| 国产精品冒白浆免费视频| 99久久免费国产精品特黄| 一个人免费日韩不卡视频| 大地资源网高清在线观看免费| 免费无码专区毛片高潮喷水| 亚洲丰满熟女一区二区哦| 亚洲中文字幕无码一去台湾| 亚洲精品一区二区三区四区乱码| 亚洲Aⅴ无码专区在线观看q| 亚洲日韩小电影在线观看| 亚洲一级Av无码毛片久久精品| 四虎永久在线精品免费观看地址 | 免费A级毛片无码免费视| 久久ww精品w免费人成| 久久免费高清视频| 中文字幕一区二区免费| 久久久精品国产亚洲成人满18免费网站| 亚洲AV无码资源在线观看| 亚洲男人的天堂网站| 亚洲国产AV无码一区二区三区| 精品丝袜国产自在线拍亚洲| 亚洲AV无码乱码在线观看代蜜桃 | 女同免费毛片在线播放| 在线观看人成视频免费无遮挡| 大妹子影视剧在线观看全集免费| a毛片成人免费全部播放| 国产精品免费看久久久香蕉| 水蜜桃视频在线观看免费播放高清 | 在线观看肉片AV网站免费| 免费国产叼嘿视频大全网站| 在线观看肉片AV网站免费|