精通面向服务的体系结构:BPEL 简明手册

第 9 部分:管理 BPEL 生产环境
作者:Stany Blanvalet

了解如何在 BPEL 生产环境中使用 BPEL 流程管理器的 API 和 Dehydration Store 自动化常见管理任务。

 查看完整的“BPEL 简明手册”索引

 

本文相关下载:
 Oracle BPEL 流程管理器

2006 年 1 月发表

一些企业将其 70% 的 IT 预算花在了维护和管理当前 IT 功能以及操作上。但矛盾的是,对于多数使用面向服务体系结构 (SOA) 的公司而言,管理 Web 服务的性能和可用性并非首要之事。因此,随着企业采用 Web 服务和 BPEL 来构建 SOA 基础架构,设计新的策略来降低应用程序管理成本变得愈加势在必行。

因为通常会将多个业务流程部署至生产环境,所以这对于 BPEL 实施而言尤其重要。随着生产环境中部署了越来越多的流程,有效管理变得日趋重要。每个已执行完毕的 BPEL 流程(无论成功与否)都存储在数据库中,在流程流的不同步骤间进行交换的每条 XML 消息也是如此。正如您可能预料到的,该流程将导致数据库大小呈几何级增长,进而导致出现性能瓶颈。

由于这些原因,在 BPEL 生产环境中,最为重要的是要能够

  • 在不影响生产系统稳定性的情况下,存档已完成 BPEL 流程的信息
  • 删除从数据库成功交付和解析的所有 XML 消息
  • 删除陈旧的实例
  • 重新运行失败的流程

在 BPEL 简明手册的这一部分中,您将了解到 BPEL 流程管理器的 API 和 Dehydration Store 是如何实行这些功能,以及如何存档和删除已完成 BPEL 流程实例的信息。您还将学习如何使用 PL/SQL 和 EJB 删除陈旧的实例、完成调用以及回调 XML 消息。最后,将介绍如何通过 BPELTest 实用程序返回失败的流程实例。

BPEL 流程管理器 API 和 Dehydration Store

Oracle BPEL 流程管理器控制台为管理、调试部署至 BPEL 服务器的流程提供了一个基于 Web 的友好界面。但是,在生产环境中,管理员需要对管理任务具有强有力的控制。通过针对 BPEL Dehydration Store 数据库的 PL/SQL 查询或 BPEL API,可以将这些管理任务中的多数进行自动化。但在创建操作项之前,了解 Dehydration Store 和 BPEL 流程管理器 API 的基础概念至关重要。

Dehydration Store 数据库用于存储流程状态数据,尤其是异步 BPEL 流程的数据。以下简要介绍了一些非常重要的表。

 

内容

CUBE_INSTANCE

实例元数据信息(创建日期、当前状态、流程 id)

CUBE_SCOPE

实例的范围数据

AUDIT_TRAIL

实例的审计跟踪信息。该信息可通过 BPEL 控制台查看。

AUDIT_DETAILS

流程实例的详细审计信息

DLV_MESSAGE

回调消息元数据

DLV_MESSAGE_BIN

回调消息的有效负载

INVOKE_MESSAGE

调用消息元数据

INVOKE_MESSAGE_BIN

调用消息的有效负载

DLV_SUBSCRIPTION

实例的交付订阅

TASK

为实例创建的任务(即标题、任务接受人、状态、有效期)


表 1:BPEL Dehydration Store 中的重要表

数据库模式位于 $ORABPEL$\integration\orabpel\system\database\scripts 目录下的 DDL 脚本 domain_oracle.dll 中。正确了解该模式后,管理员无需 BPEL 控制台就可直接针对该存储编写 SQL 查询。

除了此种 SQL 方法,管理员还可利用 BPEL 流程管理器 API。该 API 提供了一个详尽的类集,可用于在各种状态中查找、存档、删除实例,跨不同的域删除回调/调用消息,或查询特定域、流程或实例的状态。(API 文档位于 $ORABPEL$\integration\orabpel\docs\apidocs\index.html 中。)下表概述了一些相关性最强的类/接口以及相应的方法。

 

类/接口

方法

类 WhereConditionHelper

