簡體   English   中英

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 來調用規則。 在我的配置類中,我從數據庫加載 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 中有一個方法,在 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 方法,在一個單獨的控制器類中:

@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();

感謝您提供的提示和建議,它們有助於找到解決方案。

我解決了這個問題如下:

首先,我遷移到版本 6 的新語法。. ,使用 KieSession。

首先我初始化了 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 @ 冷凍豌豆的羅迪

謝謝你的提示,這是解決問題的人。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM