函数计算引擎
对于元数据、指标、特征、变量 等等不会导致外部状态变更的计算来说本质都是一种函数,通过入参、API调用、数据库读取,再进行一系列运算加工,生成最终结果。
那么对于一个集合的函数运算,往往会有很多重复的接口调用或者重复调用同一种函数的操作,而这些操作往往是可以被简化。
这个引擎减少了一个集合中的函数重复接口调用和重复的函数调用的次数,并且通过一个非常轻量级的方式嵌入到项目中。
外部API调用了2次
通过这个计算引擎,外部API只调用了1次,并且实现了计算并行化。
该引擎依赖JDK8,以及SpringBoot框架
若出现找不到arg0变量的错误,需要在项目中增加-parameters参数,并且rebuild项目,参考设置JDK8获取方法参数的流程
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<!-- 重要 -->
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
通过标注@Feature的形式,将方法转化为一个函数(仍可以通过方法的形式调用)。 下面代码表示test5依赖test4,test4无入参,可直接计算,当我只需要计算test5的时候,计算引擎会根据函数的依赖关系,生成一个DAG图: 即:test5--依赖-->test4 那么开始计算的时候,计算引擎会先计算test4的值,再把test4的值放入test5的入参中让其计算。
@FeatureClass
@Component
public class Test {
// ⬇️代表函数名称
@Feature(name = "test5")
// ⬇️ 依赖的函数名称
public Double test5(Double test4){
return test4 + 1.0;
}
@Feature(name = "test4")
public Double test4(){
return 1.0;
}
}
@Feature 标志这个方法是一个函数(方法务必是public),name属性为最后生成该函数的名字(可与方法名不同),入参的类型和参数必须已有的函数相同,不支持同名函数。
@FeatureClass 作用在类上,标明这个类中有函数需要计算,需要配合Spring中的@Component注解
入参 originDataMap原始数据 以及 calcFeatures待计算的函数名称。 返回一个计算完成的函数Map。 默认使用线程池进行并行计算(确保拓扑顺序),默认线程数为机器的核心数✖️2
可通过feature.featureThreadPoolSize以及feature.featureThreadPoolMaxSize更改。
现版本无法解决循环依赖问题,需在编码时确保,加入在环中任意一个节点的原始数据即可打破循环。
@Service
public class FeatureService {
@Autowired
FeatureEngine featureEngine;
public Map<String, Object> calc(Map<String, Object> originDataMap, Set<String> calcFeatures){
return featureEngine.calc(originDataMap, calcFeatures);
}
}
输入样例:
{
"calcFeatures": [
"test5"
],
"originDataMap": {
}
}
输出样例:
{
"test4": 1,
"test5": 2
}
通过继承AbstractFeatureBean来自定义计算的Bean。
public abstract class AbstractFeatureBean implements IFeatureBean {
//必要
protected String name;
//非必要
protected boolean output;
//若有需要依赖别的Bean,则需要将其他Bean的名称写入
protected List<String> parents;
//无需填写,系统会自动生成
protected List<String> children;
}
//Sample
public class OuterFeatureBean extends AbstractFeatureBean {
private Function<Object[], Object> fn;
@Override
public Object execute(Object[] args) {
return fn.apply(args);
}
public OuterFeatureBean(String name, Function<Object[], Object> fn){
this.name = name;
this.fn = fn;
}
}
引擎计算加入外部Bean的方法
@Service
@Slf4j
public class FeatureService {
@Autowired
FeatureEngine featureEngine;
public Map<String, Object> calcWithOuterFeatureBean(Map<String, Object> originDataMap, Set<String> calcFeatures){
Map<String, OuterFeatureBean> map = new HashMap<>(1);
map.put("zh", new OuterFeatureBean("zh", (a) -> 1));
return featureEngine.calcWithOuterFeatureBean(originDataMap, calcFeatures, map);
}
}
public interface FeaturePostProcessor{
@Nullable
default <T extends AbstractFeatureBean> T postProcessAfterInitializationFeature(T featureBean, String featureBeanName) throws BeansException {
return featureBean;
}
}
同Spring的 BeanPostProcessor