1、配置类
import com.google.common.base.Charsets;import com.google.common.hash.Funnel;import com.luban.common.constant.RedisKeyPrefixConst;import com.luban.mall.component.BloomRedisService;import com.luban.mall.service.PmsProductService;import com.luban.mall.util.BloomFilterHelper;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.util.CollectionUtils;import java.util.List;@Slf4j@Configurationpublic class BloomFilterConfig implements InitializingBean{ @Autowired private PmsProductService productService; @Autowired private RedisTemplate template; @Bean public BloomFilterHelper<String> initBloomFilterHelper() { return new BloomFilterHelper<>((Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8) .putString(from, Charsets.UTF_8), 1000000, 0.01);//误差率 }/** * 布隆过滤器bean注入 * @return */ @Bean public BloomRedisService bloomRedisService(){ BloomRedisService bloomRedisService = new BloomRedisService(); bloomRedisService.setBloomFilterHelper(initBloomFilterHelper()); bloomRedisService.setRedisTemplate(template); return bloomRedisService; } @Override public void afterPropertiesSet() throws Exception { List<Long> list = productService.getAllProductId(); log.info("加载产品到布隆过滤器当中,size:{}",list.size()); if(!CollectionUtils.isEmpty(list)){ list.stream().forEach(item->{ // LocalBloomFilter.put(item); bloomRedisService().addByBloomFilter(RedisKeyPrefixConst.PRODUCT_REDIS_BLOOM_FILTER,item+""); }); } }}
2、布隆拦截器
import com.fasterxml.jackson.databind.ObjectMapper;import com.luban.common.api.CommonResult;import com.luban.common.constant.RedisKeyPrefixConst;import com.luban.mall.component.BloomRedisService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.util.AntPathMatcher;import org.springframework.util.PathMatcher;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.Map;@Slf4jpublic class BloomFilterInterceptor implements HandlerInterceptor { @Autowired private BloomRedisService bloomRedisService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String currentUrl = request.getRequestURI(); PathMatcher matcher = new AntPathMatcher(); //解析出pathvariable Map<String, String> pathVariable = matcher.extractUriTemplateVariables("/pms/productInfo/{id}", currentUrl); //布隆过滤器存储在redis中 if(bloomRedisService.includeByBloomFilter(RedisKeyPrefixConst.PRODUCT_REDIS_BLOOM_FILTER,pathVariable.get("id"))){ return true; } /** * 存储在本地jvm布隆过滤器中 */ /* if(LocalBloomFilter.match(pathVariable.get("id"))){ return true; }*/ /* * 不在本地布隆过滤器当中,直接返回验证失败 * 设置响应头 */ response.setHeader("Content-Type","application/json"); response.setCharacterEncoding("UTF-8"); String result = new ObjectMapper().writeValueAsString(CommonResult.validateFailed("产品不存在!")); response.getWriter().print(result); return false; }}
3、本地布隆过滤器(生产不用)
import com.google.common.hash.BloomFilter;import com.google.common.hash.Funnels;import java.nio.charset.StandardCharsets;public class LocalBloomFilter { private static final BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8),1000000,0.01); /** * 谷歌guava布隆过滤器 * @param id * @return */ public static boolean match(String id){ return bloomFilter.mightContain(id); } public static void put(Long id){ bloomFilter.put(id+""); }}
4、redis布隆
import com.google.common.base.Preconditions;import com.luban.mall.util.BloomFilterHelper;import org.springframework.data.redis.core.RedisTemplate;public class BloomRedisService { private RedisTemplate<String, Object> redisTemplate; private BloomFilterHelper bloomFilterHelper; public void setBloomFilterHelper(BloomFilterHelper bloomFilterHelper) { this.bloomFilterHelper = bloomFilterHelper; } public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } /** * 根据给定的布隆过滤器添加值 */ public <T> void addByBloomFilter(String key, T value) { Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空"); int[] offset = bloomFilterHelper.murmurHashOffset(value); for (int i : offset) { redisTemplate.opsForValue().setBit(key, i, true); } } /** * 根据给定的布隆过滤器判断值是否存在 */ public <T> boolean includeByBloomFilter(String key, T value) { Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空"); int[] offset = bloomFilterHelper.murmurHashOffset(value); for (int i : offset) { if (!redisTemplate.opsForValue().getBit(key, i)) { return false; } } return true; }}
5、redis布隆依赖
import com.google.common.base.Preconditions;import com.google.common.hash.Funnel;import com.google.common.hash.Hashing;/** * 算法过程: * 1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数 * 2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0 * 3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1 * 4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。 **/public class BloomFilterHelper<T> { private int numHashFunctions; private int bitSize; private Funnel<T> funnel; public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) { Preconditions.checkArgument(funnel != null, "funnel不能为空"); this.funnel = funnel; // 计算bit数组长度 bitSize = optimalNumOfBits(expectedInsertions, fpp); // 计算hash方法执行次数 numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize); } public int[] murmurHashOffset(T value) { int[] offset = new int[numHashFunctions]; long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong(); int hash1 = (int) hash64; int hash2 = (int) (hash64 >>> 32); for (int i = 1; i <= numHashFunctions; i++) { int nextHash = hash1 + i * hash2; if (nextHash < 0) { nextHash = ~nextHash; } offset[i - 1] = nextHash % bitSize; } return offset; } /** * 计算bit数组长度 */ private int optimalNumOfBits(long n, double p) { if (p == 0) { // 设定最小期望长度 p = Double.MIN_VALUE; } return (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2))); } /** * 计算hash方法执行次数 */ private int optimalNumOfHashFunctions(long n, long m) { return Math.max(1, (int) Math.round((double) m / n * Math.log(2))); }}