environment.resolvePlaceholders占位符解析优化
文章目录
- 占位符解析
- 1、原始实现:无缓存解析
- 2、优化实现:缓存解析结果
占位符解析
- 问题背景:在多数据源切换场景中,environment.resolvePlaceholders(value) 用于解析配置文件中的占位符(如 ${db.master})。如果频繁调用此方法,尤其是在高并发场景下,重复解析相同的占位符字符串会带来不必要的性能开销。通过对比优化前后的实现,验证缓存占位符解析结果对性能的提升。
1、原始实现:无缓存解析
以下代码每次调用时直接解析占位符:
public class OriginalDataSourceResolver {
private Environment environment;
public String resolve(String dataSourceId) {
// 每次调用都解析占位符
return environment.resolvePlaceholders(dataSourceId);
}
}
性能问题:
- 当
dataSourceId
是固定值(如${db.master}
)时,每次解析相同字符串,重复计算 resolvePlaceholders
内部需遍历PropertySources
,解析嵌套占位符(如${db.${env}.url}
),存在计算开销
2、优化实现:缓存解析结果
通过 ConcurrentHashMap
缓存已解析的值:
public class OptimizedDataSourceResolver {
private Environment environment;
private final Map<String, String> cache = new ConcurrentHashMap<>();
public String resolve(String dataSourceId) {
// 使用缓存避免重复解析
return cache.computeIfAbsent(dataSourceId, environment::resolvePlaceholders);
}
}
优化关键:
- 相同
dataSourceId
仅解析一次,后续直接从缓存读取。
性能测试验证:JMH基准测试
测试目标:对比无缓存与有缓存的解析性能
- 测试数据:
- 简单占位符:
${db.master}
- 复杂占位符:
${db.${env}.url}
- 简单占位符:
- 并发量:模拟 16 个线程并发调用
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Fork(value = 4)
@Warmup(iterations = 5, time = 5)
@Measurement(iterations = 5, time = 10)
@Threads(16)
public class PlaceholderResolveBenchmark {
private OriginalDataSourceResolver originalResolver;
private OptimizedDataSourceResolver optimizedResolver;
private final String simplePlaceholder = "${db.master}";
private final String complexPlaceholder = "${db.${env}.url}";
@Setup(Level.Trial)
public void setup() {
MockEnvironment env = new MockEnvironment();
env.setProperty("db.master", "masterDataSource");
env.setProperty("env", "prod");
env.setProperty("db.prod.url", "jdbc:mysql://localhost:3306/db");
originalResolver = new OriginalDataSourceResolver();
originalResolver.setEnvironment(env);
optimizedResolver = new OptimizedDataSourceResolver();
optimizedResolver.setEnvironment(env);
}
/**
* 测试原始解析器-简单占位符
* @param bh
*/
@Benchmark
public void testOriginalResolver_Simple(Blackhole bh) {
String result = originalResolver.resolve(simplePlaceholder);
bh.consume(result);
}
/**
* 测试优化解析器-简单占位符
* @param bh
*/
@Benchmark
public void testOptimizedResolver_Simple(Blackhole bh) {
String result = optimizedResolver.resolve(simplePlaceholder);
bh.consume(result);
}
/**
* 测试原始解析器-复杂占位符
* @param bh
*/
@Benchmark
public void testOriginalResolver_Complex(Blackhole bh) {
String result = originalResolver.resolve(complexPlaceholder);
bh.consume(result);
}
/**
* 测试优化解析器-复杂占位符
* @param bh
*/
@Benchmark
public void testOptimizedResolver_Complex(Blackhole bh) {
String result = optimizedResolver.resolve(complexPlaceholder);
bh.consume(result);
}
}
测试结果及分析
- 测试结果如下:
Benchmark Mode Cnt Score Error Units
TT.PlaceholderResolveBenchmark.testOptimizedResolver_Complex thrpt 20 315340.801 ± 59664.044 ops/ms
TT.PlaceholderResolveBenchmark.testOptimizedResolver_Simple thrpt 20 332918.070 ± 51588.217 ops/ms
TT.PlaceholderResolveBenchmark.testOriginalResolver_Complex thrpt 20 18.058 ± 2.609 ops/ms
TT.PlaceholderResolveBenchmark.testOriginalResolver_Simple thrpt 20 36.011 ± 3.894 ops/ms
- 结论:
优化后的 OptimizedDataSourceResolver
性能提升显著,吞吐量达到原始方案的 10,000 倍,缓存策略对高频占位符解析场景有非常明显的优化效果。