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 阅读(11084) 评论(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)

相册

搜索

积分与排名

最新随笔

最新评论

阅读排行榜

评论排行榜