SpringBoot + 规则执行统计 + 热点规则识别:高频调用规则自动标记,优化性能瓶颈

背景:规则引擎的性能挑战

在现代应用中,规则引擎被广泛应用于各种场景,如:

  • 风控系统:实时风控规则评估
  • 营销系统:个性化推荐规则
  • 业务系统:业务规则引擎
  • 决策系统:智能决策规则

然而,随着规则数量的增加和调用频率的提高,规则引擎面临着严峻的性能挑战:

  • 执行延迟:规则执行耗时增加,影响系统响应速度
  • 资源消耗:高频规则占用大量系统资源
  • 性能瓶颈:部分规则成为系统性能瓶颈
  • 难以优化:无法快速识别需要优化的规则

本文将介绍如何使用 SpringBoot 实现规则执行统计和热点规则识别,自动标记高频调用的规则,从而精准定位性能瓶颈并进行优化。

核心概念

1. 规则执行统计

规则执行统计是指对规则执行的各种指标进行收集和分析,包括:

统计指标说明作用
调用次数规则被调用的总次数识别高频规则
执行时间规则执行的总时间和平均时间识别耗时规则
成功率规则执行成功的比例识别异常规则
内存占用规则执行的内存消耗识别内存密集型规则
CPU 使用率规则执行的 CPU 消耗识别 CPU 密集型规则

2. 热点规则

热点规则是指那些被高频调用、执行耗时较长或资源消耗较大的规则。这些规则通常是系统的性能瓶颈,需要优先优化。

热点类型定义优化策略
高频调用单位时间内调用次数超过阈值缓存结果、优化算法
高耗时平均执行时间超过阈值算法优化、并行处理
高内存内存占用超过阈值内存优化、对象池
高 CPUCPU 使用率超过阈值算法优化、异步处理

3. 热点规则识别

热点规则识别是指通过统计数据自动识别出需要优化的规则,通常采用以下策略:

  • 阈值法:设定阈值,超过阈值的规则被标记为热点
  • 百分位法:将规则按统计指标排序,前 N% 的规则被标记为热点
  • 趋势法:分析规则执行指标的变化趋势,识别异常增长的规则
  • 组合法:综合多个指标进行热点识别

技术实现

1. 核心实体类

// 规则实体
@Data
@Entity
@Table(name = "rule")
public class Rule {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "rule_id", unique = true, nullable = false, length = 64)
    private String ruleId; // 规则ID
    
    @Column(name = "rule_name", nullable = false, length = 128)
    private String ruleName; // 规则名称
    
    @Column(name = "rule_type", nullable = false, length = 32)
    private String ruleType; // 规则类型
    
    @Column(name = "rule_content", columnDefinition = "TEXT", nullable = false)
    private String ruleContent; // 规则内容(如规则表达式)
    
    @Column(name = "description", length = 512)
    private String description; // 规则描述
    
    @Column(name = "is_hot", nullable = false)
    private Boolean isHot = false; // 是否为热点规则
    
    @Column(name = "priority", nullable = false)
    private Integer priority = 0; // 规则优先级
    
    @CreationTimestamp
    @Column(name = "create_time", nullable = false)
    private Date createTime;
    
    @UpdateTimestamp
    @Column(name = "update_time", nullable = false)
    private Date updateTime;
}

// 规则执行统计实体
@Data
@Entity
@Table(name = "rule_execution_statistics")
public class RuleExecutionStatistics {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "rule_id", nullable = false, length = 64)
    private String ruleId; // 规则ID
    
    @Column(name = "total_calls", nullable = false)
    private Long totalCalls = 0L; // 总调用次数
    
    @Column(name = "total_time", nullable = false)
    private Long totalTime = 0L; // 总执行时间(毫秒)
    
    @Column(name = "avg_time", nullable = false)
    private Double avgTime = 0.0; // 平均执行时间(毫秒)
    
    @Column(name = "max_time", nullable = false)
    private Long maxTime = 0L; // 最大执行时间(毫秒)
    
    @Column(name = "min_time", nullable = false)
    private Long minTime = 0L; // 最小执行时间(毫秒)
    
    @Column(name = "success_count", nullable = false)
    private Long successCount = 0L; // 成功次数
    
    @Column(name = "failure_count", nullable = false)
    private Long failureCount = 0L; // 失败次数
    
    @Column(name = "success_rate", nullable = false)
    private Double successRate = 0.0; // 成功率
    
    @Column(name = "memory_usage", nullable = false)
    private Long memoryUsage = 0L; // 内存占用(字节)
    
    @Column(name = "cpu_usage", nullable = false)
    private Double cpuUsage = 0.0; // CPU 使用率(%)
    
    @Column(name = "last_executed_time")
    private Date lastExecutedTime; // 最后执行时间
    
    @Column(name = "period_start_time")
    private Date periodStartTime; // 统计周期开始时间
    
    @Column(name = "period_end_time")
    private Date periodEndTime; // 统计周期结束时间
    
    @UpdateTimestamp
    @Column(name = "update_time", nullable = false)
    private Date updateTime;
}

