业务上运营提前建好规则,根据广告主等级、业务类型等属性给广告主推荐不同的人群,通过规则引擎来实现,可避免代码中出现过多的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变化,重新加载规则