Oracle Bpm 11g 审批性能优化

背景


​ 项目上协调办公、工作流使用的是Oracle Bpm的产品,最近被甲方爸爸投诉审批耗时长,希望能达到平均耗时1秒,甲方爸爸都要求了,没办法,于是就开始了一段痛苦的优化过程。本着普渡众生的想法,记录下本次优化的过程,希望能为减少类似的痛苦。

问题描述


环境描述

  1. Oracle Bpm 11.1.1.6 (后续简称BPM)
  2. Oracle ESB 12.1.3(后续简称OSB)、Oracle SOA Suit 11g(后续简称SOA)

代码调用路径

​ 业务代码-->OSB-->SOA-->JAVA审批代码-->调用bpm提供的审批API

​ 本文主要描述如何对JAVA审批代码做优化

相关代码

​ 熟悉Oracle Bpm提供的审批API的同学对下面的代码应该不陌生,若是没接触过可以参考:
Oracle 官方教程

​ 项目上的审批代码:

package cn.com.utility.bpm.utils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import oracle.bpel.services.workflow.StaleObjectException;
import oracle.bpel.services.workflow.WorkflowException;
import oracle.bpel.services.workflow.client.IWorkflowServiceClient;
import oracle.bpel.services.workflow.client.IWorkflowServiceClientConstants;
import oracle.bpel.services.workflow.client.WorkflowServiceClientFactory;
import oracle.bpel.services.workflow.query.ITaskQueryService;
import oracle.bpel.services.workflow.task.ITaskService;
import oracle.bpel.services.workflow.task.model.CommentType;
import oracle.bpel.services.workflow.task.model.ObjectFactory;
import oracle.bpel.services.workflow.task.model.Task;
import oracle.bpel.services.workflow.verification.IWorkflowContext;

public class TaskService {

    private IWorkflowServiceClient wfSvcClient = null;
    private ITaskQueryService taskQueryService = null;
    private ITaskService taskService = null;
    private IWorkflowContext wfContext = null;

    private static final String BPM_MANAGER_UN = "weblogic";
    private static final String BPM_MANAGER_PW = "weblogic1";
    private static final String SOA_URL = "t3://bpmtest1.wlj.com.cn:8001";

    private static final String BPM_CLIENT_TYPE = "EJB";
    private static final String BPM_LDAP_DOMAIN = "jazn.com";

    private String username;

    public TaskService() {
        super();
    }

    public IWorkflowServiceClient getWorkflowServiceClient() {

        if (wfSvcClient == null) {
            Map properties =
                new HashMap();
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.CLIENT_TYPE,
                           BPM_CLIENT_TYPE);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL,
                           SOA_URL);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL,
                           BPM_MANAGER_UN);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS,
                           BPM_MANAGER_PW);
            wfSvcClient =
                    WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT,
                                                                          properties,
                                                                          null);
        }

        return wfSvcClient;
    }

    public ITaskQueryService getTaskQueryService() {

        if (taskQueryService == null) {
            taskQueryService =
                    this.getWorkflowServiceClient().getTaskQueryService();
        }

        return taskQueryService;
    }

    public ITaskService getTaskService() {

        if (taskService == null) {
            taskService = this.getWorkflowServiceClient().getTaskService();
        }

        return taskService;
    }

    public IWorkflowContext getWorkflowContext() {
        long start = System.currentTimeMillis();
        if (wfContext == null) {
            try {
                wfContext =
                        getTaskQueryService().authenticate(BPM_MANAGER_UN, BPM_MANAGER_PW.toCharArray(),
                                                           BPM_LDAP_DOMAIN);
                wfContext =
                        this.getTaskQueryService().authenticateOnBehalfOf(wfContext,
                                                                          username);
            } catch (WorkflowException e) {
                return null;
            }
        }
        return wfContext;
    }
    
    /**
     * 审批操作
     * @param taskId
     * @param outcome
     * @param comments
     * @param params
     * @return
     * @throws StaleObjectException
     * @throws WorkflowException
     */
    public Task updateTaskOutcome(String taskId, String outcome,
                                  String comments,
                                  Map params) throws StaleObjectException,
                                                     WorkflowException {
        Task task = this.getTaskById(taskId);
        if (params != null) {
            Iterator iter = params.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry)iter.next();
                if (entry.getKey() != null && entry.getValue() != null) {
                    String key = entry.getKey().toString();
                    String value = entry.getValue().toString();
                    this.updatePayloadElement(task, key, value);
                }
            }
        }

        ObjectFactory factory = new ObjectFactory();
        CommentType commentType = factory.createCommentType();
        commentType.setComment(comments);
        commentType.setCommentScope("TASK");
        commentType.setTaskId(taskId);
        commentType.setAction(outcome);
        task.addUserComment(commentType);
        this.getTaskService().updateTask(this.getWorkflowContext(), task);
        task = this.getTaskById(taskId);
        
        this.getTaskService().updateTaskOutcome(this.getWorkflowContext(),
                                                task, outcome);

        return task;
    }

    public void updatePayloadElement(Task task, String name, String value) {
        task.getPayloadAsElement().getElementsByTagName(name).item(0).setTextContent(value);
    }

    public Task getTaskById(String taskId) {
        Task task = null;
        try {
            task = this.getTaskQueryService().getTaskDetailsById(this.getWorkflowContext(),
                                              taskId);
        } catch (WorkflowException e) {
            e.printStackTrace();
        }
        return task;
    }

}

