整理了近期在项目上做的一些技术研究,希望与大家共同探讨交流。
在清算项目的计算模块使用 jdk 1.8 自带的 js 引擎 nashorn 做运算。 使用过程对 nashorn 做了一些简单的测试。
使用 nashorn 编译 js 后 执行 invokeFunction 计算 ,一个简单的逻辑判断 加上一个简单的乘法运算 循环执行100w次总耗时约为600~700ms
使用 mozilla 的 rhino js引擎, 编译 js 后 执行 func.call 计算 ,总耗时约为 1400ms 左右。
nashorn 中 eval 方法编译 js 耗时很大, 实际应用时需要进行缓存处理。
除此之外, 使用 nashorn 过程一直调用不到内部的 js 方法。
这里记录下这次的使用方式
public class LiquidationUtil {
private static Logger logger = LoggerFactory.getLogger(LiquidationUtil.class);
public static final String _expression = "_expression";
public static final String _conditions = "_conditions";
// 生成清算规则的计算对象
public static Invocable generateLiquidationFuction(String conditions,String expressionMark,String expressionParam) throws ScriptException, NoSuchMethodException {//Liquidation
StringBuffer sb = new StringBuffer(128);
if(StringUtils.isEmpty(conditions))
conditions = "true";
sb.append("var ").append(_conditions).append(" = function (m,o) {")
.append("\nreturn ").append(_conditions).append("(m,o,null);\n")
.append("};\n");
sb.append("var ").append(_expression).append(" = function (m,o) {")
.append("\nreturn ").append(_expression).append("(m,o,null);\n")
.append("};\n");
// 条件
sb.append("var ").append(_conditions).append(" = function (m,o,jh) {")
.append("\nreturn ").append(conditions).append(";\n")
.append("};\n");
String expressionJs = expressionMap.get(expressionMark);
// 计算
sb.append("var ").append(_expression).append(" = function (m,o,jh) {")
.append("\nvar result=null;\n")
.append("\n var ").append(expressionParam).append(";\n")
.append("\n").append(expressionJs).append(";\n")
.append("\nreturn result;\n")
.append("};");
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");//nashorn javascript
engine.eval(sb.toString());
return (Invocable) engine;
}
private static final Map<String, String> expressionMap = new HashMap<String, String>();
static {
try {
String path = Thread.currentThread().getContextClassLoader().getResource("liquidation_script").getFile();
logger.info("加载清算规则js脚本 path:{}", path);
File dir = new File(path);
for(File script : dir.listFiles()) {
String fileName = script.getName();
String filePath = script.getAbsolutePath();
logger.info("加载清算规则脚本 mark:{}\tfilePath:{}",fileName, filePath);
expressionMap.put(fileName.substring(0, fileName.length()-3), readFileContent(filePath));
}
} catch (IOException e) {
logger.error("初始化算费规则脚本出错", e);
}
}
public static String readFileContent(String uri) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(uri));
String line = "";
StringBuffer buffer = new StringBuffer();
while ((line = br.readLine()) != null) {
buffer.append(line).append("\n");
}
String fileContent = buffer.toString();
return fileContent;
}
}
调用:
jsInvocable = (Invocable)LiquidationUtil.generateLiquidationFuction(conditionsJs, expressionMark, expressionParam);// 对 invocable 缓存
result = jsInvocable.invokeFunction(_conditions, mapping, orderInfo);
js 代码的格式为
var func1 = function(param1, param2){
...
}
var func2 = function(param1, param2){
...
}
对这段字符串执行 eval, 得到 Invocable ,
然后可以执行 Invocable.invokeFunction("func1", param1, param2) 调用 js 方法。