// 热点规则实体
@Data
@Entity
@Table(name = "hot_rule")
public class HotRule {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "rule_id", nullable = false, length = 64)
    private String ruleId; // 规则ID
    
    @Column(name = "rule_name", nullable = false, length = 128)
    private String ruleName; // 规则名称
    
    @Column(name = "hot_type", nullable = false, length = 32)
    private String hotType; // 热点类型:HIGH_CALL/HIGH_TIME/HIGH_MEMORY/HIGH_CPU
    
    @Column(name = "hot_value", nullable = false)
    private Double hotValue; // 热点值
    
    @Column(name = "threshold", nullable = false)
    private Double threshold; // 阈值
    
    @Column(name = "priority", nullable = false)
    private Integer priority = 0; // 优化优先级
    
    @Column(name = "status", nullable = false, length = 32)
    private String status = "PENDING"; // 状态:PENDING/OPTIMIZED/IGNORED
    
    @Column(name = "reason", length = 512)
    private String reason; // 热点原因
    
    @Column(name = "suggestion", length = 1024)
    private String suggestion; // 优化建议
    
    @CreationTimestamp
    @Column(name = "create_time", nullable = false)
    private Date createTime;
    
    @UpdateTimestamp
    @Column(name = "update_time", nullable = false)
    private Date updateTime;
}

2. 规则执行统计服务

@Service
@Slf4j
public class RuleExecutionStatisticsService {
    
    @Autowired
    private RuleExecutionStatisticsRepository statisticsRepository;
    
    @Autowired
    private RuleRepository ruleRepository;
    
    @Autowired
    private HotRuleService hotRuleService;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Value("${rule.statistics.window:3600000}")
    private Long statisticsWindow; // 统计窗口(毫秒)
    
    @Value("${rule.statistics.batch-size:100}")
    private Integer batchSize; // 批量更新大小
    
    private final ConcurrentHashMap<String, AtomicLong> callCounter = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, AtomicLong> timeCounter = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, AtomicLong> successCounter = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, AtomicLong> failureCounter = new ConcurrentHashMap<>();
    
    /**
     * 记录规则执行
     */
    public void recordExecution(String ruleId, long executionTime, boolean success) {
        // 1. 实时统计(内存)
        callCounter.computeIfAbsent(ruleId, k -> new AtomicLong()).incrementAndGet();
        timeCounter.computeIfAbsent(ruleId, k -> new AtomicLong()).addAndGet(executionTime);
        
        if (success) {
            successCounter.computeIfAbsent(ruleId, k -> new AtomicLong()).incrementAndGet();
        } else {
            failureCounter.computeIfAbsent(ruleId, k -> new AtomicLong()).incrementAndGet();
        }
        
        // 2. 异步更新到数据库
        CompletableFuture.runAsync(() -> {
            try {
                updateStatistics(ruleId, executionTime, success);
            } catch (Exception e) {
                log.error("Failed to update statistics for rule {}", ruleId, e);
            }
        });
    }
    
    /**
     * 更新统计信息
     */
    private void updateStatistics(String ruleId, long executionTime, boolean success) {
        // 1. 获取或创建统计记录
        RuleExecutionStatistics statistics = statisticsRepository.findByRuleId(ruleId)
                .orElseGet(() -> createStatistics(ruleId));
        
        // 2. 更新统计数据
        statistics.setTotalCalls(statistics.getTotalCalls() + 1);
        statistics.setTotalTime(statistics.getTotalTime() + executionTime);
        statistics.setAvgTime(statistics.getTotalTime() * 1.0 / statistics.getTotalCalls());
        statistics.setMaxTime(Math.max(statistics.getMaxTime(), executionTime));
        statistics.setMinTime(statistics.getMinTime() == 0 ? executionTime : Math.min(statistics.getMinTime(), executionTime));
        
        if (success) {
            statistics.setSuccessCount(statistics.getSuccessCount() + 1);
        } else {
            statistics.setFailureCount(statistics.getFailureCount() + 1);
        }
        
        statistics.setSuccessRate(statistics.getTotalCalls() > 0 
                ? statistics.getSuccessCount() * 100.0 / statistics.getTotalCalls() 
                : 0.0);
        
        statistics.setLastExecutedTime(new Date());
        
        // 3. 保存统计数据
        statisticsRepository.save(statistics);
        
        // 4. 检查是否为热点规则
        checkHotRule(statistics);
    }
    
