作者:david.turing 来源:BlogJava   酷勤网收集 2008-04-20

摘要
  最近协助一些BEA客户做调优,他们使用了Spring,出现了各种各样的性能问题,这些问题其实都是不容易重现的,其中,我自己捕获了一些ThreadDump,并report了给Spring JIRA。这个Case的情况是:Spring会偶然出现CPU 100%的情况,WebLogic Server崩溃

最近协助一些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投入到高并发性环境下使用,如果有,他们应该会能碰到我所碰到的情形。

评论
  • # re: 将Spring用于高并发环境的隐忧[未登录] BeanSoft Posted @ 2008-04-19 10:03
    没错,我们以前的公司也是Weblogic + Hibernate,出了性能问题了,虽然调整解决了,但不管怎么说,因为这些开源软件之前开发的时候并没有考虑高并发和集群的情况,还是比较容易出现问题的,尤其是没有经过严格的压力测试。我个人认为,目前做的比较好的软件,依然是商业的。
  •     
  • # re: 将Spring用于高并发环境的隐忧 flyisland Posted @ 2008-04-19 10:56
    1. 的确,许多开源软件在版本发布之前是否有能力去做高并发压力测试,值得怀疑
    2. 我想,看到david这次发现的问题,用户更应该当心的是下次如果某个开源产品出了问题怎么办。
    3. 不过目前来说是,不是“用不用开源的问题”(可以是一定要用),而是“用了开源怎么办”的问题。所以这是开源商业模式的一个机会,比如david就可以去做开源调优的服务支持了,哈
    4. 这篇blog在我的firefox 3.0b5下排版混乱,不得不到IE下面来留言   
  •  
  • # re: 将Spring用于高并发环境的隐忧[未登录] af Posted @ 2008-04-19 17:43
    我一直认为开源代码是用来学习的,实际项目中还是不要用的好。 
  •     
  • # re: 将Spring用于高并发环境的隐忧[未登录] lj Posted @ 2008-04-19 21:08
    我很想知道在spring1.2.9或spring2.0.8有没有这个问题     
  • # re: 将Spring用于高并发环境的隐忧 GoGo Posted @ 2008-04-20 13:24
    @flyisland
    FireFox不仅仅3.0乱,2.0也乱,于是IE留言之。

来自:http://www.blogjava.net/security/archive/2008/04/19/spring_bug.html

分类: Java技术 中间件技术 应用服务器

上一篇:如何用jar命令对java工程进行打包   下一篇:Spring Web Flow 2.0.0.RC1发布了,增加了对Ajax、JSF和安全的集成