提供诸如 whereInstancesClosed()whereInstancesStale()whereInstancesOpen() 之类的方法,用于构造搜索相应实例的 where 子句。

接口 IBPELDomainHandle

允许开发人员在处于运行中的 BPEL 流程域上执行操作。提供诸如 archiveAllInstances()deleteAllInstances()d eleteInstancesByProcessId()deployProcess()undeployPorcess()deleteAllHandledCallback() 以及 deleteAllHandledInvoke() 等方法。

接口 IinstanceHandle

允许用户在活动的实例上执行操作。提供诸如 isStale()、getState()、getModifyDate() 以及 delete() 等方法。

类 Locator

允许用户搜索已在 Orabpel 流程域内部署并实例化的流程、实例以及活动。提供诸如 listInstances()listActivities() 等方法,并可接受 where 子句作为参数。


表 2:用于执行管理任务的重要类

接下来,您将学习如何执行一些最重要的管理任务。

存档已完成的实例

如前所述,所有执行成功的流程实例均存储在 Dehydration Store 中。现在,在实例完成后,一个 BPEL 实例保存在了两个表中:cube_instance 和 cube_scope。前者存储实例头信息:域、创建日期、状态(已完成、运行中、陈旧、已取消)、优先级、标题等等。后者存储实例的状态(变量值等)。默认情况下,使用这两个表存储已完成的实例。

可从 BPEL 控制台清除数据库中的实例信息,如下所示。

 


图 1:删除已完成的实例


在生产环境中,有必要在删除信息之前对其进行存档 — 需对数百个实例进行此项操作。幸运地是,您可以使用 PL/SQL 或 EJB 来达到此目的。(请记住,在将信息从 BPEL 数据库中清除之前,应将这些信息移至其他位置。)我们来看一些示例:

利用 EJB 存档。 此例将用到以下接口和方法。

com.oracle.bpel.client

接口 IBPELDomainHandle

方法摘要

int

archiveInstances(WhereCondition wc, boolean deleteInstances)

(存档所有由 wc 指定的搜索条件返回的实例)

方法 archiveInstances 将存档并删除所有已完成的实例。它接受参数 keepdays,该参数指定已完成实例在存档前可保留的天数。

                               
public static int archiveInstances(Locator locator, String processId, int keepdays)
throws ORABPELAccessException {
                try {
                        WhereCondition wc = WhereConditionHelper.whereInstancesClosed();
                        WhereCondition tmpWhere = new WhereCondition();
                        NonSyncStringBuffer buf = new NonSyncStringBuffer();
                        if (!"*".equals(processId)) {
                                buf.setLength(0);
                                tmpWhere.setClause(buf.append(" AND ").append(
                                                SQLDefs.AL_ci_process_id).append(" = ?").toString());
                                tmpWhere.setString(1, processId);
                                wc.append(tmpWhere);
                        }
                        Calendar cal = Calendar.getInstance();
                        cal.add(Calendar.DATE, -keepdays);
                        buf.setLength(0);
                        tmpWhere.setClause(buf.append(" AND ").append(
                                SQLDefs.AL_ci_modify_date).append(" <= ?").toString());
                        tmpWhere.setTimestamp(1, cal.getTime());
                        wc.append(tmpWhere);
                        IBPELDomainHandle domain = locator.lookupDomain();
                        return domain.archiveInstances(wc, true);
                } catch (ServerException se) {
                        throw new ORABPELAccessException(se.getMessage());
                }
        }
        
                            

利用 PL/SQL 存档。 管理员还可利用 PL/SQL 来达到此目的。在将记录从数据库中删除之前,DBA 可根据自身需要将记录移至其他数据库/表。下面就是一个例子:

                               
Query:  SELECT CIKEY FROM CUBE_INSTANCE, DOMAIN
        WHERE BPELMNG.CUBE_INSTANCE.DOMAIN_REF = BPELMNG.DOMAIN.DOMAIN_REF
        AND BPELMNG.DOMAIN.DOMAIN_ID = 'XXX'            ?XXX is the name of the domain
        AND STATE = 5                                   ?'5' mean COMPLETED
        AND MODIFY_DATE < SYSDATE-X                  ?X is the number of day of history 
        AND PROCESS_ID = 'XXX';                 ?XXX is the process name
        