    /**
     * 创建统计记录
     */
    private RuleExecutionStatistics createStatistics(String ruleId) {
        RuleExecutionStatistics statistics = new RuleExecutionStatistics();
        statistics.setRuleId(ruleId);
        statistics.setTotalCalls(0L);
        statistics.setTotalTime(0L);
        statistics.setAvgTime(0.0);
        statistics.setMaxTime(0L);
        statistics.setMinTime(0L);
        statistics.setSuccessCount(0L);
        statistics.setFailureCount(0L);
        statistics.setSuccessRate(0.0);
        statistics.setMemoryUsage(0L);
        statistics.setCpuUsage(0.0);
        statistics.setPeriodStartTime(new Date());
        return statistics;
    }
    
    /**
     * 检查是否为热点规则
     */
    private void checkHotRule(RuleExecutionStatistics statistics) {
        // 1. 获取规则信息
        Optional<Rule> ruleOptional = ruleRepository.findByRuleId(statistics.getRuleId());
        if (!ruleOptional.isPresent()) {
            return;
        }
        
        Rule rule = ruleOptional.get();
        
        // 2. 检查是否为高频调用
        if (statistics.getTotalCalls() > 1000) { // 示例阈值
            hotRuleService.markHotRule(rule, "HIGH_CALL", statistics.getTotalCalls(), 1000.0, 
                    "调用次数过高", "考虑缓存规则结果或优化规则逻辑");
        }
        
        // 3. 检查是否为高耗时
        if (statistics.getAvgTime() > 100) { // 示例阈值(毫秒)
            hotRuleService.markHotRule(rule, "HIGH_TIME", statistics.getAvgTime(), 100.0, 
                    "执行时间过长", "优化规则算法或考虑并行处理");
        }
        
        // 4. 检查是否为高内存
        if (statistics.getMemoryUsage() > 1024 * 1024) { // 示例阈值(字节)
            hotRuleService.markHotRule(rule, "HIGH_MEMORY", statistics.getMemoryUsage(), 1024 * 1024.0, 
                    "内存占用过高", "优化内存使用或使用对象池");
        }
        
        // 5. 检查是否为高 CPU
        if (statistics.getCpuUsage() > 50) { // 示例阈值(%)
            hotRuleService.markHotRule(rule, "HIGH_CPU", statistics.getCpuUsage(), 50.0, 
                    "CPU 使用率过高", "优化算法或考虑异步处理");
        }
    }
    
    /**
     * 获取规则执行统计
     */
    public RuleExecutionStatistics getStatistics(String ruleId) {
        return statisticsRepository.findByRuleId(ruleId).orElse(null);
    }
    
    /**
     * 获取热点规则统计
     */
    public List<RuleExecutionStatistics> getHotRules() {
        return statisticsRepository.findTop10ByOrderByTotalCallsDesc();
    }
    
    /**
     * 重置统计数据
     */
    @Scheduled(cron = "0 0 0 * * ?") // 每天凌晨重置
    public void resetStatistics() {
        List<RuleExecutionStatistics> statisticsList = statisticsRepository.findAll();
        for (RuleExecutionStatistics statistics : statisticsList) {
            statistics.setTotalCalls(0L);
            statistics.setTotalTime(0L);
            statistics.setAvgTime(0.0);
            statistics.setMaxTime(0L);
            statistics.setMinTime(0L);
            statistics.setSuccessCount(0L);
            statistics.setFailureCount(0L);
            statistics.setSuccessRate(0.0);
            statistics.setMemoryUsage(0L);
            statistics.setCpuUsage(0.0);
            statistics.setPeriodStartTime(new Date());
            statisticsRepository.save(statistics);
        }
        
        // 清空内存计数器
        callCounter.clear();
        timeCounter.clear();
        successCounter.clear();
        failureCounter.clear();
        
        log.info("Statistics reset completed");
    }
    
