java - Java Drools - REST 调用超时 - 内存不足
问题描述
我在 Java 项目中实现 Drools,以下依赖项的版本 6:
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>6.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>6.0.1.Final</version>
</dependency>
我打算通过调用使用 spring 4.3.0.RELEASE 构建的 REST API 来调用规则。在我的配置类中,我正在从数据库中加载 drools 脚本:
@Configuration
@EnableWebMvc
@EnableAsync
@ComponentScan(basePackages = "com.bla.bla.api.app")
@ImportResource({ "classpath:datasources-context.xml" })
public class AppConfig {
private static final Log log = LogFactory.getLog(AppConfig.class);
public @Autowired IServiceDao serviceDao;
@Repository
然后将规则作为注释类中的列表存储在全局变量中:
@Repository
public class ServiceDao implements IServiceDao
在上面的 ServiceDao 中有一个方法,在构建后阶段调用:
@PostConstruct
private void init() throws IOException {
log.debug("Initializing ruleevaluation API Context");
// Initializing rules:
serviceDao.initializeRules();
}
,从数据库重新加载规则并初始化drools环境:
public void initializeRules() {
// 1. Load Rules:
log.debug("Initiating Rules...");
rules.clear();
rules = loadRules();
// 2. Initialize Drools Environment:
try {
initializeDrools();
} catch (DroolsParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
方法“initializeDrools()”实现如下:
private void initializeDrools() throws DroolsParserException, IOException {
PackageBuilder packageBuilder = new PackageBuilder();
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
// This script will contain all the rules combined in one Drools script:
String completeRulesDroolsScript = "";
// Initialize script with object imports:
completeRulesDroolsScript = ""
+ "import com.bla.model.svc.RuleRequest \r\n"
+ "import com.bla.model.svc.RuleResponse \r\n\n\n";
if (rules != null) {
if (rules.size() > 0) {
for (RuleObject ruleObject : rules) {
// Add each individual Drools rule expression to the complete Drools script:
completeRulesDroolsScript += ruleObject.getExpression();
completeRulesDroolsScript = completeRulesDroolsScript .concat("\n\n\n");
}
}
}
try {
packageBuilder.addPackageFromDrl(new
StringReader(completeRulesDroolsScript));
org.drools.core.rule.Package rulesPackage = packageBuilder.getPackage();
ruleBase.addPackage(rulesPackage);
this.setWorkingMemory(ruleBase.newStatefulSession());
}
catch(Exception e) {
log.error("!!! Could not Initialize Drools Engine !!!");
}
}
接下来是 REST 方法,在一个单独的控制器类中:
@RestController
public class RuleEvaluationController
它只是从 ServiceDao 类调用后端服务:
@PostMapping(value = "/evaluateRule")
public ResponseEntity<RuleEvaluationResponse> evaluateRuleData(
@RequestBody RuleEvaluationRequest ruleEvaluationRequest) {
RuleEvaluationResponse ruleEvaluationResponse = new RuleEvaluationResponse();
try {
// 1. get the parameters list:
log.debug(ruleEvaluationRequest.toString());
RuleEvaluationResponse response = serviceDao
.evaluateRule(ruleEvaluationRequest);
log.debug("response = " + response.getEntry());
return new ResponseEntity<RuleEvaluationResponse>(response,
HttpStatus.OK);
} catch (Exception e) {
log.error("!!!!! Exception Occurred:" + e.getMessage());
// set request id, and date in millis:
ruleevaluationResponse.setRequestId(UUID.randomUUID().toString());
ruleevaluationResponse.setDate(System.currentTimeMillis());
ruleevaluationResponse.setErrorCode(Constants.FAILURE);
ruleevaluationResponse.setErrorDescription(e.getMessage());
return new ResponseEntity<RuleEvaluationResponse>(ruleevaluationResponse,
HttpStatus.OK);
}
}
其中serviceDao.evaluateRule 只是简单地插入请求和响应对象,然后触发规则:
public RuleEvaluationResponse evaluateRule(RuleEvaluationRequest request) {
RuleEvaluationResponse response = new RuleEvaluationResponse();
this.getWorkingMemory().insert(response);
this.getWorkingMemory().insert(request);
this.getWorkingMemory().fireAllRules();
log.debug("Response = " + response.getEntry());
return response;
}
起初,这一切都有效,我成功评估了规则并获得了预期的响应。然而,在 4 或 5 次 REST 调用之后,下一次 REST 调用需要永远并且永远不会返回响应,并且过了一会儿我得到了内存不足的错误。
谁能告诉我我在这里错过了什么?非常感谢任何提示和建议。如果上述信息不足,请随时询问更多详情。
谢谢
解决方案
您没有提供所有代码,所以也许您在其他地方进行了清理,但看起来您正在插入和调用.fireAllRules()
而不是清理。我的代码与 KieSession 一起使用,看起来或多或少像这样:
kSession = container.newKieSession("name");
kSession.insert(...);
kSession.insert(...);
kSession.insert(...);
kSession.fireAllRules();
Object[] objects = kSession.getObjects();
kSession.dispose();
您可能需要更改您的代码。https://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html_single/#d0e3555fireAllRules()
文档中描述的全状态会话需要在第一次调用
后删除对象。因此,您要么创建无状态会话并每次处理它,要么使用有状态会话并在插入新对象之前删除旧对象。
我可能会选择无国籍(因为我已经这样做了;)
[...]
try {
packageBuilder.addPackageFromDrl(new
StringReader(completeRulesDroolsScript));
org.drools.core.rule.Package rulesPackage =
packageBuilder.getPackage();
ruleBase.addPackage(rulesPackage);
this.setWorkingMemory(ruleBase.newStatefulSession());
}
[...]
我假设所有繁重的工作(解析 DRL 等)都在newStatefulSession()
调用之前发生,并且创建会话并不“昂贵”。
推荐阅读
- python - RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret
- oracle-adf - 如何根据sql查询查看对象属性?
- haskell - Error installing Data.Aeson
- php - Laravel 关系/属性到公共链接表
- java - 当 JAVA 将功能接口转换为比较器时发生了什么?
- swift - 如何重定向所有 404 错误
- c# - How to upload/download a file with Microsoft graph API in Unity3d
- angular - Angular 6 - 进度和滑块
- android - 以编程方式创建 RadioGroup OnClicked 阅读标准 Android
- angular - 在 Angular6 中使用缓存数据来优化重新加载时间