苍穹外卖布隆过滤器改造方案(自用存档)
一、核心架构原则(必须坚守)
先明确底线,避免后续混乱:
- 依赖关系:业务模块(如sky-admin-operation-service)→ common层(BloomFilterManager、BloomFilterConfig、工具类),反之绝对不允许;
- 边界清晰:每个业务模块的布隆过滤器配置、初始化逻辑,都放在自身模块内,common层只提供“可复用的能力”(Manager、工具方法),不插手任何业务细节;
- 扩展无侵入:新增业务过滤器时,只改自身模块,不修改common层和其他业务模块。
二、最终方案:3个组件各司其职(无依赖倒置,复用性拉满)
组件1:common层 - BloomFilterConfiguration(纯工具类,无任何业务依赖)
作用:提供“统一配置构建方法”,避免每个业务模块重复写 setExpectedInsertions 等代码,不依赖任何业务类(Mapper、Constant都不依赖,Constant让业务模块自己传)。
package com.sky.config;
import lombok.experimental.UtilityClass;
import java.util.function.Supplier;
/**
* common层布隆过滤器配置工具类(纯工具,无业务依赖)
* 核心:只提供配置构建模板,业务参数由业务模块自己传入
*/
@UtilityClass
public class BloomFilterConfiguration {
/**
* 通用配置构建方法(所有业务都能复用)
* @param filterKey 业务模块自己的CacheConstant key(由业务模块传入,避免依赖)
* @param dataSupplier 业务模块自己的数据源(Mapper查询,由业务模块传入)
* @param expectedInsertions 预期数据量(业务模块自己定义)
* @param falseProbability 误判率(业务模块自己定义)
* @param order 初始化优先级(业务模块自己定义)
* @return BloomFilterConfig
*/
public static BloomFilterConfig buildConfig(
String filterKey,
Supplier<List<Long>> dataSupplier,
Long expectedInsertions,
double falseProbability,
int order
) {
BloomFilterConfig config = new BloomFilterConfig(filterKey, dataSupplier);
config.setExpectedInsertions(expectedInsertions);
config.setFalseProbability(falseProbability);
config.setOrder(order);
config.setEnabled(true);
return config;
}
/**
* 简化版构建方法(提供默认参数,业务模块可按需覆盖)
* 避免重复写“预期数据量10万、误判率0.01”等通用参数
*/
public static BloomFilterConfig buildDefaultConfig(
String filterKey,
Supplier<List<Long>> dataSupplier
) {
return buildConfig(filterKey, dataSupplier, 100000L, 0.01, 5);
}
}
组件2:common层 - BloomFilterManager(改造后不依赖业务配置,只提供能力)
去掉之前的 @Autowired List<BloomFilterConfig>,改为「业务模块主动调用初始化」,避免Manager依赖业务配置,保持common层的纯粹性。
package com.sky.config;
import com.sky.utils.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* common层布隆过滤器管理器(只提供能力,不依赖任何业务)
* 核心:业务模块主动调用初始化/检查/添加方法,Manager不主动获取任何业务配置
*/
@Slf4j
@Component
public class BloomFilterManager {
@Autowired
private RedisCache redisCache;
private final Map<String, RBloomFilter<Object>> bloomFilterMap = new ConcurrentHashMap<>();
/**
* 业务模块主动调用:初始化单个业务的布隆过滤器(核心复用方法)
* 由业务模块传入自己的配置,Manager只负责执行初始化逻辑
*/
public void initBloomFilter(BloomFilterConfig config) {
if (config == null || !config.isEnabled()) {
log.warn("布隆过滤器配置为空或未启用,跳过初始化");
return;
}
String filterKey = config.getFilterKey();
if (bloomFilterMap.containsKey(filterKey)) {
log.info("布隆过滤器 {} 已存在,无需重复初始化", filterKey);
return;
}
// 复用之前的初始化逻辑(获取实例、加载数据等)
CompletableFuture.runAsync(() -> {
try {
RBloomFilter<Object> bloomFilter = redisCache.getBloomFilter(
filterKey,
config.getExpectedInsertions(),
config.getFalseProbability()
);
// 检查并初始化过滤器
if (bloomFilter.isExists()) {
bloomFilter.delete();
bloomFilter.tryInit(config.getExpectedInsertions(), config.getFalseProbability());
} else {
bloomFilter.tryInit(config.getExpectedInsertions(), config.getFalseProbability());
}
// 加载业务数据(dataSupplier由业务模块传入,Manager不关心数据来源)
List<Long> data = config.getDataSupplier().get();
if (data != null && !data.isEmpty()) {
for (Long id : data) {
bloomFilter.add(id);
}
log.info("✅ 业务布隆过滤器 {} 初始化完成,加载 {} 条数据", filterKey, data.size());
} else {
log.warn("⚠️ 业务布隆过滤器 {} 无数据可加载", filterKey);
}
bloomFilterMap.put(filterKey, bloomFilter);
} catch (Exception e) {
log.error("❌ 初始化业务布隆过滤器 {} 失败", filterKey, e);
}
});
}
/**
* 业务模块主动调用:批量初始化多个过滤器(支持一个业务模块有多个过滤器)
*/
public void initBloomFilters(List<BloomFilterConfig> configs) {
if (configs == null || configs.isEmpty()) {
return;
}
configs.forEach(this::initBloomFilter);
}
// 以下是原有复用方法(mightContain、add等),完全不变
public boolean mightContain(String filterKey, Object element) {
RBloomFilter<Object> bloomFilter = bloomFilterMap.get(filterKey);
if (bloomFilter == null) {
log.warn("布隆过滤器 {} 未找到,直接返回true", filterKey);
return true;
}
boolean result = bloomFilter.contains(element);
log.debug("布隆过滤器检查 - 过滤器: {}, 元素: {}, 结果: {}", filterKey, element, result);
return result;
}
public void add(String filterKey, Object element) {
RBloomFilter<Object> bloomFilter = bloomFilterMap.get(filterKey);
if (bloomFilter != null) {
bloomFilter.add(element);
log.debug("添加元素到布隆过滤器 - 过滤器: {}, 元素: {}", filterKey, element);
} else {
log.warn("布隆过滤器 {} 未找到,无法添加元素 {}", filterKey, element);
}
}
// 其他原有方法(getBloomFilter、printStatus等)保留不变
}
组件3:业务模块 - 自身初始化类(业务逻辑内聚,不污染common)
以 sky-admin-operation-service(员工业务)为例,在自身模块内创建初始化类,只依赖自己的Mapper和common层的工具类/Manager,完全符合依赖关系。
package com.sky.config;
import com.sky.constant.CacheConstant;
import com.sky.mapper.EmployeeMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
/**
* 员工业务-布隆过滤器初始化类(放在sky-admin-operation-service模块内)
* 核心:业务自己的配置、自己的数据源,自己初始化,不依赖其他模块
*/
@Configuration
@Slf4j
@RequiredArgsConstructor
public class EmployeeBloomFilterConfig {
// 只依赖自身模块的Mapper(符合微服务边界)
private final EmployeeMapper employeeMapper;
// 依赖common层的Manager(符合依赖关系:业务→common)
private final BloomFilterManager bloomFilterManager;
@PostConstruct
public void initEmployeeBloomFilter() {
log.info("开始初始化员工业务布隆过滤器...");
// 1. 用common层的工具类构建配置(复用工具方法,避免重复代码)
BloomFilterConfig employeeFilterConfig = BloomFilterConfiguration.buildConfig(
CacheConstant.EMPLOYEE_BLOOM_FILTER_KEY, // 自身业务的key(来自自身模块的Constant)
employeeMapper::getAllEmployeeIds, // 自身业务的数据源(自身Mapper)
50000L, // 自身业务的预期数据量
0.005, // 自身业务的误判率
1 // 自身业务的初始化优先级
);
// 2. 调用common层的Manager初始化(复用Manager的能力)
bloomFilterManager.initBloomFilter(employeeFilterConfig);
log.info("员工业务布隆过滤器初始化请求已提交(异步执行)");
}
}
三、扩展新业务(如店铺)的复用流程(完全无侵入)
当你后续需要在 sky-shop-service(店铺业务)添加布隆过滤器时,只需3步,不修改任何common层代码:
步骤1:店铺业务模块内创建初始化类
// sky-shop-service/com/sky/config/ShopBloomFilterConfig.java
@Configuration
@RequiredArgsConstructor
public class ShopBloomFilterConfig {
private final ShopMapper shopMapper; // 店铺模块自己的Mapper
private final BloomFilterManager bloomFilterManager; // 复用common层Manager
@PostConstruct
public void initShopBloomFilter() {
// 复用common层的工具类构建配置
BloomFilterConfig shopFilterConfig = BloomFilterConfiguration.buildDefaultConfig(
CacheConstant.SHOP_BLOOM_FILTER_KEY, // 店铺模块自己的key
shopMapper::getAllShopIds // 店铺模块自己的数据源
);
// 复用common层的Manager初始化
bloomFilterManager.initBloomFilter(shopFilterConfig);
}
}
步骤2:店铺Service中调用Manager(复用员工业务的逻辑)
@Service
public class ShopServiceImpl implements ShopService {
@Autowired
private BloomFilterManager bloomFilterManager; // 复用Manager
@Override
public Shop searchById(Long id) {
// 复用Manager的mightContain方法(和员工业务逻辑完全一致)
if (!bloomFilterManager.mightContain(CacheConstant.SHOP_BLOOM_FILTER_KEY, id)) {
throw new ResourceNotFoundException("店铺不存在");
}
// 后续缓存、数据库查询逻辑...
}
@Override
public void save(ShopDTO shopDTO) {
// 保存数据库后,复用Manager的add方法(和员工业务逻辑完全一致)
Shop shop = convertToEntity(shopDTO);
shopMapper.save(shop);
bloomFilterManager.add(CacheConstant.SHOP_BLOOM_FILTER_KEY, shop.getId());
// 后续缓存逻辑...
}
}
步骤3:启动店铺服务,自动初始化
店铺服务启动时,ShopBloomFilterConfig 会自动执行,调用common层的Manager初始化过滤器,全程不影响common层和其他业务模块。
四、为什么这个方案架构合规且高效?
| 架构要求 | 方案如何满足 |
|---|---|
| 无依赖倒置 | common层不依赖任何业务模块,业务模块依赖common层,符合微服务依赖原则 |
| 边界清晰 | 每个业务的过滤器配置、数据源、初始化逻辑都在自身模块内,不污染其他模块 |
| 复用高效 | 复用了BloomFilterManager的核心能力(初始化、检查、添加),复用了BloomFilterConfiguration的配置构建方法,避免重复代码 |
| 扩展无侵入 | 新增业务只改自身模块,不修改common层和其他业务模块,符合“开闭原则” |
| 故障隔离 | 每个业务的过滤器通过key隔离,一个业务的过滤器初始化失败,不影响其他业务 |
五、最终结论:BloomFilterConfiguration 不是冗余,是“复用工具”
现在的 BloomFilterConfiguration 完全符合你的需求:
- 不破坏微服务架构:无任何业务依赖,只提供工具方法;
- 复用BloomFilterManager:所有业务都通过自动注入+key的方式复用Manager;
- 减少重复代码:扩展新业务时,无需重复写配置setter、初始化逻辑,只需调用工具方法;
- 无冗余:每个组件各司其职,common层提供能力,业务层自主使用,架构清晰。
之前的混乱是因为错误设计了“集中初始化”,现在改为“业务自主初始化+common提供复用能力”,既满足你后续扩展的需求,又坚守了微服务架构原则,完美解决你的顾虑!

浙公网安备 33010602011771号