Java Drools - REST 调用超时 - 内存不足
Java Drools - REST Call Timing Out - Out Of Memory
我正在 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 来调用规则。在我的配置 class 中,我正在从数据库加载 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
注释中的列表 class:
@Repository
public class ServiceDao implements IServiceDao
在上面的ServiceDao中有一个方法,在post构造阶段调用:
@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 方法,在单独的控制器中 class:
@RestController
public class RuleEvaluationController
它只是从 ServiceDao 调用后端服务 class:
@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 调用之后,下一次休息调用永远不会 returns 响应,一段时间后我收到内存不足错误。
谁能告诉我我在这里错过了什么?非常感谢任何提示和建议。如果以上信息不足,请随时询问更多详情。
谢谢
您没有提供所有代码,所以您可能在其他地方进行了清理
但看起来您正在插入和调用 .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/#d0e3555
文档中描述的有状态会话需要在第一次 fireAllRules()
调用后删除对象。因此,您要么创建无状态会话并每次都处理它,要么使用有状态会话并在插入新对象之前删除旧对象。
我可能会选择无状态(因为我已经在这样做了;)
[...]
try {
packageBuilder.addPackageFromDrl(new
StringReader(completeRulesDroolsScript));
org.drools.core.rule.Package rulesPackage =
packageBuilder.getPackage();
ruleBase.addPackage(rulesPackage);
this.setWorkingMemory(ruleBase.newStatefulSession());
}
[...]
我假设所有繁重的工作(解析 DRL 等)都在 newStatefulSession()
调用和创建之前发生
会话不是 'expensive'.
感谢您提供的提示和建议,它们有助于找到解决方案。
我解决问题如下:
首先我使用 KieSession 迁移到版本 6.. 的新语法。
首先我初始化了 Kie 组件如下:
// Define Kie Service from the Drools factory:
KieServices ks = KieServices.Factory.get();
// Define Drools Kie Repository to house the rules:
KieRepository kr = ks.getRepository();
// By default, a Kie File System is defined. In this case rules are NOT being
// read from the *.drl file, but instead fetched from Database.
KieFileSystem kfs = ks.newKieFileSystem();
// Write rules into the Kie file system. Specified file is irrelevant and can be
// empty, since we are not reading from *.drl file.
kfs.write("src/main/resources/com/rule/tempRules.drl", completeAccountingRulesDroolsScript);
// Define rule builder of the rules in file system object:
KieBuilder kb = ks.newKieBuilder(kfs);
// Build all rules in the script.kieModule is automatically deployed to
// KieRepository if successfully built.
kb.buildAll();
// Define new session container. This container is global and will be available
// for all subsequent rule invocations:
kContainer = ks.newKieContainer(kr.getDefaultReleaseId());
在我的 REST 调用中,我用以下代码替换了代码:
kSession = kContainer.newKieSession();
kSession.insert(response);
kSession.insert(request);
kSession.fireAllRules();
Collection<? extends Object> objects = kSession.getObjects();
kSession.dispose();
现在变量 kContainer 是一个只在启动时初始化的全局变量。在每次 REST 调用结束时,我都会处理会话。未来的通话将为每个通话创建一个 kie 会话。如果这是最佳方法,可以使用您的意见。
@user3075118 @
冷冻豌豆的罗迪
感谢您的提示,正是它解决了问题。
我正在 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 来调用规则。在我的配置 class 中,我正在从数据库加载 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
注释中的列表 class:
@Repository
public class ServiceDao implements IServiceDao
在上面的ServiceDao中有一个方法,在post构造阶段调用:
@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 方法,在单独的控制器中 class:
@RestController
public class RuleEvaluationController
它只是从 ServiceDao 调用后端服务 class:
@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 调用之后,下一次休息调用永远不会 returns 响应,一段时间后我收到内存不足错误。
谁能告诉我我在这里错过了什么?非常感谢任何提示和建议。如果以上信息不足,请随时询问更多详情。
谢谢
您没有提供所有代码,所以您可能在其他地方进行了清理
但看起来您正在插入和调用 .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/#d0e3555
文档中描述的有状态会话需要在第一次 fireAllRules()
调用后删除对象。因此,您要么创建无状态会话并每次都处理它,要么使用有状态会话并在插入新对象之前删除旧对象。
我可能会选择无状态(因为我已经在这样做了;)
[...]
try {
packageBuilder.addPackageFromDrl(new
StringReader(completeRulesDroolsScript));
org.drools.core.rule.Package rulesPackage =
packageBuilder.getPackage();
ruleBase.addPackage(rulesPackage);
this.setWorkingMemory(ruleBase.newStatefulSession());
}
[...]
我假设所有繁重的工作(解析 DRL 等)都在 newStatefulSession()
调用和创建之前发生
会话不是 'expensive'.
感谢您提供的提示和建议,它们有助于找到解决方案。
我解决问题如下:
首先我使用 KieSession 迁移到版本 6.. 的新语法。
首先我初始化了 Kie 组件如下:
// Define Kie Service from the Drools factory:
KieServices ks = KieServices.Factory.get();
// Define Drools Kie Repository to house the rules:
KieRepository kr = ks.getRepository();
// By default, a Kie File System is defined. In this case rules are NOT being
// read from the *.drl file, but instead fetched from Database.
KieFileSystem kfs = ks.newKieFileSystem();
// Write rules into the Kie file system. Specified file is irrelevant and can be
// empty, since we are not reading from *.drl file.
kfs.write("src/main/resources/com/rule/tempRules.drl", completeAccountingRulesDroolsScript);
// Define rule builder of the rules in file system object:
KieBuilder kb = ks.newKieBuilder(kfs);
// Build all rules in the script.kieModule is automatically deployed to
// KieRepository if successfully built.
kb.buildAll();
// Define new session container. This container is global and will be available
// for all subsequent rule invocations:
kContainer = ks.newKieContainer(kr.getDefaultReleaseId());
在我的 REST 调用中,我用以下代码替换了代码:
kSession = kContainer.newKieSession();
kSession.insert(response);
kSession.insert(request);
kSession.fireAllRules();
Collection<? extends Object> objects = kSession.getObjects();
kSession.dispose();
现在变量 kContainer 是一个只在启动时初始化的全局变量。在每次 REST 调用结束时,我都会处理会话。未来的通话将为每个通话创建一个 kie 会话。如果这是最佳方法,可以使用您的意见。
@user3075118 @ 冷冻豌豆的罗迪
感谢您的提示,正是它解决了问题。