    /**
     * 批量更新统计数据
     */
    @Scheduled(fixedRate = 60000) // 每分钟批量更新
    public void batchUpdateStatistics() {
        List<RuleExecutionStatistics> statisticsList = new ArrayList<>();
        
        for (Map.Entry<String, AtomicLong> entry : callCounter.entrySet()) {
            String ruleId = entry.getKey();
            long calls = entry.getValue().get();
            
            if (calls > 0) {
                RuleExecutionStatistics statistics = statisticsRepository.findByRuleId(ruleId)
                        .orElseGet(() -> createStatistics(ruleId));
                
                statistics.setTotalCalls(statistics.getTotalCalls() + calls);
                statistics.setTotalTime(statistics.getTotalTime() + timeCounter.getOrDefault(ruleId, new AtomicLong(0)).get());
                statistics.setSuccessCount(statistics.getSuccessCount() + successCounter.getOrDefault(ruleId, new AtomicLong(0)).get());
                statistics.setFailureCount(statistics.getFailureCount() + failureCounter.getOrDefault(ruleId, new AtomicLong(0)).get());
                
                if (statistics.getTotalCalls() > 0) {
                    statistics.setAvgTime(statistics.getTotalTime() * 1.0 / statistics.getTotalCalls());
                    statistics.setSuccessRate(statistics.getSuccessCount() * 100.0 / statistics.getTotalCalls());
                }
                
                statistics.setLastExecutedTime(new Date());
                statisticsList.add(statistics);
                
                // 清空计数器
                callCounter.put(ruleId, new AtomicLong(0));
                timeCounter.put(ruleId, new AtomicLong(0));
                successCounter.put(ruleId, new AtomicLong(0));
                failureCounter.put(ruleId, new AtomicLong(0));
            }
        }
        
        if (!statisticsList.isEmpty()) {
            // 批量保存
            for (int i = 0; i < statisticsList.size(); i += batchSize) {
                int end = Math.min(i + batchSize, statisticsList.size());
                statisticsRepository.saveAll(statisticsList.subList(i, end));
            }
            
            log.info("Batch updated statistics for {} rules", statisticsList.size());
        }
    }
}

3. 热点规则识别服务

@Service
@Slf4j
public class HotRuleService {
    
    @Autowired
    private HotRuleRepository hotRuleRepository;
    
    @Autowired
    private RuleRepository ruleRepository;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Value("${rule.hot.threshold.call:1000}")
    private Integer callThreshold;
    
    @Value("${rule.hot.threshold.time:100}")
    private Integer timeThreshold;
    
    @Value("${rule.hot.threshold.memory:1048576}")
    private Long memoryThreshold;
    
    @Value("${rule.hot.threshold.cpu:50}")
    private Double cpuThreshold;
    
    /**
     * 标记热点规则
     */
    public void markHotRule(Rule rule, String hotType, double hotValue, double threshold, String reason, String suggestion) {
        // 1. 检查是否已经标记为热点
        Optional<HotRule> existingHotRule = hotRuleRepository.findByRuleIdAndHotType(rule.getRuleId(), hotType);
        
        if (existingHotRule.isPresent()) {
            // 更新热点规则
            HotRule hotRule = existingHotRule.get();
            hotRule.setHotValue(hotValue);
            hotRule.setPriority(calculatePriority(hotValue, threshold));
            hotRule.setReason(reason);
            hotRule.setSuggestion(suggestion);
            hotRule.setStatus("PENDING");
            hotRuleRepository.save(hotRule);
        } else {
            // 创建新的热点规则
            HotRule hotRule = new HotRule();
            hotRule.setRuleId(rule.getRuleId());
            hotRule.setRuleName(rule.getRuleName());
            hotRule.setHotType(hotType);
            hotRule.setHotValue(hotValue);
            hotRule.setThreshold(threshold);
            hotRule.setPriority(calculatePriority(hotValue, threshold));
            hotRule.setStatus("PENDING");
            hotRule.setReason(reason);
            hotRule.setSuggestion(suggestion);
            hotRuleRepository.save(hotRule);
        }
        
        // 2. 更新规则的热点标记
        rule.setIsHot(true);
        ruleRepository.save(rule);
        
        log.info("Marked rule {} as hot: {}, value: {}", rule.getRuleId(), hotType, hotValue);
    }
    
    /**
     * 计算优先级
     */
    private Integer calculatePriority(double hotValue, double threshold) {
        double ratio = hotValue / threshold;
        if (ratio > 5) {
            return 5; // 最高优先级
        } else if (ratio > 3) {
            return 4;
        } else if (ratio > 2) {
            return 3;
        } else if (ratio > 1.5) {
            return 2;
        } else {
            return 1; // 最低优先级
        }
    }
    
    /**
     * 获取热点规则列表
     */
    public List<HotRule> getHotRules() {
        return hotRuleRepository.findByStatusOrderByPriorityDesc("PENDING");
    }
    
    /**
     * 获取热点规则详情
     */
    public HotRule getHotRule(Long id) {
        return hotRuleRepository.findById(id).orElse(null);
    }
    
