1. 为什么选择Janino构建动态规则引擎在Java生态中实现动态规则引擎的方案有很多比如Drools这样的重量级框架或者用Groovy这样的动态语言。但我在实际项目中发现Janino凭借其轻量级特性和原生Java语法支持往往是最佳选择。去年我们团队重构风控系统时就面临一个典型场景需要实时调整数十条业务规则且每次修改都要避免重启服务。当时测试了三种方案最终Janino以毫秒级的规则加载速度和零学习成本胜出。Janino的核心优势在于它直接编译标准Java代码。这意味着你的规则逻辑可以这样写if (user.getLevel() 3 transaction.getAmount() 5000) { return APPROVED; } else { return auditService.checkRisk(user, transaction); }完全就是普通Java代码不需要额外学习规则语法。相比之下其他方案要么需要引入新语言如Drools的DRL要么有严重的性能瓶颈如反射调用。2. 动态规则引擎的架构设计2.1 规则定义与存储在实际项目中我们通常采用JSON或数据库存储规则。比如电商促销系统的折扣规则可能是这样的结构{ ruleId: DISCOUNT_2023, condition: user.getVipLevel() 2 cart.getTotal() 1000, action: return cart.getTotal() * 0.15; }这里有个实用技巧建议把规则拆分为condition和action两部分。condition返回boolean决定是否触发规则action执行具体业务逻辑。这样既清晰又方便单独调试。2.2 规则编译与缓存直接每次执行都编译规则代码显然不现实。我们的解决方案是用Guava Cache构建二级缓存LoadingCacheString, CompiledRule ruleCache CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(1, TimeUnit.HOURS) .build(new CacheLoaderString, CompiledRule() { public CompiledRule load(String ruleDefinition) throws Exception { ScriptEvaluator evaluator new ScriptEvaluator(); evaluator.setParameters(...); evaluator.cook(ruleDefinition); return new CompiledRule(evaluator); } });实测下来这种方案在QPS 3000的场景下平均耗时仅比硬编码规则多1.2ms。3. 实战订单风控规则引擎3.1 完整实现步骤假设我们要实现一个电商订单风控系统当订单金额异常时触发审核。以下是核心代码骨架定义规则接口public interface RiskRule { boolean checkRisk(Order order, User user); }动态规则实现类public class JaninoRuleEngine { private final ConcurrentMapString, RiskRule ruleCache new ConcurrentHashMap(); public RiskRule compileRule(String condition) throws Exception { String sourceCode String.format( public class DynamicRule implements RiskRule { public boolean checkRisk(Order o, User u) { return %s; }}, condition); JavaSourceClassLoader loader new JavaSourceClassLoader(...); Class? clazz loader.loadClass(DynamicRule); return (RiskRule) clazz.newInstance(); } }使用示例// 新增一条规则VIP用户单笔消费超5万需审核 engine.compileRule( u.isVip() o.getAmount() 50000);3.2 性能优化技巧在压力测试中我们发现几个关键点预热缓存系统启动时加载高频规则参数优化Janino的CompilerSettings可以调整CompilerSettings settings new CompilerSettings(); settings.setWarningThreshold(0); // 关闭警告提升速度 evaluator.setCompilerSettings(settings);避免频繁GC重复使用ScriptEvaluator实例比每次都创建快30%4. 生产环境注意事项4.1 安全防护允许执行动态代码等于打开了潘多拉魔盒。我们采用的防护措施包括代码白名单检测if (ruleCode.contains(System.exit) || ruleCode.contains(Runtime.exec)) { throw new SecurityException(危险操作); }沙箱环境运行SecurityManager sm new RuleSecurityManager(); System.setSecurityManager(sm);4.2 监控与调试建议在规则执行时添加埋点long start System.nanoTime(); try { result evaluator.evaluate(params); metrics.recordSuccess(start); } catch (Exception e) { metrics.recordError(e.getClass()); throw e; }我们团队通过这套方案将风控规则的平均生效时间从原来的小时级缩短到秒级而且开发人员再也不用忍受Drools那晦涩的语法了。特别是在618大促期间快速调整规则的能力让我们成功拦截了90%以上的羊毛党订单。