David.Turing's blog

           

          將Spring用于高并發環境的隱憂

          最近協助一些BEA客戶做調優,他們使用了Spring,出現了各種各樣的性能問題,這些問題其實都是不容易重現的,其中,我自己捕獲了一些ThreadDump,并report了給Spring JIRA。這個Case的情況是:Spring會偶然出現CPU 100%的情況,WebLogic Server崩潰,我后來分析了線程Dump,覺得是一種Lock Contention的情形,幸好,Juergen Hoeller很快給我Fixed了這個Bug:
          http://jira.springframework.org/browse/SPR-4664

          使用Java編程的同學都建議Review一下,呵呵:

          這是2.5.4以前的代碼:

          /**
          *?Cache?of?TransactionAttributes,?keyed?by?DefaultCacheKey?(Method?+?target?Class).
          *?<p>As?this?base?class?is?not?marked?Serializable,?the?cache?will?be?recreated
          *?after?serialization?-?provided?that?the?concrete?subclass?is?Serializable.
          */
          final ?Map?attributeCache? = ? new ?HashMap();

          /**
          *?Determine?the?transaction?attribute?for?this?method?invocation.
          *?<p>Defaults?to?the?class's?transaction?attribute?if?no?method?attribute?is?found.
          *?
          @param ?method?the?method?for?the?current?invocation?(never?<code>null</code>)
          *?
          @param ?targetClass?the?target?class?for?this?invocation?(may?be?<code>null</code>)
          *?
          @return ?TransactionAttribute?for?this?method,?or?<code>null</code>?if?the?method
          *?is?not?transactional
          */
          public ?TransactionAttribute?getTransactionAttribute(Method?method,?Class?targetClass)?{
          // ?First,?see?if?we?have?a?cached?value.
          Object?cacheKey? = ?getCacheKey(method,?targetClass);
          synchronized ?( this .attributeCache)?{
          Object?cached?
          = ? this .attributeCache.get(cacheKey);
          if ?(cached? != ? null )?{
          // ?Value?will?either?be?canonical?value?indicating?there?is?no?transaction?attribute,
          // ?or?an?actual?transaction?attribute.
          if ?(cached? == ?NULL_TRANSACTION_ATTRIBUTE)?{
          return ? null ;
          }
          else ?{
          return ?(TransactionAttribute)?cached;
          }
          }
          else ?{
          // ?We?need?to?work?it?out.
          TransactionAttribute?txAtt? = ?computeTransactionAttribute(method,?targetClass);
          // ?Put?it?in?the?cache.
          if ?(txAtt? == ? null )?{
          this .attributeCache.put(cacheKey,?NULL_TRANSACTION_ATTRIBUTE);
          }
          else ?{
          if ?(logger.isDebugEnabled())?{
          logger.debug(
          " Adding?transactional?method?[ " ? + ?method.getName()? + ? " ]?with?attribute?[ " ? + ?txAtt? + ? " ] " );
          }
          this .attributeCache.put(cacheKey,?txAtt);
          }
          return ?txAtt;
          }
          }
          }


          這是2.5.4 Fixed后的代碼:

          ???? /**
          ?????*?Cache?of?TransactionAttributes,?keyed?by?DefaultCacheKey?(Method?+?target?Class).
          ?????*?<p>As?this?base?class?is?not?marked?Serializable,?the?cache?will?be?recreated
          ?????*?after?serialization?-?provided?that?the?concrete?subclass?is?Serializable.
          ?????
          */
          ????
          final ?Map?attributeCache? = ?CollectionFactory.createConcurrentMapIfPossible( 16 );


          ????
          /**
          ?????*?Determine?the?transaction?attribute?for?this?method?invocation.
          ?????*?<p>Defaults?to?the?class's?transaction?attribute?if?no?method?attribute?is?found.
          ?????*?
          @param ?method?the?method?for?the?current?invocation?(never?<code>null</code>)
          ?????*?
          @param ?targetClass?the?target?class?for?this?invocation?(may?be?<code>null</code>)
          ?????*?
          @return ?TransactionAttribute?for?this?method,?or?<code>null</code>?if?the?method
          ?????*?is?not?transactional
          ?????
          */
          ????
          public ?TransactionAttribute?getTransactionAttribute(Method?method,?Class?targetClass)?{
          ????????
          // ?First,?see?if?we?have?a?cached?value.
          ????????Object?cacheKey? = ?getCacheKey(method,?targetClass);
          ????????Object?cached?
          = ? this .attributeCache.get(cacheKey);
          ????????
          if ?(cached? != ? null )?{
          ????????????
          // ?Value?will?either?be?canonical?value?indicating?there?is?no?transaction?attribute,
          ????????????
          // ?or?an?actual?transaction?attribute.
          ???????????? if ?(cached? == ?NULL_TRANSACTION_ATTRIBUTE)?{
          ????????????????
          return ? null ;
          ????????????}
          ????????????
          else ?{
          ????????????????
          return ?(TransactionAttribute)?cached;
          ????????????}
          ????????}
          ????????
          else ?{
          ????????????
          // ?We?need?to?work?it?out.
          ????????????TransactionAttribute?txAtt? = ?computeTransactionAttribute(method,?targetClass);
          ????????????
          // ?Put?it?in?the?cache.
          ???????????? if ?(txAtt? == ? null )?{
          ????????????????
          this .attributeCache.put(cacheKey,?NULL_TRANSACTION_ATTRIBUTE);
          ????????????}
          ????????????
          else ?{
          ????????????????
          if ?(logger.isDebugEnabled())?{
          ????????????????????logger.debug(
          " Adding?transactional?method?[ " ? + ?method.getName()? + ? " ]?with?attribute?[ " ? + ?txAtt? + ? " ] " );
          ????????????????}
          ????????????????
          this .attributeCache.put(cacheKey,?txAtt);
          ????????????}
          ????????????
          return ?txAtt;
          ????????}
          ????}


          但是2.5.4 snapshot是未經很好測試的版本,客戶一般不太敢用。
          我不知道其實有多少客戶真正地把Spring投入到高并發性環境下使用,
          如果有,他們應該會能碰到我所碰到的情形。

          posted on 2008-04-19 09:47 david.turing 閱讀(11100) 評論(21)  編輯  收藏 所屬分類: BEA新聞頻道

          評論

          # re: 將Spring用于高并發環境的隱憂[未登錄] 2008-04-19 10:03 BeanSoft

          沒錯,我們以前的公司也是Weblogic + Hibernate,出了性能問題了,雖然調整解決了,但不管怎么說,因為這些開源軟件之前開發的時候并沒有考慮高并發和集群的情況,還是比較容易出現問題的,尤其是沒有經過嚴格的壓力測試。我個人認為,目前做的比較好的軟件,依然是商業的。  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-19 10:56 flyisland

          1. 的確,許多開源軟件在版本發布之前是否有能力去做高并發壓力測試,值得懷疑
          2. 我想,看到david這次發現的問題,用戶更應該當心的是下次如果某個開源產品出了問題怎么辦。
          3. 不過目前來說是,不是“用不用開源的問題”(可以是一定要用),而是“用了開源怎么辦”的問題。所以這是開源商業模式的一個機會,比如david就可以去做開源調優的服務支持了,哈
          4. 這篇blog在我的firefox 3.0b5下排版混亂,不得不到IE下面來留言  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂[未登錄] 2008-04-19 17:43 af

          我一直認為開源代碼是用來學習的,實際項目中還是不要用的好。  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂[未登錄] 2008-04-19 21:08 lj

          我很想知道在spring1.2.9或spring2.0.8有沒有這個問題  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-20 13:24 GoGo

          @flyisland
          FireFox不僅僅3.0亂,2.0也亂,于是IE留言之。  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂[未登錄] 2008-04-20 18:29 Roy

          @af
          你能擔保自己寫的就可以解決掉所有問題?  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-21 18:05 雨奏

          我用FireFox 2.0.0.13瀏覽本文再正常不過了  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-21 21:44 YuLimin

          new HashMap();

          這個本來就不是線程安全的東東。。。
          在寫高并發程序的時候用腳后腳想想就知道了。。。:)  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-22 10:16 david.turing

          說的沒錯,任何優秀的產品都會有Bug,但除非用的場景足夠多和復雜,否則某些嚴重的Bug還是會隱藏的很深。  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂[未登錄] 2008-04-22 12:36 allenny

          你們以為Websphere,Weblogic就不會有這樣的問題了嗎??  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-22 17:41 MarkDong

          @af
          呵呵,我是不同意最好不用開源軟件的觀點。商業軟件也同樣會有問題,只不過可能隱藏的更深而已。自己寫就更不用說了,重復造輪子不說,也許還造的不如人家的圓。
            回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-22 17:49 邢紅瑞

          這算是歷史的遺留問題吧,spring應該使用java5的api重寫了,至少還和guice有一。,開源問題太多了,以前經常和spring team的人扯皮一些小問題,不過我寫代碼的質量遠不如他們寫的  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-23 11:01 david.turing

          邢總說對了,佩服。不過,廠家的支持會讓Spring更適用于商業化。未來的WebLogic版本(Essex),應該對Spring有較大的優化,包括:

          1, 基于Spring的部署方式實現本地WLS部署,我們可以將Spring Module實現weblogic.application.Module接口,即可讓Spring模塊享受WebLogic的2階段部署的特性。

          2, 一些預配置的Beans能夠無需Spring配置聲明即可注入到Spring應用中,applicationContext看上去會簡潔很多

          3, 為Beans提供scope,比如ClusteringScope,以便支持集群技術

          4, WebLogic Consle展示Spring應用的RuntimeMBeans

          5, 9.2之后WLDF可以用于上面的RuntimeMBeans,可以定期抓取Runtime信息了

          6, Spring應用默認支持OpenJPA作為持久層支持,當然,KODO、Hibernate切換也是簡單的
            回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-23 13:34 MyUser

          修正后的代碼,滿足不了之前的并發需求吧??  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-23 13:59 Qulong

          是呀,第一段代碼能夠保證同一個TransactionAttribute只被創建一次,而第二段代碼無法做到吧?有可能同一個TransactionAttribute被創建多次,然后被put到map中覆蓋。  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂[未登錄] 2008-04-24 13:05 david.turing

          1.5的concurrent hashmap產生的用途就是為了hashmap同步  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-04-24 18:55 Qulong

          @david.turing

          確實是同步,但是它只能保證多個線程在同時操作Map時保證同步,但是上面的第一段代碼是分兩步來操作Map的  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂[未登錄] 2008-05-18 17:59 wfeng007

          朋友是bea的? 。。。 我們公司用了spring作了框架 然后再weblogic中 redeploy的話似乎spring的context沒有被銷毀。 我們已經在listener中調用了context的關閉銷毀方法但是似乎沒啥效果。。。 redeploy多次后java heap就不行了。。。 不知道其他地方有沒有這種情況?????  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2008-06-14 09:49 david.turing

          典型的部署期泄露,這種有可能是Spring的Bug,它可能沒有正確實現J2EE的context銷毀接口。  回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂 2009-06-17 14:14 ufo

          (web server軟件)UFO不會出現一個字節的內存泄漏和一個線程的不能回收,使用UFO做Web Server的好處是網站能做得很穩定,永遠也不會自己down掉;UFO在托管機房丟包率很高、遭受Hacker攻擊、互聯網 骨干網被黑等惡劣的環境條件下仍然能很好地運行;UFO在對付Hacker方面(防Hacker弄down和Hacker抓取不該訪問的資源)也有足夠措施。
          另外,UFO幾乎不會進行垃圾回收,消耗CPU很少,在普通的PC Server上用UFO運行網站,平時CPU占用率<0.1%,最多時也不會超 過5%。您知道,JVM的垃圾回收會導致大量的運算,消耗很多CPU,從而導致Server的負載能力和響應速度下降。UFO在對象管理方面采 用了很好的機制和算法,做得很出色。用UFO運行網站,可以一直保證高負載能力,快速的響應速度和低CPU消耗。發布網址:www.gm365.com
            回復  更多評論   

          # re: 將Spring用于高并發環境的隱憂[未登錄] 2014-05-14 17:12 南云

          @flyisland
          我也混亂了,ff29.  回復  更多評論   

          導航

          統計

          常用鏈接

          留言簿(110)

          我參與的團隊

          隨筆分類(126)

          隨筆檔案(155)

          文章分類(9)

          文章檔案(19)

          相冊

          搜索

          積分與排名

          最新隨筆

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 舒城县| 西华县| 伊金霍洛旗| 宜兰市| 伊吾县| 临桂县| 台南市| 乐山市| 永和县| 灌云县| 崇仁县| 长顺县| 新宾| 密云县| 娱乐| 乌拉特中旗| 张家口市| 田阳县| 余庆县| 永福县| 忻城县| 枣阳市| 浮梁县| 淳安县| 普兰店市| 铁岭县| 晴隆县| 醴陵市| 乌拉特中旗| 巩留县| 长沙市| 岗巴县| 南乐县| 子洲县| 苏州市| 宁阳县| 遂宁市| 玛曲县| 化德县| 山东省| 金乡县|