paulwong

application server 下的任务异步/并行执行方案

在application server下,比如常见的weblogic,glassfish,jboss等,由于javaee规范的要求,一般不容许直接启动线程。因此在常见的异步/并行任务执行上,会遭遇到比普通javase程序更多的麻烦。

典型例子,在javase中,jdk1.5后就引入了java.util.concurrent包,提供Executor这个非常好用的框架,完美的满足一下典型需求:

1. 同步变异步
请求进来后,将请求封装为task,交给Executor执行,原线程可以立即返回
2. 并行执行
请求进来后,将请求拆分为若干个task,例如下发短信,有100个收件人就可以按照每个收件人一个task来执行,这样可以通过Executor来并行执行这些请求,远比循环执行要快的多。

3. 等待任务结束
有时有要求调用线程必须等待所有任务完成后再继续运行的需要,此外还有超时等细节设置要求。

而在application server,为了避开自己启动线程的弊端,只好通过其他的方式来完成类似的功能。

目前我们的项目开发中主要有三种实现方式:

1. jms queue

通过jms来实现异步和并发,然后自己通过编码方式完成调用线程等待所有任务执行成功。

这个方案比较通用,因为jms是javaee的标准,所有的application server上都支持。因此天然具有跨application server的能力。

缺点就比较多了,首先jms是需要实现串行化的,因此对task是有要求,不能串行化的类是不能传递的。另外串行化的性能损失比较大,造成性能和稳定性问题,这个在大压力下比较突出,基本我们目前在考虑放弃这个方案,而且逐步将原有的实现替换掉。
这个方案还有另外一个缺点,配置麻烦,维护困难:需要创建jsm queque, connection factory, MDB等,如果系统中使用的多了,配置起来很罗嗦,修改时容许出错。

2. commonj work manager

这个是weblogic和WebSphere上支持的一个很实用的解决方案,个人感觉使用上非常舒服,配置简单,只要在weblogic-ejb-jar.xml中间中简单配置:

    < work-manager > 
         
< name > wm/taskDistributionWorkManager </ name > 
         
< min-threads-constraint > 
             
< name > minthreads </ name > 
             
< count > 1 </ count > 
         
</ min-threads-constraint > 
         
< max-threads-constraint > 
             
< name > maxthreads </ name > 
             
< count > 100 </ count > 
         
</ max-threads-constraint > 
     
</ work-manager > 


使用时用jdni lookup到就可以使用了。功能和使用方式和executor框架很类似,同样提供future,而且提供一个非常实用的waitAll()方法论支持等待任务完成。

这个方案的性能非常好,和jms相比提升极大,运行也稳定。缺点就是不是标准,只有weblogic和WebSphere执行,在glassfish,jboss上无法使用。

3. JCA work manager

这个是JCA标准了,glassfish,jboss都支持的,和commonj work manager很像,但是,很遗憾的是没有future的支持,而且也没有类似的waitAll()方法,只能自己编码实现。
spring为glassfish提供了一个工具类"org.springframework.jca.work.WorkManagerTaskExecutor",简化了JCA work manager的使用。

JCA work manager的性能和稳定性都还不错,对比jms要好的多。


JBOSS下的配置

jboss-web.xml

<?xml version="1.0" encoding="UTF-8"?>   
<jboss-web>   
<resource-ref id="ResourceRef_1163654014164">   
      
<description>WorkManager</description>   
      
<res-ref-name>jboss.jca:service=WorkManager</res-ref-name>   
      
<res-type>org.jboss.resource.work.JBossWorkManager</res-type>   
      
<res-auth>Container</res-auth>   
      
<res-sharing-scope>Shareable</res-sharing-scope>   
      
<jndi-name>WorkManager</jndi-name>   
  
</resource-ref>      
  
  
</jboss-web>   


applicationContext.xml


<bean id="taskExecutor" class="org.springframework.jca.work.jboss.JBossWorkManagerTaskExecutor">     
        
<property name="workManagerName" value="WorkManager"/>   
        
<property name="resourceRef" value="true"/>   
    
</bean>   



从目前我们项目的使用经验上看,jms是准备要被淘汰的了(其实我是一直对jms不感冒的,尤其是在有性能要求的地方,想不出用jms的理由)。目前项目要求同时支持weblogic和glassfish,因此commonj work manager和JCA work manager刚好对应于weblogic和glassfish平台。实际使用中,是在这两个work manager上封装了一个通用的接口,然后再有commonj work manager和JCA work manager两个实现,在运行时通过判断平台来自动选择注入其中的一个。

posted on 2011-12-08 18:58 paulwong 阅读(360) 评论(0)  编辑  收藏 所属分类: 性能优化


只有注册用户登录后才能发表评论。


网站导航: