基于Drools的人群推荐实现方案

业务上运营提前建好规则,根据广告主等级、业务类型等属性给广告主推荐不同的人群,通过规则引擎来实现,可避免代码中出现过多的if else判断

加载规则

1、从ClassPath下加载DRL文件头,比如引入的类型、定义的全局变量

private void loadRuleTemplate() throws IOException {
    ClassPathResource template = new ClassPathResource("rule/templ.drl");
    InputStream in = template.getInputStream();
    BufferedReader bf = new BufferedReader(new InputStreamReader(in));
    StringBuilder sb = new StringBuilder();
    String line;
    while ((line = bf.readLine())!= null) {
        sb.append(line);
        sb.append("\n");
    }
    bf.close();
    in.close();
    ruleTemplate = sb.toString();
}

DRL文件头可能是这样的:
package rules
import com.xxx.model.RuleModel

import java.util.List
import org.slf4j.LoggerFactory
import org.slf4j.Logger

global com.xxx.RuleEngineServiceImpl service

2、 从DB中加载运营创建的规则,以面向对象的方式由每个Entity拼接每条规则对应的DSL

private String loadRulesFromDB() {
    List ruleModels = ruleService.listRules(new RuleQueryModel());
    List droolsDsl = new ArrayList<>();
    ruleModels.forEach(r->droolsDsl.add(r.generateDSL()));
    String template = ruleTemplate + String.join("\n\n", droolsDsl);
    LOG.info("rules:\n{}", template);
    return template;
}

业务对象生成drools规则:

public String generateDSL() {
    String ruleName = "rule-" + id;
    String code = "rule \"%s\" \nagenda-group \"user-group-rules\"\nno-loop true\nlock-on-active true\nwhen\n    %s \nthen\n    %s \nend";
    return String.format(code, ruleName, this.generateWhen(), this.generateThen());
}

public String generateWhen() {
    return String.format("$rcd: RuleConditionDecision(primaryBrandFlow == PrimaryBrandFlow.%s, conversion == Conversion.%s, businessType == BusinessType.%s, gradeType == AdvertiserGradeType.%s)",
            brandFlow, conversion, businessType, gradeType);
}

public String generateThen() {
    String code = "Logger logger = LoggerFactory.getLogger(drools.getRule().getPackageName() + \".RuleEngine\");\n" +
            "    logger.info(\"Advertiser {}, fire rule {}, decision {}\", $rcd.getAdvertiser(), drools.getRule().getName(), TagType.%s);\n" +
            "    RuleModel decision = service.getRule(%d);\n" +
            "    if(service.validUserGroupTemplate($rcd.getAdvertiser(), decision)) {\n" +
            "      modify($rcd) { addDecision(decision) };\n"+
            "    }";
    return String.format(code, tagType, id);
}

3、编译规则文件,生成KieContainer

public void reloadRules() {
    KieHelper helper = new KieHelper();
    try {
        helper.addContent(loadRulesFromDB(), ResourceType.DRL);
        Results results = helper.verify();
        if (results.hasMessages(Message.Level.ERROR)) {
            LOG.error(results.getMessages().toString());
            return;
        }
        this.kieContainer = helper.getKieContainer();
        LOG.info("success to load rules");
    } catch (Exception e) {
        LOG.error("fail to reload rules", e);
    }
}

4、根据请求条件,生成KieSession,触发规则,产生决策

private KieSession getKieSession() {
    KieSession kieSession = this.kieContainer.newKieSession();
    kieSession.getAgenda().getAgendaGroup("user-group-rules").setFocus();
    kieSession.setGlobal("service", this);
    return kieSession;
}

public List recommendUserGroup(AdvertiserModel advertiser, Conversion conversion) {
    BusinessType bzType = conditionService.getBusinessType(advertiser);
    PrimaryBrandFlow brandFlow = conditionService.getPrimaryBrandFlow(advertiser);
    AdvertiserGradeType gradeType = conditionService.getGrade(advertiser);
    RuleCondition condition = new RuleCondition(bzType, brandFlow, gradeType, conversion);
    KieSession kieSession = getKieSession();
    RuleConditionDecision cd = new RuleConditionDecision(advertiser, condition);
    kieSession.insert(cd);
    int count = kieSession.fireAllRules();
    kieSession.dispose();
    LOG.info("Advertiser {} fire {} rules!", advertiser.getAdvertiserId(), count);
    return cd.getDecisions();
}

5、可在Zookeeper上记录规则版本,如果规则有变动,监控zk变化,重新加载规则

你可能感兴趣的:(Java)