    /**
     * 更新热点规则状态
     */
    public void updateHotRuleStatus(Long id, String status) {
        HotRule hotRule = hotRuleRepository.findById(id).orElse(null);
        if (hotRule != null) {
            hotRule.setStatus(status);
            hotRuleRepository.save(hotRule);
        }
    }
    
    /**
     * 清理过期热点规则
     */
    @Scheduled(cron = "0 0 0 * * ?") // 每天凌晨清理
    public void cleanupHotRules() {
        // 清理已优化或忽略的热点规则(超过7天)
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, -7);
        Date cutoffDate = calendar.getTime();
        
        List<HotRule> hotRules = hotRuleRepository.findByStatusInAndCreateTimeBefore(
                Arrays.asList("OPTIMIZED", "IGNORED"), cutoffDate);
        
        for (HotRule hotRule : hotRules) {
            hotRuleRepository.delete(hotRule);
        }
        
        log.info("Cleaned up {} expired hot rules", hotRules.size());
    }
    
    /**
     * 分析热点规则趋势
     */
    public List<HotRuleTrend> analyzeHotRuleTrends() {
        // 分析热点规则的变化趋势
        // 实现略
        return new ArrayList<>();
    }
}

4. 规则执行引擎

@Service
@Slf4j
public class RuleEngineService {
    
    @Autowired
    private RuleRepository ruleRepository;
    
    @Autowired
    private RuleExecutionStatisticsService statisticsService;
    
    @Autowired
    private RuleCacheService ruleCacheService;
    
    /**
     * 执行规则
     */
    public RuleExecutionResult executeRule(String ruleId, Map<String, Object> context) {
        long startTime = System.currentTimeMillis();
        RuleExecutionResult result = new RuleExecutionResult();
        
        try {
            // 1. 从缓存获取规则
            Rule rule = ruleCacheService.getRule(ruleId);
            if (rule == null) {
                // 从数据库获取
                Optional<Rule> ruleOptional = ruleRepository.findByRuleId(ruleId);
                if (!ruleOptional.isPresent()) {
                    result.setSuccess(false);
                    result.setMessage("规则不存在");
                    return result;
                }
                rule = ruleOptional.get();
                // 加入缓存
                ruleCacheService.putRule(rule);
            }
            
            // 2. 执行规则
            boolean success = doExecuteRule(rule, context);
            result.setSuccess(success);
            
            // 3. 记录执行结果
            long executionTime = System.currentTimeMillis() - startTime;
            statisticsService.recordExecution(ruleId, executionTime, success);
            
        } catch (Exception e) {
            log.error("Failed to execute rule {}", ruleId, e);
            result.setSuccess(false);
            result.setMessage(e.getMessage());
            
            // 记录失败
            long executionTime = System.currentTimeMillis() - startTime;
            statisticsService.recordExecution(ruleId, executionTime, false);
        }
        
        return result;
    }
    
    /**
     * 执行规则逻辑
     */
    private boolean doExecuteRule(Rule rule, Map<String, Object> context) {
        // 这里实现具体的规则执行逻辑
        // 示例:执行规则表达式
        try {
            // 解析规则内容
            String ruleContent = rule.getRuleContent();
            
            // 执行规则
            // 实际实现中可能会使用规则引擎库,如 Drools、EasyRules 等
            
            // 模拟规则执行
            Thread.sleep(10); // 模拟执行时间
            
            return true;
        } catch (Exception e) {
            log.error("Error executing rule {}", rule.getRuleId(), e);
            return false;
        }
    }
    
    /**
     * 批量执行规则
     */
    public List<RuleExecutionResult> executeRules(List<String> ruleIds, Map<String, Object> context) {
        List<RuleExecutionResult> results = new ArrayList<>();
        
        for (String ruleId : ruleIds) {
            RuleExecutionResult result = executeRule(ruleId, context);
            results.add(result);
        }
        
        return results;
    }
    
    /**
     * 执行规则集
     */
    public RuleSetExecutionResult executeRuleSet(String ruleSetId, Map<String, Object> context) {
        RuleSetExecutionResult result = new RuleSetExecutionResult();
        
        // 获取规则集中的规则
        List<Rule> rules = getRulesByRuleSet(ruleSetId);
        List<RuleExecutionResult> executionResults = new ArrayList<>();
        
        long startTime = System.currentTimeMillis();
        
        for (Rule rule : rules) {
            RuleExecutionResult executionResult = executeRule(rule.getRuleId(), context);
            executionResults.add(executionResult);
        }
        
        result.setExecutionResults(executionResults);
        result.setTotalTime(System.currentTimeMillis() - startTime);
        result.setSuccess(executionResults.stream().allMatch(RuleExecutionResult::isSuccess));
        
        return result;
    }
    
