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

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            210 隨筆 :: 1 文章 :: 320 評論 :: 0 Trackbacks
          <2010年9月>
          2930311234
          567891011
          12131415161718
          19202122232425
          262728293012
          3456789

          常用鏈接

          留言簿(34)

          隨筆檔案

          淘寶同學的blog

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

           

          Author:放翁(文初)

          Date: 2010/9/9

          Email:fangweng@taobao.com

          圍脖: http://t.sina.com.cn/fangweng

                   有段時間沒有更新技術blog了,現在有空每天都寫寫圍脖,記錄生活和工作的點滴,但是有時候發現有些技術的想法和工作總結沒有像過去那么完整的寫很大一篇,但是也有零零散散的不少點滴,因此想著隨意的寫這么一個連續的片段分享。

                   為什么叫做代碼背后的點滴呢,其實在現在互聯網應用來說,其實用什么語言,用什么平臺有些場景有影響,但已經不是絕對重要的因素的,其實代碼被后的設計思想才是最重要的。而用最熟悉的方式去表現最自然的想法,那才能做到游刃有余,就好比我向華黎同學申請這次內部獎勵的獎品希望是手寫筆,因為不論什么畫圖工具用起來都會妨礙我的順暢的表達,最終我把注意力集中到了畫本身上,而丟失了應有的靈感(在沒有拿到畫筆前,最近先少畫點圖)。寫代碼也是一樣,不要被流行,高性能,有潛力這些詞搞丟了自己的目標,工具就是工具,能廣泛的去學,但不必要廣泛的用,把干活要用的那門搞熟練了再說。廢話不多說,言歸正傳。

                   先說一下,以下的任何觀點都必須“對癥下藥”,沒啥萬能靈丹妙藥,怎么用,什么時候用是關鍵。

          聚與散:

                   場景一,在分享時談到Jetty7整個體系架構就一個線程資源池,為什么?整個jetty體系都是事件驅動模式(包括系統事件:NIO事件,包括業務事件:request suspend),我們系統很多時候有很多資源池(線程,DB,服務)等等,有些是直接的,有些是間接的(依賴于第三方包引入的)。我們要求每個開發者都要給資源池設置上限,給隊列限定長度,防止崩潰,但是當這些資源池散落在各個地方的時候,那么每一個資源達到邊界以前,總和可能已經超過了系統負載能力,那么一樣會奔潰。因此資源的統一規劃看來有必要,那可以考慮將物理隔離變成邏輯隔離(防止業務干擾,同時可以根據權重模型來動態分配和預留資源),另一方面整個小團隊到大團隊,對于連接池的使用抽象出來以后,那么多個依賴都可以在抽象的基礎上公用一個物理資源池,如果引入第三方緩存,都可以將資源策略應用到多機上。回過來,這里就要說聚和散的關系,聚能夠在一定程度上有全局觀,同時可以根據策略模型來調配資源,散最大的好處就是業務隔離,互相不會影響。最近在寫一個線程資源分配的小東西,目標就是:資源可根據權重模型分配。(權重模型兩條:設定某一類key獲取資源可以保留一定資源獨享,設定某一類key獲取資源最大上限可控)其實這就是一個邏輯隔離資源的最基本的雛形。

                   場景二,有同學談起他們的系統屬于消耗CPU類型的應用,特別是對數據庫獲取到的數據作處理和渲染的時候,問有啥好的建議。我的建議是數據獲取端沒啥可優化的話,考慮數據存入端增加數據預處理功能,將數據處理的壓力分攤到更多也數據存入方。當然這個代價是數據的可復用性降低,業務邏輯侵入到其他系統,數據存入方性能會有所下降(如果這三點不足以影響業務流程和數據存入方,那么可做)。其實這是聚和散的另一種表現,很多時候需要考慮的是全局優化,而不是局部優化。

                   場景三,我今天看了淘寶Tair的作者若海介紹關于tair的一些設計思路的文章,這和早先我做memcached的客戶端思想是有共同點的。首先就是數據的獲取從服務端中轉路由變成了客戶端獲得配置本地hash選擇目標服務器,這就將服務路由的功能客戶端化了,避免了單點的瓶頸,也提高了訪問性能。同時metadata的數據保存在configserver中,以弱依賴的方式主動推送,避免客戶端受到影響。但他的設計里面數據同步是服務端做的,而我當年是客戶端做的(隊列中異步備份),當時一方面是沒有辦法去改服務端的memcached,另一方面服務端能有多少,客戶端有多少,這些壓力的分攤,天然的緩解服務端的壓力,將壓力分散在了客戶端上。(代價就是客戶端工作多一點,即時性依賴于各個客戶端的能力)這也是一種散。

              場景四,TOP大數據請求的處理設計,TOP的目標是什么?大壩。主要職能:限流,授權,路由。那么路由這件事情必須在TOP走么,數據交互和平臺校驗是否一定要在一個流程中完成,其實不然,安全校驗通過以后可以頒發會話標識和目標服務地址,直接由客戶端和服務端交互。(這其實就和很多分布式文件系統或者分布式緩存系統一樣的考慮,校驗通過后就直接建立數據通道進行數據交互,避免性能受到影響),這了的聚和散就涉及到業務的耦合性分析決定流程的聚散,改善性能和穩定性。

             場景五,上面談到過最近自己實現資源分配邏輯隔離的這樣的資源池,現在就是做線程池,起初不想用jdk自帶的線程池,原因是自帶線程池啟動線程后,線程只要在coresize內就不會被銷毀,也沒有調度來從池里面獲取執行任務,完成任務放回到資源池,這些線程就輪訓的去獲取隊列的數據。好處在于沒有一個manager的管理性能和復雜度會提升,缺點就是資源分配就無法實現。其實這就是典型的一種資源分配有一個實例主導還是由多個消費者自己主導的設計。

          強弱依賴

                   場景:JettyContinuation設計中,從調用suspend方法來懸掛起請求,避免退出容器service方法reqres被回收,到業務主動調用complete結束請求流程,回寫數據到客戶端,都采用產生事件,異步的由核心線程池去執行。這樣業務線程池的線程生命周期就不會受到系統線程輸出快慢或者容器本身壓力的影響。這種弱依賴能夠極大的解耦兩個系統的服務能力和穩定性。

          關鍵先生

                   場景一,有同學一個壓力測試報告中做了這么一個測試,整個事務處理流程第一步驟消耗CPU,第二步驟消耗IO。整體事務時間=CPU 10 ms + IO 90 ms。他考慮是否能夠通過縮短IO消耗時間或者CPU消耗時間來提升RT時間,就能夠提升TPS。他的結果是IO提升一倍TPS沒有任何變化,CPU消耗時間提升一倍,TPS翻番。這能說明啥,說明瓶頸在CPU上,提升一倍,那么處理能力增加一倍(就好比資源池內的資源生命周期縮短,被利用的次數更多),大部分請求都在CPU上排隊等待處理,因此系統的TPS取決于瓶頸資源的處理能力而不是簡單的縮短RT就可以改變的。(貼切的比喻就是漏斗,不論你大口朝上或者朝下,小口決定了水流量,小口就是那個關鍵先生)

                   場景二,異步化壓力測試的結果中有如下一組結果:

          容器

          請求服務

          請求處理模式

          TPS

          Nginx + Jetty

          Time.get

          同步

          1534

          Nginx + Jetty

          Time.get

          異步

          1068

          Nginx + Jetty

          User.get

          同步

          1060

          Nginx + Jetty

          User.get

          異步

          1027

                 








                 Time.get是不向后中轉服務的模擬請求,也就是服務端很快就響應給客戶端了,而user.get是真實模擬服務向后請求,有一定消耗。但看這個結果,同樣是同步和異步對比,前者性能相差很大,后者相差不大。原因?異步一定有消耗,但這個消耗占整體事務處理時間的比例是多少,這就會放大影響。而TOP大部分服務都要比user.get更加耗時,因此后面才是線上的真實狀況。(因此場景模擬是壓力測試最重要的條件),這里的關鍵先生還是要看整個事務處理的消耗點。衍生一下上次電話面試的一個北京的朋友,談到了NIO,問了他是否做過BIONIO的測試比較,什么時候用NIO最好,他還真做過四類測試,有結果,但還是沒搞明白為什么:1.傳輸數據量大,后端服務處理慢。2.傳輸數據量大,后端服務處理快。3.傳輸數據量小,后端服務處理慢。4.傳輸數據量小,后端處理速度快。結果是NIO4處于劣勢。其實BIO釋放的夠快,那么NIO的異步在整體事務處理消耗就形成了劣勢。

          生命周期

                   場景:在異步分享過程中,談到了事件驅動的架構設計。原來的流程可以分成很多個步驟,每一個步驟由于處理的需要都會申請必要的資源,不同步驟有些是共享資源,有些是不共享的,那么從資源的生命周期角度來說,一個流程中所有資源的生命周期都是一樣,就是整個業務事務的執行周期,對于在一個業務流程中各個步驟很少共享資源的狀況,或者有等待外部返回內容的情況時,切割開流程步驟,降低資源生命周期,提升資源利用率,是事件驅動的最大優勢。NIO如此,Servlet3規范如此。簡單來說就是:按需分配。另一方面我們也能看到,處理都是無狀態的,只有輸入和輸出,無狀態的處理更容易復用。看起來這種設計很好,但其實背后還是有不少問題:1.流程復雜化,原來通過程序保證流程的順序和依賴,現在切割以后,如何驅動,順序如何保證,如何容錯,都給系統增加很大的維護成本。2.異步化后消耗時間必然增大,不論是通過pull主動獲取狀態變更還是通過push被通知到,都在性能和時間消耗上會有一些付出。因此還是需要依環境而定。

          瓶頸移動

                   場景一,還是一個同學的優化的案例,當時談到有個叫做最佳線程數,但是覺得在優化過程中最佳線程數是在變化的,其實很多時候當你覺得任何的結論是不穩定的,那么你需要進一步深入挖掘下去。其實之所以最佳線程數這個值不穩定,關鍵在于我們如何去優化,找到問題所在。和前面舉例一樣,系統可以看成各種類型資源池的一個整體,然后由很多個步驟不斷申請資源來完成各個步驟,最后返回結果。那么說白了,哪個資源池是流程中處理能力最弱的,他就是問題所在,就是瓶頸,但是通過優化后這個瓶頸可能有所改善,與此同時另一個資源成為漏斗的小口,這時候,瓶頸移動了,優化的預期結果和提升的瓶頸點性能就不吻合了。所以,把帶寬,CPUIO,外部服務,DBWeb線程池等等都看成資源池,找到瓶頸所在去優化,同時在優化過程中不斷修正優化目標,達到最后全局優化的效果。

                   場景二,異步化分享中談到一點,異步化要有全局觀。例如A依賴與B系統,組成了一個完整的服務體系,通過異步化方式將A的資源生命周期縮短到只有A處理的時間,B的資源生命周期就是B的處理時間,此時如果A是原有整個系統的瓶頸,那么就會產生雪中送炭的效果,系統整體性能會有所提高(大部分情況下,原因看后面介紹)。如果B是瓶頸,那么就是雪上加霜,更多的水流被放進來,如果B自身沒有流量控制,那么系統就會處于不健康的狀況,甚至奔潰,系統的整體RT時間可能會增加(A節省的時間小于B增加的時間)。因此這就是系統間瓶頸移動產生的影響,同樣也和一個系統一樣,考慮優化一定是整體性的考慮,否則問題不會朝著預期的方向解決。

                   晚了晚了,休息,希望這些對大家有幫助。

          posted on 2010-09-09 02:05 岑文初 閱讀(4299) 評論(8)  編輯  收藏

          評論

          # re: 代碼背后的點滴 2010-09-11 15:38 xzs
          你每天這是在晚上12點以后睡覺,早上9點前去公司嗎?  回復  更多評論
            

          # re: 代碼背后的點滴 2010-09-11 15:39 xzs
          你每天都是在晚上12點以后睡覺,早上9點前去公司嗎?  回復  更多評論
            

          # re: 代碼背后的點滴 2010-09-11 16:18 xzs
          使用java -Xmx2048啟動jvm
          請教一個問題,jvm創建一個線程時,線程本身使用的內存是該jvm的內存(2G范圍內)還是操作系統的內存?
          如果是使用操作系統的內存,為什么?這一點有點疑惑。  回復  更多評論
            

          # re: 代碼背后的點滴 2010-09-11 23:10 岑文初
          線程的棧數據是jvm自己的,要不然也不會有啟動太多線程導致OOM或者棧溢出的問題了@xzs
            回復  更多評論
            

          # re: 代碼背后的點滴 2010-09-11 23:11 岑文初
          一般盡量不會太晚,超過12點太多時間,早晨6點多肯定起來了@xzs
            回復  更多評論
            

          # re: 代碼背后的點滴 2010-09-13 21:47 楊國利
          @岑文初,
          文初,我回答一下這個網友的問題:
          使用java -Xmx2048啟動jvm
          請教一個問題,jvm創建一個線程時,線程本身使用的內存是該jvm的內存(2G范圍內)還是操作系統的內存?
          如果是使用操作系統的內存,為什么?這一點有點疑惑。

          首先第一點:ivm創建線程是使用了自己的內存,但是同時你也得注意,JVM在給這個線程分配資源的時候,操作系統本身也會分配一定的空間。舉個例子:大家都知道windows給一個進程分配的最大內存空間是2G,當我們在jvm中最大分配了2G的空間,線程增多,空間不夠用,是jvm的空間不夠,有的時候還真不是,是操作系統分配的空間不夠了。這個也得慢慢調試和增加tomcat的進程數量來解決這個問題。就說這么多。
            回復  更多評論
            

          # re: 代碼背后的點滴 2010-09-14 20:26 xzs
          你不收郵件嗎?  回復  更多評論
            

          # re: 代碼背后的點滴 2010-09-16 17:04 君楚
          關于異步和資源自制的思路。我最近也在思考。

          覺得以前設計的系統都是死的。一個高可靠的系統,就像一個生物體一樣。簡單說就是自治的。需要做到自治,就涉及到很多隔離和資源管理的機制。恰恰,異步能解決交互方面的自治問題。像你提到的邏輯隔離,物理統一的思路,就是另一種解決資源自治的方式。

          穩定的系統,內部應該是有自己的脈搏的,不會因為外界的影響而打亂。思路有些混亂,有感而發。博主總結的很好,對我啟發很大,計劃后續了解一下Jetty架構。  回復  更多評論
            


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


          網站導航:
           
          主站蜘蛛池模板: 吉安县| 甘孜| 商丘市| 苗栗县| 麻栗坡县| 寿光市| 合江县| 祁门县| 昭通市| 香格里拉县| 莆田市| 安陆市| 万盛区| 宜良县| 镇沅| 昌乐县| 莆田市| 双鸭山市| 镇赉县| 大安市| 定陶县| 铅山县| 汤原县| 嘉兴市| 霍林郭勒市| 鹤岗市| 搜索| 罗定市| 昌邑市| 温泉县| 井冈山市| 汉中市| 平顶山市| 肥西县| 奎屯市| 邳州市| 建宁县| 滁州市| 将乐县| 漳浦县| 桓台县|