问题排查

​ 这个过程很痛苦的:......

(PS:不知道有没大佬能提供一套方法能减少这个过程的痛苦?欢迎大佬指点!)

思路

​ 记录下排查的思路:

  1. 源码检查
  2. 打印每一段可能耗时长的代码耗时
  3. 部署、监控
  4. 导出日志、统计分析

结果

项目上的审批接口主要实现了两件事:

  1. 审批
  2. 记录审批历史

从日志上分析,记录审批历史(这一步其实可以不要,但是由于项目上的种种原因,无法使用BPM官方的审批历史表(WFHISTORY),审批历史需要单独写入一张历史表中)暂时可以不考虑。

所以下面的篇幅就主要开始描述如优化审批操作。

优化处理


日志分析

  1. 结合源码可以看出,获取context的总耗时达到4s左右
  2. 审批时似乎重复调用了一次BPM 审批的API
【getWorkflowContext】获取context总耗时 cost(ms):918
【getWorkflowContext】获取context总耗时 cost(ms):700
【getWorkflowContext】获取context总耗时 cost(ms):1957
【getWorkflowContext】获取context总耗时 cost(ms):300
【BpmService】taskId :0a28e3e0-8c75-4eac-ae0d-78a29783be83 审批操作 cost(ms):1917
【BpmService】taskId :0a28e3e0-8c75-4eac-ae0d-78a29783be83 记录审批历史 cost(ms):8
【BpmService】taskId :0a28e3e0-8c75-4eac-ae0d-78a29783be83 总耗时 cost(ms):5892【MHM.MHM_RECEIPT_HEADERS_T】

优化

  1. 缓存context
  2. 处理重复调用API的代码

    • 增加参数(系统/流程标识)让没有设置Outcomes Require Comment的流程跳过。
    • 测试后发现,BPM流程建模的时候如果设置了Outcomes Require Comment ,那么就不能直接调用updateTaskOutcome() 方法,需要先调用下述代码:
    this.getTaskService().updateTask(this.getWorkflowContext(), task);

Bpm 建模时在humanTask上设置Outcomes Require Comment:
BPM流程建模截图

代码举例:

 //标识流程模型上有没有设置OUTCOME必须注释
Boolean comment = true;
if (funName != null && funName.startsWith("CRM.")) {
    //CRM系统不进行备注
    comment = false;
}
//省略部分代码
if (comment) {
    this.getTaskService().updateTask(this.getWorkflowContext(), task);
    task = this.getTaskById(taskId);
}

相关代码

package cn.com.utility.bpm.utils;

import java.text.SimpleDateFormat;

import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import oracle.bpel.services.workflow.StaleObjectException;
import oracle.bpel.services.workflow.WorkflowException;
import oracle.bpel.services.workflow.client.IWorkflowServiceClient;
import oracle.bpel.services.workflow.client.IWorkflowServiceClientConstants;
import oracle.bpel.services.workflow.client.WorkflowServiceClientFactory;
import oracle.bpel.services.workflow.query.ITaskQueryService;
import oracle.bpel.services.workflow.task.ITaskService;
import oracle.bpel.services.workflow.task.model.CommentType;
import oracle.bpel.services.workflow.task.model.ObjectFactory;
import oracle.bpel.services.workflow.task.model.Task;
import oracle.bpel.services.workflow.verification.IWorkflowContext;

public class TaskService2 {

    private IWorkflowServiceClient wfSvcClient = null;
    private ITaskQueryService taskQueryService = null;
    private ITaskService taskService = null;
    private IWorkflowContext wfContext = null;