Actions:
        For each CIKEY, call COLLAXA.delete_ci(CIKEY); oracle procedure.

                            

在接下来的部分中,您将学习如何删除回调和调用消息。

删除回调和调用消息

只要实例要求从合作伙伴获得消息( receiveonMessage 等),就针对该特定接收活动发出订阅。一旦接收到交付消息,交付层就会试图将消息与预定的订阅相关联。成功订阅的消息将继续保留在数据库中。这些消息可通过 collaxa.delete_ci(CIKEY) 存储过程删除(操作如同前述的实例存档)。

所有的回调和调用 XML 消息均是如此。即使已成功解析和交付这些消息,它们仍会保留在数据库中。

可以使用以下方法删除所有回调、调用以及订阅。

com.oracle.bpel.client

接口 IBPELDomainHandle

方法摘要

int

deleteAllHandledCallback ()

(删除所有交付至此域的、已成功解析和交付的回调消息)

int

DeleteAllHandledInvoke ()

(删除所有交付至此域的、已成功解析和交付的调用消息)

int

DeleteAllHandledSubscription ()

(删除此域中所有已成功解析和处理的消息订阅者 — 即已向其交付了消息)

还可以使用 PL/SQL 来执行相同的操作,如以下存储过程所示。建议 DBA 将该脚本用作模版,并对其进行修改来满足特定的存档需求(删除、移至另一数据库、更改 where 子句以包含基于业务条件的选择,等等)。

                               
/**
* Procedure to clean invocation messages for a particular domain.
* Invocation messages are stored in invoke_message and invoke_message_bin table 
* It will select all the invocation messages from invoke_message table.
* For each message which has been delivered or resolved, delete it from
* invoke_message and invoke_message_bin table
*/      
procedure delete_invoke( p_domain_ref in integer ) 
as
cursor c_invoke_message is
select message_guid
from invoke_message
where ( state = 2 or state = 3 )
          and domain_ref = p_domain_ref
for update;
          begin
                   for r_invoke_message in c_invoke_message loop
                           delete from invoke_message_bin where message_guid = r_invoke_message.message_guid;
                   delete from invoke_message where current of c_invoke_message;
                        end loop;
                        commit;
        end delete_invoke;

    
/**
* Procedure to clean callback messages for a particular domain.
* Callback messages are stored in dlv_message and dlv_message_bin table 
* It will select all the invocation messages from dlv_message table.
* For each message which has been delivered or resolved, delete it from
* dlv_message and dlv_message_bin table
*/      
procedure delete_callback( p_domain_ref in integer ) 
as
cursor c_dlv_message is
select message_guid
from dlv_message
where ( state = 2 or state = 3 )
                and domain_ref = p_domain_ref
for update;
begin
for r_dlv_message in c_dlv_message loop
delete from dlv_message_bin
where message_guid = r_dlv_message.message_guid;
delete from dlv_message 
where current of c_dlv_message;
                end loop;
                commit;
end delete_callback;

                            

接下来,简要介绍一下如何删除陈旧的实例。

删除陈旧的实例

使用 BPEL 控制台可识别所有陈旧的实例,并按如下所示将其清除。

 


图 2:在 BPEL 控制台中清除陈旧的实例


遗憾地是,只能针对特定域搜索到这些陈旧实例。但在生产环境中,可能要跨不同域部署多个流程,从而造成沉重的管理负担。此外,在一次性清除特定域内的所有陈旧实例不可能做到。

作为一种替代方式,可以通过该 SQL 查询查找每个陈旧实例的特有多维数据集实例键 (cube instance key, cikey)。

SELECT CUBE_INSTANCE.cikey, CUBE_INSTANCE.root_id, CUBE_INSTANCE.process_id,            
CUBE_INSTANCE.domain_ref
FROM CUBE_INSTANCE 
WHERE STATE = 9

