`
snowfox2008
  • 浏览: 127933 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

在Jboss集群中jBPM工作流引擎的可伸缩性及性能

阅读更多

在Jboss集群中jBPM工作流引擎的可伸缩性及性能
                                                        作者:Szymon Zeslawski
                                                        翻译:snowfox

原文地址:http://www.theserverside.com/tt/articles/article.tss?l=WorkflowEngineJBossCluster
3/2009
任务/范围
这篇文章的目的是展示怎样通过调整jBPM的配置并且利用分布式的TreeCache将它安装到一个JBoss的集群中而取得它的近乎线性的伸缩性能。读者将被按照有效地集群jBPM所需要的所有步骤所指引-从集群的安装到精确地调整jBPM配置-并且提供性能测试报告和可以取得最好性能的各种不同的小技巧和诀窍。
摘要
jBPM是一个很强大的工作流引擎-健壮的,可扩展的并且快速的。然而,如果我们需要比一个服务器能够提供的更好的性能有什么样的可能性?集群是立即涌到我们头脑中的的方案。但是它可以很快地并且很容易地切实可行么,并且更重要的是,它可以产生期望的结果么?这篇文章将带你浏览将一个分布式的TreeCache与JBoss集群一起安装,并且调整jBPM来交付它的完整的性能的整个过程。你也可以获知通过标准的配置可以获得你期望的多大程度的改进。
jBPM介绍
jBPM是一个工作流引擎,可以使它的用户轻松地管理各种不同的业务流程。这些业务流程以一个包含活动的流程定义为基础。相似或相关的活动可以被组合成scopes。活动可以在后边的执行中被手动或者定时触发。在这里JobExecutors发挥作用。它轮询数据库检查是否有工作准备好可以被执行。无论一个工作是否准备好,它的执行都是约定好的,JobExecutor在锁住当前工作然后执行任何与这个工作相关的动作。例如一个定时执行被自动升级地调用,这是本篇文章的主要焦点。
业务背景
我们已经基于一堆尖端技术包括jBPM开发了一个业务流程管理的应用。在我们系统中主要的业务单元承担名称呼叫。我们的客户将主要的关注点放在工作流引擎的伸缩性和性能上,因此我们在测试和调优集群化的jBPM配置上做了很多工作。我的目标是通过集群和调整自动化的呼叫升级来满足客户的关于安装大小和使用特性的需求。
测试环境概述
测试环境包括4台独立运行应用的机器,都连接到一个Oracle 10g的数据库。服务器上运行Ubuntu 8.10 Linux和JBoss 4.2.3GA,每一个都有一个4核的2.5GHz的Q9300 Intel处理器和4GB 的RAM内存。JBoss互相之间使用JGroups协议通信。Library的版本中文章的最后被给出。
系统准备和集群安装
为了避免jBPM的工作所有权和将来某个时候的执行发生严重的问题,必须采取以下步骤:
./etc/host需要有一个同Jboss服务器的IP地址一致的实体绑定到(这应用中集群的每个节点上)使用工作区,此处描述了示例1:
例如,InetAddress.getLocalHost() 方法返回一个非回路的地址
.所有的集群节点通过使用ntpd有它们自己的同步锁机制
JBoss集群是容易安装的,使用“all”配置(用来开发和测试目标)或者用来自“all”的下述文件来丰富你的配置都是足够了:

  •     deploy/cache-invalidation-service.xml
  •     deploy/cluster-service.xml
  •     deploy/deploy.last/farm-service.xml
  •     lib/jbossha.jar
  •     lib/jgroups.jar

Log4j的配置
我们已经确认了同我们新的安装相关的所看到的消息。下边线框内的jboss-log4j.xml将允许我们来验证集群是否正确地工作而且不会错漏我们控制台上冗长的输出:

 

<logger name="org.jboss.cache">
    <level value="INFO"/>
  </logger>
  <!-- Keep chatty support libraries as silent as possible -->
  <logger name="org.jgroups.protocols.UDP">
    <level value="ERROR"/>
  </logger>
  <!-- Limit the org.jgroups logger to WARN as its INFO is verbose -->
  <logger name="org.jgroups">
    <level value="WARN"/>
  </logger>
  <!-- Clustering logging -->
  <logger name="org.jgroups">
    <level value="INFO" />
  </logger>
  <logger name="org.jboss.ha">
    <level value="INFO" />
  </logger>

 

定时启动JBoss
现在我们可以利用./run.sh –c<config_name> -b<bind_address>命令来启动JBoss了。在控制台上你应该可以看到与这些相似的信息:

12:48:23,783 INFO                jgroups.JChannel| JGroups version: 2.4.1 SP-4
12:48:24,098 INFO                 protocols.FRAG2| frag_size=60000, overhead=200, new frag_size=59800
12:48:24,102 INFO                 protocols.FRAG2| received CONFIG event: {bind_addr=/10.10.1.43}
12:48:24,187 INFO    HAPartition.DefaultPartition| Initializing
12:48:24,232 INFO                          STDOUT|
-------------------------------------------------------
GMS: address is 10.10.1.43:46941
-------------------------------------------------------
12:48:26,335 INFO    HAPartition.DefaultPartition| Number of cluster members: 4
12:48:26,335 INFO    HAPartition.DefaultPartition| Other members: 3
12:48:26,335 INFO    HAPartition.DefaultPartition| Fetching state (will wait for 30000 milliseconds):
12:48:26,393 INFO    HAPartition.DefaultPartition| state was retrieved successfully (in 58 milliseconds)
12:48:26,449 INFO            jndi.HANamingService| Started ha-jndi bootstrap jnpPort=1100, backlog=50, bindAddress=/10.10.1.43
12:48:26,457 INFO  mingService$AutomaticDiscovery| Listening on /10.10.1.43:1102, group=230.0.0.4, HA-JNDI address=10.10.1.43:1100

 

你可以发现,一个具有4个节点的集群已经建立了,现在我们可以部署应用程序了。JBoss提供了一个农场化的机制,用来在一步中部署一个应用程序到所有的集群成员上。仅仅需要将你的EAR或者WAR扔到server/<config_name>/farm目录下,然后它会自动地被部署到所有的节点上。移除应用程序文件就会在各节点上卸载应用。
集群中的jBPM
jBPM3.2被设计为可以无缝地址集群中工作。所有的应用程序都是独立的,互相之间并不知晓。工作的分布通过数据库级别的锁和包含线程名称和主机信息的工作所有权来实现。
当一个工作准备执行并且没有被锁住,JobExecutors将会在其上放置一把锁。由于使用乐观锁,仅有他们当中的一个被完成,获得一个乐观锁异常的JobExecutors在获得新的工作之前将被暂停一段已经定义好的时间。
在多服务器环境中缓存jBPM
“二级缓存是JBoss jBPM实现的一个重要的方面。如果这个二级缓存不生效,JBoss jBPM与用其它技术实现的BPM引擎相比可能有一个验证的缺陷。”
--jBPM jPDL 用户手册
jBPM的实体类默认的是被缓存的。然而他们的缓存配置仅仅是适用本地部署的 – 他们使用一个非严格的读写策略,这是JBoss Cache所不支持的。你应该怎样将这个策略改为事务型呢?你不得不覆盖jBPM实体的Hibernate映射。通过在你的SessionFactory Bean中定义一个映射资源的列表,你可以手动实现这个功能。

<bean id="myAppSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource">
    <ref bean="..."/>
  </property>
  <property name="mappingResources">
    <list>
      <value>classpath*:com/yourcompany/jbpm/**/*.hbm.xml</value>
      <!--<value>classpath*:org/jbpm/**/*.hbm.xml</value>-->
      <value>classpath*:org/jbpm/bytes/ByteArray.hbm.xml</value>
      <value>classpath*:org/jbpm/context/def/ContextDefinition.hbm.xml</value>
      <!--<value>classpath*:org/jbpm/context/def/VariableAccess.hbm.xml</value>-->
      <value>classpath*:org/jbpm/context/exe/ContextInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/TokenVariableMap.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/VariableInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/variableinstance/ByteArrayInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/variableinstance/DateInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/variableinstance/DoubleInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/variableinstance/HibernateLongInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/variableinstance/HibernateStringInstance.hbm.xml
      </value>
      <value>classpath*:org/jbpm/context/exe/variableinstance/JcrNodeInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/variableinstance/LongInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/variableinstance/NullInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/exe/variableinstance/StringInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/context/log/VariableLog.hbm.xml</value>
      <value>classpath*:org/jbpm/context/log/VariableCreateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/context/log/VariableDeleteLog.hbm.xml</value>
      <value>classpath*:org/jbpm/context/log/VariableUpdateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/context/log/variableinstance/ByteArrayUpdateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/context/log/variableinstance/DateUpdateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/context/log/variableinstance/DoubleUpdateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/context/log/variableinstance/HibernateLongUpdateLog.hbm.xml
      </value>
      <value>classpath*:org/jbpm/context/log/variableinstance/HibernateStringUpdateLog.hbm.xml
      </value>
      <value>classpath*:org/jbpm/context/log/variableinstance/LongUpdateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/context/log/variableinstance/StringUpdateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/db/hibernate.queries.hbm.xml</value>
      <!--<value>classpath*:org/jbpm/file/def/FileDefinition.hbm.xml</value>-->
      <value>classpath*:org/jbpm/graph/action/MailAction.hbm.xml</value>
      <!--<value>classpath*:org/jbpm/graph/action/Script.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/graph/def/ProcessDefinition.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/graph/def/Node.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/graph/def/Transition.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/graph/def/Event.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/graph/def/Action.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/graph/def/SuperState.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/graph/def/ExceptionHandler.hbm.xml</value>-->
      <value>classpath*:org/jbpm/graph/exe/Comment.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/exe/ProcessInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/exe/Token.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/exe/RuntimeAction.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/log/ActionLog.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/log/NodeLog.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/log/ProcessInstanceCreateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/log/ProcessInstanceEndLog.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/log/ProcessStateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/log/SignalLog.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/log/TokenCreateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/log/TokenEndLog.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/log/TransitionLog.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/node/StartState.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/node/EndState.hbm.xml</value>
      <!--<value>classpath*:org/jbpm/graph/node/ProcessState.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/graph/node/Decision.hbm.xml</value>-->
      <value>classpath*:org/jbpm/graph/node/Fork.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/node/Join.hbm.xml</value>
      <value>classpath*:org/jbpm/graph/node/State.hbm.xml</value>
      <!--<value>classpath*:org/jbpm/graph/node/TaskNode.hbm.xml</value>-->
      <value>classpath*:org/jbpm/graph/node/MailNode.hbm.xml</value>
      <!--<value>classpath*:org/jbpm/instantiation/Delegation.hbm.xml</value>-->
      <value>classpath*:org/jbpm/job/ExecuteActionJob.hbm.xml</value>
      <value>classpath*:org/jbpm/job/ExecuteNodeJob.hbm.xml</value>
      <value>classpath*:org/jbpm/job/Job.hbm.xml</value>
      <value>classpath*:org/jbpm/job/Timer.hbm.xml</value>
      <value>classpath*:org/jbpm/logging/log/ProcessLog.hbm.xml</value>
      <value>classpath*:org/jbpm/logging/log/MessageLog.hbm.xml</value>
      <value>classpath*:org/jbpm/logging/log/CompositeLog.hbm.xml</value>
      <!--<value>classpath*:org/jbpm/module/def/ModuleDefinition.hbm.xml</value>-->
      <value>classpath*:org/jbpm/module/exe/ModuleInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/scheduler/def/CreateTimerAction.hbm.xml</value>
      <value>classpath*:org/jbpm/scheduler/def/CancelTimerAction.hbm.xml</value>
      <!--<value>classpath*:org/jbpm/taskmgmt/def/TaskMgmtDefinition.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/taskmgmt/def/Swimlane.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/taskmgmt/def/Task.hbm.xml</value>-->
      <!--<value>classpath*:org/jbpm/taskmgmt/def/TaskController.hbm.xml</value>-->
      <value>classpath*:org/jbpm/taskmgmt/exe/TaskMgmtInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/exe/TaskInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/exe/PooledActor.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/exe/SwimlaneInstance.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/log/TaskLog.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/log/TaskCreateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/log/TaskAssignLog.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/log/TaskEndLog.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/log/SwimlaneLog.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/log/SwimlaneCreateLog.hbm.xml</value>
      <value>classpath*:org/jbpm/taskmgmt/log/SwimlaneAssignLog.hbm.xml</value>
    </list>
  </property>
</bean>
 

这个列表除了被缓存的以外(他们被注释掉了)包含了所有的被匹配的jBPM实体。为了保持映射的完整性,你需要拷贝这些被注释掉的hbm.xml文件到你的项目中并且改变它们的缓存策略定义,从:

<cache usage="nonstrict-read-write"/>

到:

<cache usage="transactional"/>

在一个包中保证这些文件被定义在mappingResources列表的第一行,它们会被自动地读取。

请注意,事务型的缓存策略仅能在你的应用中与一个JTA事物管理器一起工作!你不得不定义Hibernate属性hibernate.transaction.manager_lookup_class,使它指向你的事物管理器的查找类。

使用JBoss TreeCache作为hibernate的二级缓存
对于在你的应用中准备使用TreeCache需要一些步骤但不是很长的步骤。但是不要害怕,毕竟它不是非常复杂的。首先,你不得不创建一个缓存的MBean并且在一个名称为jboss-service.xml的文件保存它的定义:

<mbean code="org.jboss.cache.TreeCache" name="jboss.cache:service=TreeCache">
  <depends>jboss:service=Naming</depends>
  <depends>jboss:service=TransactionManager</depends>

  <!-- Configure the TransactionManager -->
  <attribute name="TransactionManagerLookupClass">org.jboss.cache.JBossTransactionManagerLookup</attribute>

  <!--
      Node locking scheme :
                PESSIMISTIC (default)
                OPTIMISTIC
  -->
  <attribute name="NodeLockingScheme">OPTIMISTIC</attribute>

  <!--
          Node locking isolation level :
                 SERIALIZABLE
                               REPEATABLE_READ (default)
                               READ_COMMITTED
                               READ_UNCOMMITTED
                               NONE
 
      (ignored if NodeLockingScheme is OPTIMISTIC)
  -->
  <attribute name="IsolationLevel">REPEATABLE_READ</attribute>

  <!--     Valid modes are LOCAL
                           REPL_ASYNC
                           REPL_SYNC
                           INVALIDATION_ASYNC
                           INVALIDATION_SYNC
  -->
  <attribute name="CacheMode">REPL_ASYNC</attribute>

  <!--  Whether each interceptor should have an mbean
registered to capture and display its statistics.  -->
  <attribute name="UseInterceptorMbeans">true</attribute>

  <!-- Name of cluster. Needs to be the same for all clusters, in order
to find each other -->
  <attribute name="ClusterName">Cache-Cluster</attribute>

  <attribute name="ClusterConfig">
    <config>
      <!-- UDP: if you have a multihomed machine,
              set the bind_addr attribute to the appropriate NIC IP address
      -->
      <!-- UDP: On Windows machines, because of the media sense feature
               being broken with multicast (even after disabling media sense)
               set the loopback attribute to true
      -->
      <UDP mcast_addr="228.1.2.3" mcast_port="45566" ip_ttl="64" ip_mcast="true"
           mcast_send_buf_size="150000" mcast_recv_buf_size="80000" ucast_send_buf_size="150000"
           ucast_recv_buf_size="80000" loopback="false"/>
      <PING timeout="2000" num_initial_members="3" up_thread="false" down_thread="false"/>
      <MERGE2 min_interval="10000" max_interval="20000"/>
      <FD shun="true" up_thread="true" down_thread="true"/>
      <VERIFY_SUSPECT timeout="1500" up_thread="false" down_thread="false"/>
      <pbcast.NAKACK gc_lag="50" max_xmit_size="8192" retransmit_timeout="600,1200,2400,4800" up_thread="false"
                     down_thread="false"/>
      <UNICAST timeout="600,1200,2400" window_size="100" min_threshold="10" down_thread="false"/>
      <pbcast.STABLE desired_avg_gossip="20000" up_thread="false" down_thread="false"/>
      <FRAG frag_size="8192" down_thread="false" up_thread="false"/>
      <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true" print_local_addr="true"/>
      <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
    </config>
  </attribute>

  <!--    The max amount of time (in milliseconds) we wait until the
          initial state (ie. the contents of the cache) are retrieved from
          existing members in a clustered environment
  -->
  <attribute name="InitialStateRetrievalTimeout">5000</attribute>

  <!--    Number of milliseconds to wait until all responses for a
          synchronous call have been received.
  -->
  <attribute name="SyncReplTimeout">10000</attribute>

  <!--  Max number of milliseconds to wait for a lock acquisition -->
  <attribute name="LockAcquisitionTimeout">15000</attribute>

  <!--  Name of the eviction policy class. -->
  <attribute name="EvictionPolicyClass">org.jboss.cache.eviction.LRUPolicy</attribute>

  <!--  Specific eviction policy configurations. This is LRU -->
  <attribute name="EvictionPolicyConfig">
    <config>
      <attribute name="wakeUpIntervalSeconds">5</attribute>
      <!--  Cache wide default -->
      <region name="/_default_">
        <attribute name="maxNodes">5000</attribute>
        <attribute name="timeToLiveSeconds">1000</attribute>
        <!-- Maximum time an object is kept in cache regardless of idle time -->
        <attribute name="maxAgeSeconds">120</attribute>
      </region>

      <region name="/org/jboss/data">
        <attribute name="maxNodes">5000</attribute>
        <attribute name="timeToLiveSeconds">1000</attribute>
      </region>

      <region name="/org/jboss/test/data">
        <attribute name="maxNodes">5</attribute>
        <attribute name="timeToLiveSeconds">4</attribute>
      </region>
    </config>
  </attribute>

  <!-- New 1.3.x cache loader config block -->
  <attribute name="CacheLoaderConfiguration">
    <config>
      <!-- if passivation is true, only the first cache loader is used; the rest are ignored -->
      <passivation>false</passivation>
      <!-- <preload>/a/b, /allTempObjects, /some/specific/fqn</preload> -->
      <shared>false</shared>

      <!-- we can now have multiple cache loaders, which get chained
      <cacheloader>
          <class>org.jboss.cache.loader.FileCacheLoader</class>
          <properties>
              location=/tmp/cacheFileStore
          </properties>
          <async>false</async>
          <fetchPersistentState>true</fetchPersistentState>
          <ignoreModifications>false</ignoreModifications>
          <purgeOnStartup>false</purgeOnStartup>
      </cacheloader>
      -->

      <cacheloader>
        <class>org.jboss.cache.loader.JDBCCacheLoader</class>
        <properties>
          cache.jdbc.table.name=jbosscache
          cache.jdbc.table.create=true
          cache.jdbc.table.drop=true
          cache.jdbc.table.primarykey=jbosscache_pk
          cache.jdbc.fqn.column=fqn
          cache.jdbc.fqn.type=varchar(255)
          cache.jdbc.node.column=node
          cache.jdbc.node.type=blob
          cache.jdbc.parent.column=parent
          cache.jdbc.driver=oracle.jdbc.driver.OracleDriver
          cache.jdbc.url=jdbc:oracle:thin:@[hostname]:1521:orcl
          cache.jdbc.user=[username]
          cache.jdbc.password=[password]
        </properties>
        <async>true</async>
        <fetchPersistentState>false</fetchPersistentState>
        <ignoreModifications>false</ignoreModifications>
        <purgeOnStartup>false</purgeOnStartup>
      </cacheloader>
    </config>
  </attribute>
</mbean>

然后将这个文件按照下边的结构打包到一个SAR文档中(本质上就是一个仅仅有不同名字的ZIP):
jbosscache.sar/
   - META-INF/
      - jboss-service.xml
你还要在你的EAR的根目录中打包这个SAR,并且在你的META-INF/jboss-app.xml中增加下面的行:

<module>
   <service>jbosscache.sar</service>
</module>

在启动或后边使用的过程中,你可能会遭遇到任何序列化的问题,你可以通过增加下边的java虚拟机选项来将JBoss的序列化改为标准的Java序列化:
-Dserialization.jboss=false
为了能够使用JBoss Cache作为它的缓存提供者(假设你使用Maven来构建)你的应用程序应该依赖于下边的配置:

<dependencies>
  <dependency>
    <groupId>org.jboss.cluster</groupId>
    <artifactId>hibernate-jbc-cacheprovider</artifactId>
    <version>1.0.1.GA</version>
    <exclusions>
      <exclusion>
        <groupId>hibernate</groupId>
        <artifactId>hibernate3</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-common</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-jmx</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-system</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-j2ee</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-transaction</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-jbosscache</artifactId>
    <version>3.3.1.GA</version>
    <exclusions>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-cache</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-system</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-common</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-minimal</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jboss</groupId>
        <artifactId>jboss-j2se</artifactId>
      </exclusion>
      <exclusion>
        <groupId>concurrent</groupId>
        <artifactId>concurrent</artifactId>
      </exclusion>
      <exclusion>
        <groupId>jgroups</groupId>
        <artifactId>jgroups-all</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
</dependencies>

注意:其中被排除的项目用来阻止与我们项目中已经包含或者JBoss自己提供的的libraries的发生版本不匹配。你可能必须为你的应用来手动调整他们。

如果没有使用Maven,你不得不手动地去下载这些提到的libraries并且把它们包含进的类路径。

现在是时候配置Hibernate的缓存了。有几个选项就足够了:

hibernate.cache.provider_class=org.jboss.hibernate.jbc.cacheprovider.JmxBoundTreeCacheProvider
hibernate.treecache.mbean.object_name=jboss.cache:service=TreeCache
hibernate.cache.use_second_level_cache=true
hibernate.cache.use_query_cache=false
hibernate.transaction.manager_lookup_class=<your_transaction_manager_class>

做完了所有这些事情,你就可以通过@Cache标记它们来缓存你的实体类了。记住集群的TreeCache仅仅支持只读型的和事务型的缓存策略。
你最新创建的缓存能够通过两种方式来监控 – 通过我们已经创建的Hibernate统计模块或者通过TreeCache JMX MBean。
使用Hibernate统计,需要在你的POM中增加一个附加的依赖:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-jmx</artifactId>
    <version>3.3.1.GA</version>
</dependency>

为了使能统计收集和输出,下述配置必须放进你的Spring Context文件中:

<bean id="jmxExporter"
      class="org.springframework.jmx.export.MBeanExporter">
  <property name="beans">
    <map>
      <entry key="Hibernate:name=statistics">
        <ref local="statisticsBean"/>
      </entry>
    </map>
  </property>
</bean>

<bean id="statisticsBean" class="org.hibernate.jmx.StatisticsService">
<property name="statisticsEnabled">
  <value>true</value>
</property>
<property name="sessionFactory">
  <ref local="hibernateSessionFactory"/>
</property>
</bean>

此处的“hibernateSessionFactory”是session factory Spring bean的ID。通过这个改变,Hibernate统计模块就可以通过JMX使用了。
你可以通过完全合格的名称(被称为二级缓存区域),输入比率,命中和失效次数来监控缓存实体,从而验证缓存是否按照期望的工作了。经过正确地缓存jBPM的片刻操作之后,应该会取得一个很高的命中/失效比率,就像从JConsole屏幕上看到的:

调整jBPM的性能
jBPM按照默认的配置是可以运行的,但是这样仅提供了它潜在性能的一小部分。下边这张图演示了呼叫升级时间是怎样随着后继节点的增加而下降的。在我测试的包含1000个呼叫的场景中,每一个都自动地升级2次 – 这导致总共2000个升级。每个升级都导致数据库更新。所有的结果都可以作为例证,在不同的测试条件下会有一些波动。

我们一直在寻找取得近乎线性的伸缩性能。线性伸缩性,是与服务器资源相关的,这意味着对于一个稳定的负载,随着资源的增加,性能是以一个稳定的比率来提高的。

左边的图表基于线性加速度显示了真实的呼叫升级时间与理论时间之间的比较。右边的图表比较了真实的加速度与理论的线性加速度。

这些结果通过标准的jBPM配置来收集的 –  1个JobExecutor线程,10秒的idleInterval ,1个小时的maxIdleInterval ,这意味着仅仅显示在默认的安装情况下jBPM是怎样来衡量的。这并不坏,但加速的因素会更高。

现在,让我们来对配置文件做点小改动。jBPM有两个名称为idleInterval 和maxIdleInterval 的选项,都我们是有意义的。当一个异常被JobExecutor扔出时,它暂停一段在idleInterval 定义的时间,然后按照双倍时间来增加直到它到达maxIdleInterval。对我们来说不幸的是,每次一个乐观锁冲突被探测到后都会扔出一个StaleObjectStateException ,这种情况在伴随着很多的并发JobExecutors试图获取一个工作而发生的很频繁。为了取得一个更高的并发比率降低idleInterval 和maxIdleInterval两个值是很重要的。下边是将idleInterval 降低到500毫秒和将andmaxIdleInterval 降低到1000毫秒的结果:

左边的图表显示了基于线性加速度的真实的呼叫升级时间与理论的时间之间的比较。右边的图表比较了真实的加速度与理论的线性加速度。

你可以看到现在加速曲线已经很接近于最佳了。让我们看看,如果我们将整个时间曲线向下转换。为了增加工作流引擎的吞吐量,你可以改变每个机器的JobExecutor线程的数量。我已经执行了与以前一样的性能测试,但是这次仅在4个节点上增加线程的数量并且尝试打开或关闭缓存。缓存增加了15-23%的吞吐量,但是最重要的获得是来自于增加线程的数量:

这终于使我们获得一些真正的高性能。随着使能缓存将线程数量增加到20个,吞吐量与标准的配置相比突飞猛进到了340%。

我们可以看到这每个机器上跑20个线程,我们已经达到了饱和点,进一步增加这个值不会产生更好的结果了。如果数据库处在来自于应用程序的其它部分的稳定负载下,缓存可能会带来更光明的前景。
结论
你已经看到了一个完整的对jBPM集群和调优的研究。最终的判断是jBPM是一个非常有效率的工作流引擎,为了获得最好的效果它仅需要调整正确的配置。通过增加3个服务器和调校jBPM的配置,与一个服务器的默认安装相比我们能够增加16倍的吞吐量。如果你需要增加你的工作流的吞吐量,我建议你按照下边的顺序进行修改:

  •     增加JobExecutor线程的数量
  •     增加缓存降低数据库负载
  •     按必要增加更多的服务器

一个必须被记住的事情是 – 数据通常迟早在某些点上是一个性能瓶颈。毕竟,jBPM是基于Hibernate的。因此,如果你的努力没有带来期望的效果,考虑调整/集群你的数据库。

链接

  •     Sun's documentation on InetAddress.getLocalHost() issues: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037
  •     JBoss clustering HOWTO: http://docs.jboss.org/jbossas/jboss4guide/r4/html/cluster.chapt.html
  •     jBPM user guide – caching: http://docs.jboss.com/jbpm/v3.2/userguide/html_single/#secondlevelcache
  •     Hibernate second-level cache: http://www.hibernate.org/hib_docs/reference/en/html/performance-cache.html
  •     TreeCache reference: http://www.jboss.org/file-access/default/members/jbosscache/freezone/docs/1.4.0/TreeCache/en/html_single/index.html#d0e2066
  •     JBoss Cache configuration options: http://www.jboss.org/community/docs/DOC-10265
  •     Hibernate JBossCache provider: http://www.jboss.org/community/docs/DOC-12948

Library versions
jBPM-jPDL: 3.2.2
Hibernate: 3.3.1
JBoss: 4.2.3GA
TreeCache: 1.4.1SP9
Hibernate JBossCache provider: 1.0.1GA

关于作者
Szymon Zeslawski目前作为Consol 咨询及解决方案的高级开发人员,开发尖端的业务解决方案。毕业于在波兰克拉科夫的AGH科技大学,他从2002年开始就从事java技术。他擅长的专业领域集中于三年的专业经验,覆盖了不同的web,手机和人机工程学和性能方面的企业技术。When not at computer, he stretches his mind and body training Chuo Jiao or chases ghosts from behind the steering wheel.(这一句,偶实在不会翻译了)。

  • 大小: 12.3 KB
  • 大小: 15.2 KB
  • 大小: 15.1 KB
  • 大小: 5.5 KB
  • 大小: 8 KB
2
1
分享到:
评论

相关推荐

    基于JBOSS_jBPM工作流技术的改进和应用

    基于JBOSS_jBPM工作流技术的改进和应用基于JBOSS_jBPM工作流技术的改进和应用

    JBossjBPM.rar_工作流引擎

    在提供的压缩包中,“JBossjBPM.nh”可能是一个笔记或文档文件,详细介绍了在实际项目中使用JBoss jBPM的经验和技巧。通常,这种文件会涵盖以下内容: 1. 安装与配置:如何在本地环境中设置jBPM,包括依赖库的导入和...

    jbpm 工作流引擎

    ### jBPM 工作流引擎关键知识点解析 #### 一、概述 - **jBPM**:全称为 JBoss Business Process Management,是由 JBoss 开发的一款开源工作流管理系统。该系统提供了高度灵活且易于扩展的功能,适用于各种复杂的...

    jbpm和shark工作流引擎对比.doc

    本文对jbpm和shark工作流引擎进行了深入的比较,涵盖了稳定性、易用性、灵活性、可监管性、扩展性和可维护性等多方面。工作流引擎是企业信息化建设中非常重要的一环,选择合适的工作流引擎对企业的业务流程和效率...

    Spring与JBoss JBPM工作流集成开发指南

    Spring与JBoss JBPM工作流集成开发指南 Spring与JBoss JBPM工作流集成开发指南是关于JBPM与Spring框架集成的开发文档,涵盖了工作流的基本概念、设计要点、常见问题解答、参考资源等。下面是从该文件中生成的相关...

    工作流引擎开发包jbpm

    jbpm工作流引擎是一款强大的开源工作流管理系统,用于构建企业级的应用程序,它允许开发者定义、执行和管理工作流程。jbpm的核心功能包括流程定义、流程实例管理、任务管理和监控。这款开发包是针对版本3.2.GA的,这...

    Java四大主流开源工作流引擎分析Shark,osworkflow,jbpm,jflow

    Java平台上的工作流引擎在企业级应用中扮演着至关重要的角色,它们帮助企业自动化业务流程,提高效率。本文将深入分析四个主流的开源工作流引擎:Shark、osworkflow、jbpm和jflow。 首先,Shark是Enhydra项目的一...

    jboss --JBPM介绍及应用

    JBoss jBPM(Job Business Process Management)是JBoss提供的一个强大的工作流引擎,它支持业务流程管理和自动化,尤其适用于Java环境中开发WS(Web Services)应用。本文档主要介绍了如何使用JBoss jBPM图形化流程...

    工作流框架JBoss+jBPM+jPDL用户开发手册

    《工作流框架JBoss+jBPM+jPDL用户开发手册》是针对企业级应用中工作流管理系统设计与实现的一份详尽指南。本手册旨在帮助开发者深入理解和运用JBoss、jBPM以及jPDL这三者之间的协作,构建高效的工作流程自动化解决...

    JBoss jBPM

    JBoss jBPM 是一个复杂的可扩展的工作流管理系统. JBoss jBPM 有直观的流程语言来表示商业流程图的术语比如,任务,异步通讯的等待状态,定时器,自动操作等等.把这些操作绑在一起,, JBoss jBPM 就有了最强大和易扩展性...

    jbpm 5.0 jboss jbpm 5.0 最新版jboss jbpm 5.0 最新版jboss jbpm 5.0 最新版jboss jbpm 5.0 最新版

    jboss jbpm 5.0 最新版jboss jbpm 5.0 最新版jboss jbpm 5.0 最新版jboss jbpm 5.0 最新版jboss jbpm 5.0 最新版jboss jbpm 5.0 最新版jboss jbpm 5.0 最新版

    工作流程JBPM工作流管理

    JBPM(JBoss Business Process Management)是Red Hat公司推出的一款开源的工作流管理系统,它提供了一整套解决方案,包括工作流引擎、流程设计工具、监控和管理功能,帮助企业实现业务流程的标准化和自动化。...

    jbpm工作流详解

    【jbpm工作流详解】 工作流(Workflow)是业务过程在计算机环境下的自动化体现,...在企业级应用中,jBPM常被用于OA(办公自动化)、CRM(客户关系管理)、ERP(企业资源规划)等系统中,构建复杂且可扩展的业务流程。

    使用JBoss ESB和JBPM实现垂直市场解决方案VMS

    直市场解决方案(VMS)是NAVTEQ公司中的一个机构,负责为客户提供定制的解决方案,...在本文中,我将讨论如何使用JBoss中间件平台来构建这样的系统,尤其是使用JBoss ESB和jBPM(JBoss Business Process Management)。

    jboss集群完整介绍

    在Jboss集群中,WebCluster尤为关键,它涉及到负载均衡和状态同步两个核心概念。负载均衡策略可根据具体需求选择针对每个请求或每个用户的平衡,不同策略需要匹配相应的状态同步机制。例如,基于请求的负载均衡需在...

    jboss集群技术介绍

    在现代企业级应用环境中,为了提高系统的可用性、扩展性和容错能力,通常会采用集群技术。JBoss作为一个广泛使用的开源Java应用服务器,提供了丰富的集群功能来满足这些需求。本文档详细介绍了JBoss服务器如何实现...

    JBPM工作流开发指南v10-20070706.doc

    JBPM工作流开发指南v10-20070706.doc 是一份详细的学习文档,主要针对那些想要深入理解并使用JBPM工作流引擎的开发者。JBPM(Java Business Process Management)是一个开源的工作流管理系统,它允许开发者设计、...

Global site tag (gtag.js) - Google Analytics