    private static final String BPM_MANAGER_UN = "weblogic";
    private static final String BPM_MANAGER_PW = "weblogic1";
    private static final String SOA_URL = "t3://bpmtest1.wlj.com.cn:8001";

    private static final String BPM_CLIENT_TYPE = "EJB";
    private static final String BPM_LDAP_DOMAIN = "jazn.com";
    private static IWorkflowContext managerWfContext = null;
    private static Map tokenCache =
        new Hashtable();

    private String username;

    public TaskService2() {
        super();
    }

    public IWorkflowServiceClient getWorkflowServiceClient() {

        if (wfSvcClient == null) {
            Map properties =
                new HashMap();
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.CLIENT_TYPE,
                           BPM_CLIENT_TYPE);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_PROVIDER_URL,
                           SOA_URL);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_PRINCIPAL,
                           BPM_MANAGER_UN);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.EJB_SECURITY_CREDENTIALS,
                           BPM_MANAGER_PW);
            wfSvcClient =
                    WorkflowServiceClientFactory.getWorkflowServiceClient(WorkflowServiceClientFactory.REMOTE_CLIENT,
                                                                          properties,
                                                                          null);
        }

        return wfSvcClient;
    }

    public ITaskQueryService getTaskQueryService() {

        if (taskQueryService == null) {
            taskQueryService =
                    this.getWorkflowServiceClient().getTaskQueryService();
        }

        return taskQueryService;
    }

    public ITaskService getTaskService() {

        if (taskService == null) {
            taskService = this.getWorkflowServiceClient().getTaskService();
        }

        return taskService;
    }
    
    private IWorkflowContext getIWorkflowContext() {
        String logTag = "【getWorkflowContext】";
        long totalStartTime = System.currentTimeMillis();
        long totalEndTime = 0;
        long startTime = 0;
        long endTime = 0;
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        TokenCache token = tokenCache.get(username);
        if (token == null || token.isTimeout()) {
            System.out.println(logTag + "【" + df.format(new Date()) + "】" +
                               "create new context cache:" + username);
            try {
                if (managerWfContext == null) {
                    startTime = System.currentTimeMillis();
                    managerWfContext =
                            getTaskQueryService().authenticate(BPM_MANAGER_UN,
                                                               BPM_MANAGER_PW.toCharArray(),
                                                               BPM_LDAP_DOMAIN);
                    endTime = System.currentTimeMillis();
                    System.out.println(logTag + "【" + df.format(new Date()) +
                                       "】" + " 获取managerWfContext cost(ms):" +
                                       (endTime - startTime));
                }
                startTime = System.currentTimeMillis();
                wfContext =
                        this.getTaskQueryService().authenticateOnBehalfOf(managerWfContext,
                                                                          username);
                endTime = System.currentTimeMillis();
                System.out.println(logTag + "【" + df.format(new Date()) + "】" +
                                   " 获取wfContext cost(ms):" +
                                   (endTime - startTime));
                startTime = System.currentTimeMillis();
                if (token == null) {
                    token = new TokenCache();
                    token.setWorkflowContext(wfContext);
                    tokenCache.put(username, token);
                } else {
                    token.updateToken(wfContext);
                }
                endTime = System.currentTimeMillis();
                System.out.println(logTag + "【" + df.format(new Date()) + "】" +
                                   " 处理缓存 cost(ms):" + (endTime - startTime));
                System.out.println(logTag + "【" + df.format(new Date()) + "】" +
                                   "WorkflowContext Initialization Completed!");
            } catch (WorkflowException e) {
                System.out.println(logTag + "【" + df.format(new Date()) + "】" +
                                   "获取IWorkflowContext报错");
                System.out.println(e.getMessage());
                return null;
            }
        } else {
            System.out.println(logTag + "【" + df.format(new Date()) + "】" +
                               "get context from cache:" + username);
            wfContext = token.getWorkflowContext();
        }
        totalEndTime = System.currentTimeMillis();
        System.out.println(logTag + "【" + df.format(new Date()) + "】" +
                           " 获取context总耗时 cost(ms):" +
                           (totalEndTime - totalStartTime));
        return wfContext;
    }
    
    public IWorkflowContext getWorkflowContext() {
        String logTag = "【getWorkflowContext】";
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        IWorkflowContext context = getIWorkflowContext();
        //报错或者超时后增加一次重试
        if (context == null) {
            System.out.println(logTag + "【" + df.format(new Date()) + "】" +
                               "重新获取IWorkflowContext");
            managerWfContext = null;
            context = getIWorkflowContext();
        }
        return context;
    }
    
    /**
     * 审批操作
     * @param taskId
     * @param outcome
     * @param comments
     * @param params
     * @param funName 系统标识
     * @return
     * @throws StaleObjectException
     * @throws WorkflowException
     */
    public Task updateTaskOutcome(String taskId, String outcome,
                                  String comments, Map params,
                                  String funName) throws StaleObjectException,
                                                         WorkflowException {
        Task task = this.getTaskById(taskId);
        if (params != null) {
            Iterator iter = params.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry)iter.next();
                if (entry.getKey() != null && entry.getValue() != null) {
                    String key = entry.getKey().toString();
                    String value = entry.getValue().toString();
                    this.updatePayloadElement(task, key, value);
                }
            }
        }
        
        //标识流程模型上有没有设置OUTCOME必须注释
        Boolean comment = true;
        if (funName != null && funName.startsWith("CRM.")) {
            //CRM不进行备注
            comment = false;
        }
        ObjectFactory factory = new ObjectFactory();
        CommentType commentType = factory.createCommentType();
        commentType.setComment(comments);
        commentType.setCommentScope("TASK");
        commentType.setTaskId(taskId);
        commentType.setAction(outcome);
        task.addUserComment(commentType);
        if (comment) {
            this.getTaskService().updateTask(this.getWorkflowContext(), task);
            task = this.getTaskById(taskId);
        }
        this.getTaskService().updateTaskOutcome(this.getWorkflowContext(),
                                                task, outcome);

        return task;
    }
    
