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

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

          Author:放翁(文初)

          Date: 2010/9/9

          Email:fangweng@taobao.com

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

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

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

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

          聚與散:

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

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

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

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

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

          強弱依賴

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

          關(guān)鍵先生

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

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

          容器

          請求服務(wù)

          請求處理模式

          TPS

          Nginx + Jetty

          Time.get

          同步

          1534

          Nginx + Jetty

          Time.get

          異步

          1068

          Nginx + Jetty

          User.get

          同步

          1060

          Nginx + Jetty

          User.get

          異步

          1027

                 








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

          生命周期

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

          瓶頸移動

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

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

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

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

          評論

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

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

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

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

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

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

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

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

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

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

          穩(wěn)定的系統(tǒng),內(nèi)部應(yīng)該是有自己的脈搏的,不會因為外界的影響而打亂。思路有些混亂,有感而發(fā)。博主總結(jié)的很好,對我啟發(fā)很大,計劃后續(xù)了解一下Jetty架構(gòu)。  回復(fù)  更多評論
            


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 达日县| 金阳县| 梨树县| 攀枝花市| 衡阳市| 太仆寺旗| 任丘市| 荣成市| 嘉黎县| 肇州县| 通河县| 南安市| 斗六市| 治多县| 福贡县| 崇州市| 色达县| 枝江市| 绥芬河市| 横山县| 资兴市| 萍乡市| 新巴尔虎右旗| 宿迁市| 扬州市| 专栏| 德阳市| 方山县| 兴义市| 政和县| 科技| 佳木斯市| 云南省| 临海市| 当雄县| 高雄县| 霍州市| 鸡西市| 新沂市| 龙口市| 万年县|