一旦识别出了每个陈旧实例的 cikey,就可使用该键通过调用由 SQL 查询找到的每个多维数据集实例 delete_ci(CIKEY) 从 CUBE_INSTANCE 中删除陈旧实例以及其他引用这些多维数据集实例的表。 delete_ci(CIKEY) 是一个存储过程,以多维数据集实例作参数;它删除多维数据集实例以及其他引用该多维数据集实例的 BPEL 表上的所有行。

该方法的好处之一是可跨多个域删除陈旧实例(如果删除了 where 条件中的 domain 子句)。它还允许大量清除陈旧实例。这两个好处使 PL/SQL 成为管理生产环境的理想选择。

以下的示例代码演示了如何在 EJB 中使用这些方法列出和删除陈旧实例。

WhereCondition where = WhereConditionHelper.whereInstancesStale();
IInstanceHandle[] instances = getLocator(domainId, domainPassword).listInstances(where);
for(int i=0; i<instances.length; i++){
instances[i].delete();
        }

前两行按域返回了所有陈旧实例的 IInstanceHandle bean 数组。之后,使用 IInstanceHandle.delete() 删除所有这些实例。可轻松扩展该代码来包含所有域。

存档旧实例和删除陈旧实例以及处理过的 XML 消息将有助于减小表的大小。结果表明,除非截断或重建表,只是删除行并不会将空间释放回表空间。

使用以下命令重新获取可用空间;可在 Dehydration Store 中针对其他表执行类似命令。
alter table bookings enable row movement;
alter table bookings shrink space cascade;
alter table cube_scope shrink space;
alter table cube_scope disable row movement;

此外,为了防止以后的增长,DBA 可以操纵 PCTFREE、PCTUSED 以及 PCTVERSION 参数。

最后,我们来看一下如何处理运行时失败的实例。

返回失败的实例

BPEL 流程失败的原因可能有多种,如错误输入、不正确的流程设计或外部应用程序不可用,等等。在任何情况下,流程都不会成功完成它的执行并以“fault”状态结束。如果流程由于不正确的设计而失败,则必须修改流程设计并重新部署 BPEL 流程。

然而,如果流程失败是由于错误输入或外部问题(网络故障、外部应用程序不可用)导致,则您可能希望在更正输入或解决了外部问题后重新运行流程。理论上,应使用旧的输入重新执行流程,以便可正确处理最初的请求。

可使用 BPELTest(10.1.3 及以后版本中有提供)实现该目标,这是一个重要的实用程序,可用于在 BPEL 流程上创建和运行测试用例以进行单元测试和集成测试。BPELTest 可模仿合作伙伴调用、执行断言以及提供各种测试结果的信息。(有关详细信息,请查看此处所列的 BPELTest 网络研讨会录像。)

BPELTest 中最重要的功能是其能够从审计跟踪创建测试用例,这也就是所谓的自动测试生成 (Automatic Test Generation)。例如,考虑一下流程实例由于外部应用程序脱机而导致执行失败的情况。理论上,您可能会在外部应用程序重新联机后以完全相同的条件重新运行该流程。但是,该方法可能比较麻烦;它涉及到重新配置流程所有着可控或不可控的外部相关性。

而使用 BPELTest,您可以从失败实例的审计跟踪生成基本的测试用例。生成的测试用例将包含完全仿效合作伙伴的命令,就像失败实例所具有的那样。在遇到不正确的数据时,可使用正确数据修改测试用例。如果导致实例失败的原因是外部应用程序不可用,则只需在应用程序可用时重新运行测试用例即可。

结论

如您所见,可以针对 BPEL Dehydration Store 使用 BPEL 流程管理器 API 和 EJB 或 PL/SQL 将 BPEL 生产环境的管理进行部分自动化。随着部署的 BPEL 流程越来越多,设计可自动化日常任务并主动解决潜在生产问题的实用程序库势在必行。


Stany Blanvalet Stany Blanvalet 是一位 BPEL 与 J2EE 咨询师。之前曾做过 Java EE 架构师。Stany 引进并管理着 Belgacom 的基于 BPEL 的 DSL 供应应用程序,这是一个任务关键的 BPEL 生产系统。他与人共同开发了 jaisy-OrabpelInterface 项目,一个适用于 Oracle BPEL 流程管理器的开放源代码的 JMX 监视工具。

将您的意见发送给我们