drools性能优化

多线程读58万数据处理后写入到另外一张表中性能调优实例遇到第一个问题就是drools的性能瓶颈,对CPU消耗比加大,那么如何优化呢?
1
从下图看到i5-8250U的计算能力应该不行,这个时候终于知道cpu好,会能起到什么效果了。
2
使用arthas可以定位到,有两个地方耗费时间Results results = kieHelper.verify();KieBase kieBase = kieHelper.build(config);

public KieSession decodeToSession(String... drl) {
        KieHelper kieHelper = new KieHelper();
        String[] var3 = drl;
        int var4 = drl.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String s = var3[var5];
            kieHelper.addContent(s, ResourceType.DRL);
        }

        Results results = kieHelper.verify();
        if (results.hasMessages(new Level[]{Level.WARNING, Level.ERROR})) {
            List<Message> messages = results.getMessages(new Level[]{Level.WARNING, Level.ERROR});
            Iterator var11 = messages.iterator();

            while(var11.hasNext()) {
                Message message = (Message)var11.next();
                this.logger.error("Error: {}", message.getText());
            }

            throw new IllegalStateException("Compilation errors.");
        } else {
            KieBaseConfiguration config = kieHelper.ks.newKieBaseConfiguration();
            if (EventProcessingOption.STREAM.getMode().equalsIgnoreCase(this.getMode())) {
                config.setOption(EventProcessingOption.STREAM);
            } else {
                config.setOption(EventProcessingOption.CLOUD);
            }

            KieBase kieBase = kieHelper.build(config);
            KieSession kieSession = kieBase.newKieSession();
            if (this.getListener() == null || !"off".equalsIgnoreCase(this.getListener())) {
                kieSession.addEventListener(new DefaultRuleRuntimeEventListener());
                kieSession.addEventListener(new DefaultAgendaEventListener());
                kieSession.addEventListener(new DefaultProcessEventListener());
            }

            return kieSession;
        }
    }

先看KieHelper.verify,对规则的校验并不总是需要,那么设置一个规则,校验过的不再校验,这个时间就节省了。

public KieSession getKieSession(List<String> ruleFileNames){
        if (CollectionUtils.isEmpty(ruleFileNames)){
            throw new NullPointerException("ruleFileNames is null");
        }
        List<String> rules = new ArrayList<>();
        Long start = System.currentTimeMillis();
        for (String ruleFileName:ruleFileNames){
            // 从缓存中获取内容
            String rule = cacheService.get(ruleFileName);
            if (StringUtils.isBlank(rule)){
                // 缓存中没有取到数据,这种情况是针对新增的文件,在cache中还没有存在,定时任务还没有刷到,则主动加载
                rule = loadAppointRule(ruleFileName);
                rules.add(rule);
            } else {
                if (Constants.ON.equals(cacheService.get(ruleFileName+Constants.FLAG))){
                    // 缓存没有变化,就不需要再校验了,因为loadAppointRule的时候已经verify了
                    rules.add(rule);
                    log.info("文件:{},缓存命中",ruleFileName);
                } else{
                    log.info("文件:{},缓存没有命中",ruleFileName);
                    // 缓存发生变化,需要再次verify
                    if (verify(rule)){
                        // 校验通过的标志
                        cacheService.put(ruleFileName+Constants.FLAG,Constants.ON);
                        rules.add(rule);
                    } else{
                        throw new IllegalArgumentException("ruleFileName verify error");
                    }
                }
            }
        }
        Long end = System.currentTimeMillis();
        log.info("load rule 总耗时:{}ms",end-start);
        // 构建KieSession
        start = System.currentTimeMillis();
        KieSession kieSession = buildKieSession(rules);
        end = System.currentTimeMillis();
        log.info("buildKieSession 总耗时:{}ms",end-start);
        return kieSession;
    }

加载文件,校验通过之后,就存入缓存,并设置校验通过标志位,这个时间就节省掉了。

private String loadAppointRule(String fileName) {
        List<File> fileList = getAllRuleFiles();
        for (File file : fileList) {
            if (fileName.equals(file.getName())) {
                String rule = encodeToString(file.getPath());
                // 校验加载的规则文件是否合法
                if (verify(rule)){
                    // 将正确的规则文件写入到缓存中
                    cacheService.put(fileName, rule);
                    // 校验通过的标志
                    cacheService.put(fileName+Constants.FLAG,Constants.ON);
                } else{
                    log.error("规则文件:{},存在异常,请注意核查",fileName);
                }
                return rule;
            }
        }
        return null;
    }

继续跟踪,发现耗时还是getKieSession
1
问题出现下面的代码,那么该如何优化呢?