    /**
     * 根据规则集获取规则
     */
    private List<Rule> getRulesByRuleSet(String ruleSetId) {
        // 实现略
        return new ArrayList<>();
    }
}

5. 规则缓存服务

@Service
@Slf4j
public class RuleCacheService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Value("${rule.cache.ttl:3600}")
    private Integer cacheTTL; // 缓存过期时间(秒)
    
    private final Cache<String, Rule> localCache = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .build();
    
    /**
     * 从缓存获取规则
     */
    public Rule getRule(String ruleId) {
        // 1. 从本地缓存获取
        Rule rule = localCache.getIfPresent(ruleId);
        if (rule != null) {
            return rule;
        }
        
        // 2. 从 Redis 缓存获取
        String key = "rule:" + ruleId;
        String ruleJson = redisTemplate.opsForValue().get(key);
        if (ruleJson != null) {
            try {
                rule = objectMapper.readValue(ruleJson, Rule.class);
                // 放入本地缓存
                localCache.put(ruleId, rule);
                return rule;
            } catch (Exception e) {
                log.error("Failed to deserialize rule {}", ruleId, e);
            }
        }
        
        return null;
    }
    
    /**
     * 放入缓存
     */
    public void putRule(Rule rule) {
        try {
            // 1. 放入本地缓存
            localCache.put(rule.getRuleId(), rule);
            
            // 2. 放入 Redis 缓存
            String key = "rule:" + rule.getRuleId();
            String ruleJson = objectMapper.writeValueAsString(rule);
            redisTemplate.opsForValue().set(key, ruleJson, cacheTTL, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("Failed to cache rule {}", rule.getRuleId(), e);
        }
    }
    
    /**
     * 从缓存删除规则
     */
    public void removeRule(String ruleId) {
        // 1. 从本地缓存删除
        localCache.invalidate(ruleId);
        
        // 2. 从 Redis 缓存删除
        String key = "rule:" + ruleId;
        redisTemplate.delete(key);
    }
    
    /**
     * 批量获取规则
     */
    public Map<String, Rule> getRules(List<String> ruleIds) {
        Map<String, Rule> rules = new HashMap<>();
        
        for (String ruleId : ruleIds) {
            Rule rule = getRule(ruleId);
            if (rule != null) {
                rules.put(ruleId, rule);
            }
        }
        
        return rules;
    }
}

6. 规则执行控制器

@RestController
@RequestMapping("/api/rule")
@Slf4j
public class RuleController {
    
    @Autowired
    private RuleEngineService ruleEngineService;
    
    @Autowired
    private RuleExecutionStatisticsService statisticsService;
    
    @Autowired
    private HotRuleService hotRuleService;
    
    @Autowired
    private RuleRepository ruleRepository;
    
    /**
     * 执行规则
     */
    @PostMapping("/execute/{ruleId}")
    public Result<RuleExecutionResult> executeRule(@PathVariable String ruleId, @RequestBody Map<String, Object> context) {
        RuleExecutionResult result = ruleEngineService.executeRule(ruleId, context);
        return Result.success(result);
    }
    
    /**
     * 批量执行规则
     */
    @PostMapping("/execute/batch")
    public Result<List<RuleExecutionResult>> executeRules(@RequestBody RuleBatchRequest request) {
        List<RuleExecutionResult> results = ruleEngineService.executeRules(request.getRuleIds(), request.getContext());
        return Result.success(results);
    }
    
    /**
     * 执行规则集
     */
    @PostMapping("/execute/set/{ruleSetId}")
    public Result<RuleSetExecutionResult> executeRuleSet(@PathVariable String ruleSetId, @RequestBody Map<String, Object> context) {
        RuleSetExecutionResult result = ruleEngineService.executeRuleSet(ruleSetId, context);
        return Result.success(result);
    }
    
    /**
     * 获取规则执行统计
     */
    @GetMapping("/statistics/{ruleId}")
    public Result<RuleExecutionStatistics> getStatistics(@PathVariable String ruleId) {
        RuleExecutionStatistics statistics = statisticsService.getStatistics(ruleId);
        return Result.success(statistics);
    }
    
    /**
     * 获取热点规则列表
     */
    @GetMapping("/hot")
    public Result<List<HotRule>> getHotRules() {
        List<HotRule> hotRules = hotRuleService.getHotRules();
        return Result.success(hotRules);
    }
    
    /**
     * 获取热点规则详情
     */
    @GetMapping("/hot/{id}")
    public Result<HotRule> getHotRule(@PathVariable Long id) {
        HotRule hotRule = hotRuleService.getHotRule(id);
        return Result.success(hotRule);
    }
    
