背景

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

问题描述

环境描述

Oracle Bpm 11.1.1.6 (后续简称BPM)

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:不知道有没大佬能提供一套方法能减少这个过程的痛苦?欢迎大佬指点!)

思路

​ 记录下排查的思路:

源码检查

打印每一段可能耗时长的代码耗时

部署、监控

导出日志、统计分析

结果

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

审批

记录审批历史

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

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

优化处理

日志分析

结合源码可以看出,获取context的总耗时达到4s左右

审批时似乎重复调用了一次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】

优化

缓存context

处理重复调用API的代码

增加参数(系统/流程标识)让没有设置Outcomes Require Comment的流程跳过。

测试后发现,BPM流程建模的时候如果设置了Outcomes Require Comment ,那么就不能直接调用updateTaskOutcome() 方法,需要先调用下述代码:

this.getTaskService().updateTask(this.getWorkflowContext(), task);

Bpm 建模时在humanTask上设置Outcomes Require Comment:

代码举例:

//标识流程模型上有没有设置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;

}

}

结果

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

没有设置Outcomes Require Comment的流程平均耗时1秒

最后

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

其他优化

修改审批方案:可以考虑使用MQ等消息队列做异步处理

审批历史的优化:如果需要单独记录审批历史,建议使用MQ等消息队列做异步处理

oracle bpm难点,Oracle Bpm 11g 审批性能优化相关推荐

  1. oracle 考试技巧,从 TPCH 测试学习性能优化技巧

    一.目标 TPCH是由TPC(Transaction Processing Performance Council)事务处理性能委员会公布的一套针对数据库决策支持能力的测试基准,通过模拟数据库中与业务 ...

  2. oracle分片键,数据库SQL语句及性能优化

    3.阅读以下文章,回答一下问题: 3.1.什么是数据库分片技术? 3.2.数据库分片的好处有哪些? 3.3.Oracle 数据库分片技术比NoSQL的分片有哪些优势? Overview of Orac ...

  3. oracle spa性能测试,SPA for 11g 分析性能

    11G的新特性SPA(SQL Performance Analyze)现在被广泛的应用到升级和迁移的场景.当然还有一些其他的场景可以考虑使用,比如(参数修改,I/O子系统变更),但是主要是为了帮助我们 ...

  4. oracle union all 特别慢_Oracle查询性能优化

    原则一:注意WHERE子句中的连接顺序:ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE ...

  5. Oracle笔记(十二):性能优化篇

    目录 一.概述 二.修改系统全局区 三.修改进程全局区 四.优化查询 分析执行计划 使用索引 优化子查询 五.优化数据库结构 拆分表 增加中间 优化插入记录速度---禁用索引 禁用唯一性检查 使用批量 ...

  6. oracle 考试技巧,从 TPCH 测试学习性能优化技巧之 Q14

    一. 查询要求 Q14语句查询获得某一个月的收入中有多大的百分比是来自促销零件.用以监视促销带来的市场反应. Q14语句的特点是:带有聚集.连接操作的简单查询. 二. Oracle执行 Oracle编 ...

  7. mysql半连接_mysql表的半连接,反连接导致的mysql性能优化剖析

    [导读] 关于Oracle的半连接,反连接,我一直认为这是一个能讲很长时间的话题,所以在我的新书<Oracle DBA工作笔记>中讲性能优化的时候,我花... 关于Oracle的半连接,反 ...

  8. 10 行代码解决漏斗转换计算之性能优化

    可阅读原文:http://c.raqsoft.com.cn/article/1539156910581?r=alice 大数据分析的性能优化,说道底,就优化一个事情:针对确定的一个计算任务(数据确定, ...

  9. Oracle 11G常见性能诊断报告(AWR/ADDM/ASH)收集

    Docker安装Oracle_11g数据库并配置: https://blog.csdn.net/qq_44895681/article/details/113975822 前言:  在生产环境中,当运 ...

最新文章

  1. 我收藏的技术知识图(每张都是大图)
  2. Python Module_pdb_DEBUG 方法
  3. layui table行点击tr_layui框架table表格实现单击行选中checkbox功能
  4. 你要「老婆」不要?谷歌程序员20行代码把二次元「老婆」带到现实世界
  5. 设计模式2:工程模式(1)
  6. C++ opengl 纹理过滤之GL_REPEAT
  7. 华为政企云副总裁 年薪_看好“新基建”华为云战略投入政企市场
  8. 11、Libgdx的音频
  9. selenium 获取不了标签文本的解决方法
  10. [TJOI2015]弦论(后缀数组or后缀自动机)
  11. Rank() 、DENSE_RANK()、NTILE(n)的用法-转
  12. Swift - 43 - 继承, 多态, 析构函数
  13. Redpill:在后渗透中实现反向TCP Shell
  14. python开发的网络调试助手_Linux/windows/mac 下的socket网络通信调试助手 UDP/TCP
  15. react 实战案例(webpack构建)
  16. 32位汇编语言程序设计(钱晓捷) 高清完整
  17. 范罗士空气净化器PT65评测
  18. 关于常用颜色小聚:有据说是令人舒服的颜色值,有R G B 常用颜色列表
  19. Intellij IDEA摸鱼插件 看电子书 AndroidStudio 摸鱼插件 摸鱼神器 IdeaTxt
  20. 火车头采集翻页内容_火车头采集:内容替换支持[参数],标签

热门文章

  1. 调用百度短网址API接口进行短网址的转换(Java程序的实现)
  2. Stopwatch常用方法,不积硅步无以至千里
  3. 亚马逊Alexa的深度学习与语音识别的核心技术原理
  4. 【观察】亚马逊云科技发布中国战略背后,是“在中国”到“懂中国”的蜕变...
  5. 2020年光通信市场下光缆生产发展的宏观分析
  6. 淘宝数据分析:利用数据细分目标客户群
  7. MADlib——基于SQL的数据挖掘解决方案(6)——数据转换之矩阵分解
  8. encoder to decoder
  9. cookie获取和钓鱼攻击演示
  10. 本科课程【数字图像处理】实验2 - 图像增强