SpringBoot + 规则执行统计 + 热点规则识别:高频调用规则自动标记,优化性能瓶颈
背景:规则引擎的性能挑战
在现代应用中,规则引擎被广泛应用于各种场景,如:
- 风控系统:实时风控规则评估
- 营销系统:个性化推荐规则
- 业务系统:业务规则引擎
- 决策系统:智能决策规则
然而,随着规则数量的增加和调用频率的提高,规则引擎面临着严峻的性能挑战:
- 执行延迟:规则执行耗时增加,影响系统响应速度
- 资源消耗:高频规则占用大量系统资源
- 性能瓶颈:部分规则成为系统性能瓶颈
- 难以优化:无法快速识别需要优化的规则
本文将介绍如何使用 SpringBoot 实现规则执行统计和热点规则识别,自动标记高频调用的规则,从而精准定位性能瓶颈并进行优化。
核心概念
1. 规则执行统计
规则执行统计是指对规则执行的各种指标进行收集和分析,包括:
| 统计指标 | 说明 | 作用 |
|---|---|---|
| 调用次数 | 规则被调用的总次数 | 识别高频规则 |
| 执行时间 | 规则执行的总时间和平均时间 | 识别耗时规则 |
| 成功率 | 规则执行成功的比例 | 识别异常规则 |
| 内存占用 | 规则执行的内存消耗 | 识别内存密集型规则 |
| CPU 使用率 | 规则执行的 CPU 消耗 | 识别 CPU 密集型规则 |
2. 热点规则
热点规则是指那些被高频调用、执行耗时较长或资源消耗较大的规则。这些规则通常是系统的性能瓶颈,需要优先优化。
| 热点类型 | 定义 | 优化策略 |
|---|---|---|
| 高频调用 | 单位时间内调用次数超过阈值 | 缓存结果、优化算法 |
| 高耗时 | 平均执行时间超过阈值 | 算法优化、并行处理 |
| 高内存 | 内存占用超过阈值 | 内存优化、对象池 |
| 高 CPU | CPU 使用率超过阈值 | 算法优化、异步处理 |
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. 规则执行流程
- 接收执行请求:客户端请求执行规则
- 获取规则:从缓存或数据库获取规则
- 执行规则:执行规则逻辑
- 记录统计:记录执行时间、结果等统计信息
- 检查热点:检查是否为热点规则
- 返回结果:返回执行结果
2. 热点规则识别流程
- 收集统计数据:收集规则执行的各种统计数据
- 分析统计数据:分析统计数据,识别热点规则
- 标记热点规则:将识别出的热点规则标记为热点
- 生成优化建议:根据热点类型生成优化建议
- 更新规则状态:更新规则的热点状态
技术要点
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;
}
互动话题
- 你在实际项目中遇到过哪些规则引擎的性能问题?是如何解决的?
- 对于热点规则识别,你有什么更好的算法或策略?
- 在分布式环境下,如何保证统计数据的一致性?
- 你认为规则引擎的性能优化还有哪些方面可以改进?
欢迎在评论区交流讨论!
公众号:服务端技术精选,更多精彩请关注
标题:SpringBoot + 规则执行统计 + 热点规则识别:高频调用规则自动标记,优化性能瓶颈
作者:jiangyi
地址:http://jiangyi.space/articles/2026/03/22/1774080695466.html
公众号:服务端技术精选
- 背景:规则引擎的性能挑战
- 核心概念
- 1. 规则执行统计
- 2. 热点规则
- 3. 热点规则识别
- 技术实现
- 1. 核心实体类
- 2. 规则执行统计服务
- 3. 热点规则识别服务
- 4. 规则执行引擎
- 5. 规则缓存服务
- 6. 规则执行控制器
- 核心流程
- 1. 规则执行流程
- 2. 热点规则识别流程
- 技术要点
- 1. 统计数据收集
- 2. 热点规则识别
- 3. 性能优化
- 4. 监控告警
- 最佳实践
- 1. 规则设计最佳实践
- 2. 统计配置最佳实践
- 3. 热点规则优化最佳实践
- 4. 监控告警最佳实践
- 常见问题
- 1. 统计数据不准确
- 2. 热点规则误识别
- 3. 性能影响
- 4. 规则执行延迟
- 代码优化建议
- 1. 统计数据收集优化
- 2. 热点规则识别优化
- 3. 规则缓存优化
- 互动话题
评论
0 评论