    /**
     * 更新热点规则状态
     */
    @PutMapping("/hot/{id}/status")
    public Result<String> updateHotRuleStatus(@PathVariable Long id, @RequestParam String status) {
        hotRuleService.updateHotRuleStatus(id, status);
        return Result.success("状态更新成功");
    }
    
    /**
     * 获取规则列表
     */
    @GetMapping("/list")
    public Result<List<Rule>> getRules() {
        List<Rule> rules = ruleRepository.findAll();
        return Result.success(rules);
    }
    
    /**
     * 获取规则详情
     */
    @GetMapping("/{ruleId}")
    public Result<Rule> getRule(@PathVariable String ruleId) {
        Optional<Rule> ruleOptional = ruleRepository.findByRuleId(ruleId);
        if (!ruleOptional.isPresent()) {
            return Result.error("规则不存在");
        }
        return Result.success(ruleOptional.get());
    }
}

核心流程

1. 规则执行流程

  1. 接收执行请求:客户端请求执行规则
  2. 获取规则:从缓存或数据库获取规则
  3. 执行规则:执行规则逻辑
  4. 记录统计:记录执行时间、结果等统计信息
  5. 检查热点:检查是否为热点规则
  6. 返回结果:返回执行结果

2. 热点规则识别流程

  1. 收集统计数据:收集规则执行的各种统计数据
  2. 分析统计数据:分析统计数据,识别热点规则
  3. 标记热点规则:将识别出的热点规则标记为热点
  4. 生成优化建议:根据热点类型生成优化建议
  5. 更新规则状态:更新规则的热点状态

技术要点

1. 统计数据收集

  • 实时统计:使用内存计数器实时收集统计数据
  • 批量更新:定期批量更新统计数据到数据库
  • 分布式统计:支持分布式环境下的统计数据收集
  • 统计窗口:支持设置统计窗口,避免历史数据影响

2. 热点规则识别

  • 多维度识别:从调用次数、执行时间、内存占用、CPU 使用率等多个维度识别热点
  • 动态阈值:支持动态调整热点识别阈值
  • 优先级计算:根据热点程度计算优化优先级
  • 趋势分析:分析热点规则的变化趋势

3. 性能优化

  • 规则缓存:使用本地缓存 + Redis 缓存规则,减少数据库访问
  • 异步处理:异步更新统计数据,不影响规则执行性能
  • 批量操作:批量更新统计数据,减少数据库操作次数
  • 内存优化:使用原子操作和并发集合,减少内存开销

4. 监控告警

  • 热点规则告警:当识别出热点规则时发送告警
  • 性能瓶颈告警:当规则执行性能下降时发送告警
  • 资源使用告警:当规则资源使用异常时发送告警
  • 统计数据异常告警:当统计数据异常时发送告警

最佳实践

1. 规则设计最佳实践

  • 规则粒度:规则粒度要适中,避免过大或过小
  • 规则复杂度:控制规则复杂度,避免过于复杂的规则
  • 规则依赖:减少规则之间的依赖,提高规则执行效率
  • 规则版本:支持规则版本管理,便于回滚和对比

2. 统计配置最佳实践

  • 统计窗口:根据业务需求设置合适的统计窗口
  • 批量大小:根据系统负载设置合适的批量更新大小
  • 阈值设置:根据系统性能设置合适的热点识别阈值
  • 缓存策略:根据规则更新频率设置合适的缓存策略

3. 热点规则优化最佳实践

  • 缓存结果:对于高频调用的规则,缓存规则执行结果
  • 算法优化:优化规则执行算法,减少执行时间
  • 并行处理:对于耗时较长的规则,考虑并行处理
  • 资源隔离:对于资源密集型规则,进行资源隔离
  • 规则拆分:将复杂规则拆分为多个简单规则

4. 监控告警最佳实践

  • 监控指标:监控规则执行的关键指标
  • 告警阈值:设置合理的告警阈值
  • 告警级别:根据热点程度设置不同的告警级别
  • 告警渠道:支持多种告警渠道,如邮件、短信、钉钉等

常见问题

1. 统计数据不准确

问题:统计数据与实际执行情况不符

解决方案

  • 检查统计数据收集逻辑
  • 确保异步更新机制正常工作
  • 检查分布式环境下的统计数据一致性
  • 定期验证统计数据的准确性

2. 热点规则误识别

问题:某些规则被错误标记为热点规则

解决方案

  • 调整热点识别阈值
  • 优化热点识别算法
  • 考虑规则的业务重要性
  • 增加人工审核环节