//    //旧代码
//    public IWorkflowContext getWorkflowContext() {
//        long start = System.currentTimeMillis();
//        if (wfContext == null) {
//            try {
//                wfContext =
//                        getTaskQueryService().authenticate(BPM_MANAGER_UN, BPM_MANAGER_PW.toCharArray(),
//                                                           BPM_LDAP_DOMAIN);
//                wfContext =
//                        this.getTaskQueryService().authenticateOnBehalfOf(wfContext,
//                                                                          username);
//            } catch (WorkflowException e) {
//                return null;
//            }
//        }
//        System.out.println("getWorkflowContext cost:" +
//                           (System.currentTimeMillis() - start) + "ms");
//        return wfContext;
//    }
    
//    旧代码
//    /**
//     * 审批操作
//     * @param taskId
//     * @param outcome
//     * @param comments
//     * @param params
//     * @return
//     * @throws StaleObjectException
//     * @throws WorkflowException
//     */
//    public Task updateTaskOutcome(String taskId, String outcome,
//                                  String comments,
//                                  Map params) throws StaleObjectException,
//                                                     WorkflowException {
//        Task task = this.getTaskById(taskId);
//        if (params != null) {
//            Iterator iter = params.entrySet().iterator();
//            while (iter.hasNext()) {
//                Map.Entry entry = (Map.Entry)iter.next();
//                if (entry.getKey() != null && entry.getValue() != null) {
//                    String key = entry.getKey().toString();
//                    String value = entry.getValue().toString();
//                    this.updatePayloadElement(task, key, value);
//                }
//            }
//        }
//
//        ObjectFactory factory = new ObjectFactory();
//        CommentType commentType = factory.createCommentType();
//        commentType.setComment(comments);
//        commentType.setCommentScope("TASK");
//        commentType.setTaskId(taskId);
//        commentType.setAction(outcome);
//        task.addUserComment(commentType);
//        this.getTaskService().updateTask(this.getWorkflowContext(), task);
//        task = this.getTaskById(taskId);
//        
//        this.getTaskService().updateTaskOutcome(this.getWorkflowContext(),
//                                                task, outcome);
//
//        return task;
//    }
    
    

    public void updatePayloadElement(Task task, String name, String value) {
        task.getPayloadAsElement().getElementsByTagName(name).item(0).setTextContent(value);
    }

    public Task getTaskById(String taskId) {
        Task task = null;
        try {
            task = this.getTaskQueryService().getTaskDetailsById(this.getWorkflowContext(),
                                              taskId);
        } catch (WorkflowException e) {
            e.printStackTrace();
        }
        return task;
    }

}

结果


  1. 设置了Outcomes Require Comment的流程由于添加了缓存机制,平均减少了2秒的耗时
  2. 没有设置Outcomes Require Comment的流程平均耗时1秒

最后


​ 做了缓存和系统标识后,总算是满足了甲方爸爸。

其他优化

  1. 修改审批方案:可以考虑使用MQ等消息队列做异步处理
  2. 审批历史的优化:如果需要单独记录审批历史,建议使用MQ等消息队列做异步处理

你可能感兴趣的:(oracle,java,后端,流程)