类似淘宝、腾讯之类的复杂业务系统一般都采用分布式的系统集群结构,每一个独立的子系统负责一块业务,这样做主要是为了使业务更加分离,专人负责专人的应用系统,同时降低系统的耦合性,增强系统的安全和稳定性。同时分布式的数据库和应用结构,使很多复杂的业务规则也变成了一个特定的系统调用。
比如“支付订单(12781)”,需要调用一个交易中心的支付订单API。
本章主要介绍的是如何通过扩展QlExpress的上下文,来管理和调用spring的bean。以及在出现系统故障的时候,编写批量脚本来快速的恢复子系统的间系统调用。
我们来看这个场景:
例如因为处理订单流程的平台因为网络原因暂停了很多用户的流程,导致有一大堆的订单号等待处理(12781,12788,12312)。
对应的qlExpress的参数设置原理图:
这个控制台的界面大概是这样设计的:
****************************************************
用户上下文:idList ={12781,12788,12312}
***************************************************
执行的脚本:orderService.orderPay(id);
***************************************************
问题是 QlExpress虽然可以解读orderService.orderPay(id)是个bean的方法调用,但是orderService这个bean怎么从spring获取到和脚本关联起来呢?
答案是扩展QlExpress原来的上下文。
上下文的扩展类,实现了 IExpressContext:
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import com.ql.util.express.IExpressContext;
@SuppressWarnings("serial")
public class QLExpressContext extends HashMap implements IExpressContext{
private ApplicationContext context;
public QLExpressContext(ApplicationContext aContext){
this.context = aContext;
}
public QLExpressContext( Map aProperties,ApplicationContext aContext){
super(aProperties);
this.context = aContext;
}
/**
* 抽象方法:根据名称从属性列表中提取属性值
*/
public Object get(Object name) {
Object result = null;
result = super.get(name);
try{
if (result == null &&this.context!= null && this.context.containsBean((String)name)) {
//如果在Spring容器中包含bean,则返回String的Bean
result = this.context.getBean((String)name);
}
}catch(Exception e){
throw new RuntimeException(e);
}
return result;
}
public Object put(String name, Object object) {
return super.put(name,object);
}
}
执行的工具类,实现了ApplicationContextAware 接口
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import com.taobao.upp.sc.common.expression.QLExpressContext;
public class QlExpressUtil implements ApplicationContextAware{
private static ExpressRunner runner ;
static{
runner = new ExpressRunner();
}
private static boolean isInitialRunner = false;
private ApplicationContext applicationContext;//spring上下文
/**
*
* @param statement 执行语句
* @param context 上下文
* @throws Exception
*/
@SuppressWarnings("unchecked")
public Object execute(String statement,Map context) throws Exception
{
initRunner(runner);
IExpressContext expressContext = new QLExpressContext(context,applicationContext);
return runner.execute(statement, expressContext, null, true, false);
}
private void initRunner(ExpressRunner runner)
{
if(isInitialRunner == true){
return ;
}
synchronized (runner) {
if(isInitialRunner == true){
return ;
}
try {
//在此可以加入预定义函数
} catch (Exception e) {
throw new RuntimeException("初始化失败表达式",e);
}
}
isInitialRunner = true;
}
public void setApplicationContext(ApplicationContext aContext)
throws BeansException {
applicationContext = aContext;
}
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.map.ListOrderedMap;
控制台代码的实现
public class ClientQlExpressTest {
/**
*
* @param idList
* @param text
* @param resultMap
*/
public void run(List idList, String text, ListOrderedMap resultMap) {
for (Long id : idList) {
Map innerContext = new HashMap();
innerContext.put("id", id);
Object result = qlExpressUtil.execute(statement, innerContext);
resultMap.put(id, result);
}
}
}
最终我们可以在resultMap看到类似这样的结果:
12781 | true |
12788 | false |
12312 | true |
就简单的几个类实现一个很强大的java脚本语言执行工具,是不是很犀利呢!
orderService.orderPay(id);
这个脚本虽然对于java开发工程师师没有问题的,但是对于技术支持人员,显然还是太难记住了,下篇我们将进行宏定义,变成
支付订单(id)
因为本身QlExpressRunner本身不依赖spring,所以这个例子就不在QlExpress的源代码demo中,如果有疑问可以联系我,
旺旺:天樵
QQ:371754252
阿里巴巴目前也专门针对脚本引擎,规则引擎招收相关人才,欢迎发邮件或者建立给我哦 。[email protected]
项目开源地址: https://github.com/alibaba/qlExpress