3. 性能影响

问题:统计和热点识别影响规则执行性能

解决方案

  • 使用异步处理统计数据
  • 优化统计数据收集逻辑
  • 合理设置统计频率
  • 使用缓存减少数据库操作

4. 规则执行延迟

问题:规则执行延迟增加

解决方案

  • 优化规则执行算法
  • 使用规则缓存
  • 考虑并行执行规则
  • 识别并优化热点规则

代码优化建议

1. 统计数据收集优化

/**
 * 使用原子操作和批量更新优化统计数据收集
 */
private final ConcurrentHashMap<String, AtomicLong> callCounter = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, AtomicLong> timeCounter = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, AtomicLong> successCounter = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, AtomicLong> failureCounter = new ConcurrentHashMap<>();

@Scheduled(fixedRate = 60000) // 每分钟批量更新
public void batchUpdateStatistics() {
    List<RuleExecutionStatistics> statisticsList = new ArrayList<>();
    
    for (Map.Entry<String, AtomicLong> entry : callCounter.entrySet()) {
        String ruleId = entry.getKey();
        long calls = entry.getValue().get();
        
        if (calls > 0) {
            // 批量处理统计数据
            // ...
            
            // 重置计数器
            callCounter.put(ruleId, new AtomicLong(0));
            timeCounter.put(ruleId, new AtomicLong(0));
            successCounter.put(ruleId, new AtomicLong(0));
            failureCounter.put(ruleId, new AtomicLong(0));
        }
    }
    
    // 批量保存
    if (!statisticsList.isEmpty()) {
        for (int i = 0; i < statisticsList.size(); i += batchSize) {
            int end = Math.min(i + batchSize, statisticsList.size());
            statisticsRepository.saveAll(statisticsList.subList(i, end));
        }
    }
}

2. 热点规则识别优化

/**
 * 使用多维度分析优化热点规则识别
 */
public void checkHotRule(RuleExecutionStatistics statistics) {
    // 1. 计算热点得分
    double hotScore = calculateHotScore(statistics);
    
    // 2. 根据得分判断是否为热点规则
    if (hotScore > 80) { // 示例阈值
        // 标记为热点规则
        // ...
    }
}

/**
 * 计算热点得分
 */
private double calculateHotScore(RuleExecutionStatistics statistics) {
    double score = 0.0;
    
    // 调用次数得分(权重 0.4)
    double callScore = Math.min(statistics.getTotalCalls() / 10000.0, 1.0) * 40;
    
    // 执行时间得分(权重 0.3)
    double timeScore = Math.min(statistics.getAvgTime() / 1000.0, 1.0) * 30;
    
    // 成功率得分(权重 0.2)
    double successScore = (100 - statistics.getSuccessRate()) / 100.0 * 20;
    
    // 资源使用得分(权重 0.1)
    double resourceScore = Math.min(statistics.getCpuUsage() / 100.0, 1.0) * 10;
    
    score = callScore + timeScore + successScore + resourceScore;
    return score;
}

3. 规则缓存优化

/**
 * 使用多级缓存优化规则访问
 */
private final Cache<String, Rule> localCache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(5, TimeUnit.MINUTES)
        .build();

public Rule getRule(String ruleId) {
    // 1. 从本地缓存获取
    Rule rule = localCache.getIfPresent(ruleId);
    if (rule != null) {
        return rule;
    }
    
    // 2. 从 Redis 缓存获取
    String key = "rule:" + ruleId;
    String ruleJson = redisTemplate.opsForValue().get(key);
    if (ruleJson != null) {
        try {
            rule = objectMapper.readValue(ruleJson, Rule.class);
            // 放入本地缓存
            localCache.put(ruleId, rule);
            return rule;
        } catch (Exception e) {
            log.error("Failed to deserialize rule {}", ruleId, e);
        }
    }
    
    // 3. 从数据库获取
    // ...
    
    return null;
}

互动话题

  1. 你在实际项目中遇到过哪些规则引擎的性能问题?是如何解决的?
  2. 对于热点规则识别,你有什么更好的算法或策略?
  3. 在分布式环境下,如何保证统计数据的一致性?
  4. 你认为规则引擎的性能优化还有哪些方面可以改进?

欢迎在评论区交流讨论!


公众号:服务端技术精选,更多精彩请关注


标题:SpringBoot + 规则执行统计 + 热点规则识别:高频调用规则自动标记,优化性能瓶颈
作者:jiangyi
地址:http://jiangyi.space/articles/2026/03/22/1774080695466.html
公众号:服务端技术精选
    评论
    0 评论
avatar

取消