private KieSession buildKieSession(List<String> rules){
        KieHelper kieHelper = new KieHelper();
        for (String rule : rules) {
            kieHelper.addContent(rule, ResourceType.DRL);
        }
        KieBaseConfiguration config = kieHelper.ks.newKieBaseConfiguration();

        if (EventProcessingOption.STREAM.getMode().equalsIgnoreCase(getMode())) {
            config.setOption(EventProcessingOption.STREAM);
        } else {
            config.setOption(EventProcessingOption.CLOUD);
        }
        KieBase kieBase = kieHelper.build(config);
        KieSession kieSession = kieBase.newKieSession();
        if (getListener() == null || !Constants.OFF.equalsIgnoreCase(getListener())) {
            kieSession.addEventListener(new DefaultRuleRuntimeEventListener());
            kieSession.addEventListener(new DefaultAgendaEventListener());
            kieSession.addEventListener(new DefaultProcessEventListener());
        }
        return kieSession;
    }

换了一台i5-9400的,buildKieSession 总耗时:2350ms,相比i5-8250U,快了8倍以上,确实难以想象。
1
drools的线程数量,跟CPU核数有关系
1
我们知道有状态的kiesession构建时间要比无状态的statelesskiesession长,那么如果是统一个规则,重复支持,这个状态保持住不就可以了吗?推理逻辑就是这样。
可以看到kiesession的编译时间骤降,性能很快提升上去了。
1
调整代码如下:

 @Override
    public boolean matchByDay(LiuShiJiaZiEnum day) throws InvocationTargetException, IllegalAccessException {
        log.info(String.format("task %s start", day.display()));
        long start = System.currentTimeMillis();
        List<BaziInfo> baziInfos = baziInfoService.getByDay(day.display());
        // 获取kiesession
        List<String> ruleList = new ArrayList<>();
        ruleList.add("shy3.xlsx");
        KieSession session = kieTemplate.getKieSession(ruleList);
        // 生成
        ShyGenYinDto dto = null;
        ShyGenyinQr shyGenyinQr = null;
        for (BaziInfo baziInfo: baziInfos){
            BaZi bazi = new BaZi.Builder().setYear(baziInfo.getYear())
                    .setMonth(baziInfo.getMonth()).setDay(baziInfo.getDay())
                    .setHour(baziInfo.getHour()).build();
            dto = qiangRuoService.getQiangRuo(session,bazi);
            shyGenyinQr = convert2Shy(dto);
            shyGenyinQr.setId(baziInfo.getFullBazi());
            //
            shyGenyinQrService.insertSelective(shyGenyinQr);
        }
        session.dispose();
        long end = System.currentTimeMillis();
        log.info("日柱{}:数量{},查询耗时{}", day.display(),baziInfos.size(), end-start);
        return true;
    }

再看CPU负载如下,降低很多了。
2
上图还有一个耗时的22.0% - 540 s org.apache.commons.beanutils.BeanUtils.copyProperties,这个地方用到了一个存在性能问题,参见几种copyProperties工具类性能比较
改用org.springframework.beans.BeanUtils,住一个这个跟apache的顺序刚好相反。再次测试发现spring的BeanUtils性能真是好。
1
换成我的i5-9400,发现线程是Net I/O状态,
2
查看CPU,打log都消耗了3.2%的CPU,如果想更快,这个块的日志都可以关掉
3

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Drools 是一个开源的规则引擎,用于实现业务规则的管理和执行。Drools 可以帮助我们将业务规则从应用程序中分离出来,以便于灵活地进行修改和管理。Drools 提供了一个基于规则(Rule-Based)的编程范式,通过编写规则文件,我们可以描述出各种各样的业务规则,例如条件语句、循环语句、计算等。Drools 还提供了强大的模式匹配和推理逻辑的功能,能够快速地对大量的数据进行处理和分析。 《Drools 入门 PDF》是一本针对 Drools 初学者编写的入门指南。该书通过简单易懂的语言和丰富的实例,介绍了 Drools 的基本概念、语法和应用。读者可以通过该书学习到如何使用 Drools 进行规则的创建和管理,如何通过规则引擎实现业务逻辑的自动化,以及如何优化规则引擎的性能等方面的知识。 《Drools 入门 PDF》可以帮助读者快速入门和掌握 Drools 的基本用法,为后续深入学习和实践打下良好的基础。该书适用于对规则引擎和业务规则感兴趣的开发人员、架构师和系统分析师等。无论是想要在工作中应用规则引擎,还是希望通过规则引擎提升系统性能和灵活性的读者,都可以从《Drools 入门 PDF》中获得有价值的指导和帮助。 总之,如果你想快速入门 Drools,并掌握规则引擎的相关知识和技能,那么《Drools 入门 PDF》是一个值得阅读的指南。通过学习该书,你将能够更好地理解和应用 Drools,提升自己在规则引擎领域的能力和竞争力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

warrah

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值