1、配置类

  1. import com.google.common.base.Charsets;
  2. import com.google.common.hash.Funnel;
  3. import com.luban.common.constant.RedisKeyPrefixConst;
  4. import com.luban.mall.component.BloomRedisService;
  5. import com.luban.mall.service.PmsProductService;
  6. import com.luban.mall.util.BloomFilterHelper;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.springframework.beans.factory.InitializingBean;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.context.annotation.Bean;
  11. import org.springframework.context.annotation.Configuration;
  12. import org.springframework.data.redis.core.RedisTemplate;
  13. import org.springframework.util.CollectionUtils;
  14. import java.util.List;
  15. @Slf4j
  16. @Configuration
  17. public class BloomFilterConfig implements InitializingBean{
  18. @Autowired
  19. private PmsProductService productService;
  20. @Autowired
  21. private RedisTemplate template;
  22. @Bean
  23. public BloomFilterHelper<String> initBloomFilterHelper() {
  24. return new BloomFilterHelper<>((Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8)
  25. .putString(from, Charsets.UTF_8), 1000000, 0.01);//误差率
  26. }
  27. /**
  28. * 布隆过滤器bean注入
  29. * @return
  30. */
  31. @Bean
  32. public BloomRedisService bloomRedisService(){
  33. BloomRedisService bloomRedisService = new BloomRedisService();
  34. bloomRedisService.setBloomFilterHelper(initBloomFilterHelper());
  35. bloomRedisService.setRedisTemplate(template);
  36. return bloomRedisService;
  37. }
  38. @Override
  39. public void afterPropertiesSet() throws Exception {
  40. List<Long> list = productService.getAllProductId();
  41. log.info("加载产品到布隆过滤器当中,size:{}",list.size());
  42. if(!CollectionUtils.isEmpty(list)){
  43. list.stream().forEach(item->{
  44. // LocalBloomFilter.put(item);
  45. bloomRedisService().addByBloomFilter(RedisKeyPrefixConst.PRODUCT_REDIS_BLOOM_FILTER,item+"");
  46. });
  47. }
  48. }
  49. }

2、布隆拦截器

  1. import com.fasterxml.jackson.databind.ObjectMapper;
  2. import com.luban.common.api.CommonResult;
  3. import com.luban.common.constant.RedisKeyPrefixConst;
  4. import com.luban.mall.component.BloomRedisService;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.util.AntPathMatcher;
  8. import org.springframework.util.PathMatcher;
  9. import org.springframework.web.servlet.HandlerInterceptor;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12. import java.util.Map;
  13. @Slf4j
  14. public class BloomFilterInterceptor implements HandlerInterceptor {
  15. @Autowired
  16. private BloomRedisService bloomRedisService;
  17. @Override
  18. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  19. String currentUrl = request.getRequestURI();
  20. PathMatcher matcher = new AntPathMatcher();
  21. //解析出pathvariable
  22. Map<String, String> pathVariable = matcher.extractUriTemplateVariables("/pms/productInfo/{id}", currentUrl);
  23. //布隆过滤器存储在redis中
  24. if(bloomRedisService.includeByBloomFilter(RedisKeyPrefixConst.PRODUCT_REDIS_BLOOM_FILTER,pathVariable.get("id"))){
  25. return true;
  26. }
  27. /**
  28. * 存储在本地jvm布隆过滤器中
  29. */
  30. /* if(LocalBloomFilter.match(pathVariable.get("id"))){
  31. return true;
  32. }*/
  33. /*
  34. * 不在本地布隆过滤器当中,直接返回验证失败
  35. * 设置响应头
  36. */
  37. response.setHeader("Content-Type","application/json");
  38. response.setCharacterEncoding("UTF-8");
  39. String result = new ObjectMapper().writeValueAsString(CommonResult.validateFailed("产品不存在!"));
  40. response.getWriter().print(result);
  41. return false;
  42. }
  43. }

3、本地布隆过滤器(生产不用)

  1. import com.google.common.hash.BloomFilter;
  2. import com.google.common.hash.Funnels;
  3. import java.nio.charset.StandardCharsets;
  4. public class LocalBloomFilter {
  5. private static final BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8),1000000,0.01);
  6. /**
  7. * 谷歌guava布隆过滤器
  8. * @param id
  9. * @return
  10. */
  11. public static boolean match(String id){
  12. return bloomFilter.mightContain(id);
  13. }
  14. public static void put(Long id){
  15. bloomFilter.put(id+"");
  16. }
  17. }

4、redis布隆

  1. import com.google.common.base.Preconditions;
  2. import com.luban.mall.util.BloomFilterHelper;
  3. import org.springframework.data.redis.core.RedisTemplate;
  4. public class BloomRedisService {
  5. private RedisTemplate<String, Object> redisTemplate;
  6. private BloomFilterHelper bloomFilterHelper;
  7. public void setBloomFilterHelper(BloomFilterHelper bloomFilterHelper) {
  8. this.bloomFilterHelper = bloomFilterHelper;
  9. }
  10. public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
  11. this.redisTemplate = redisTemplate;
  12. }
  13. /**
  14. * 根据给定的布隆过滤器添加值
  15. */
  16. public <T> void addByBloomFilter(String key, T value) {
  17. Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
  18. int[] offset = bloomFilterHelper.murmurHashOffset(value);
  19. for (int i : offset) {
  20. redisTemplate.opsForValue().setBit(key, i, true);
  21. }
  22. }
  23. /**
  24. * 根据给定的布隆过滤器判断值是否存在
  25. */
  26. public <T> boolean includeByBloomFilter(String key, T value) {
  27. Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
  28. int[] offset = bloomFilterHelper.murmurHashOffset(value);
  29. for (int i : offset) {
  30. if (!redisTemplate.opsForValue().getBit(key, i)) {
  31. return false;
  32. }
  33. }
  34. return true;
  35. }
  36. }

5、redis布隆依赖

  1. import com.google.common.base.Preconditions;
  2. import com.google.common.hash.Funnel;
  3. import com.google.common.hash.Hashing;
  4. /**
  5. * 算法过程:
  6. * 1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数
  7. * 2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0
  8. * 3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1
  9. * 4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。
  10. **/
  11. public class BloomFilterHelper<T> {
  12. private int numHashFunctions;
  13. private int bitSize;
  14. private Funnel<T> funnel;
  15. public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) {
  16. Preconditions.checkArgument(funnel != null, "funnel不能为空");
  17. this.funnel = funnel;
  18. // 计算bit数组长度
  19. bitSize = optimalNumOfBits(expectedInsertions, fpp);
  20. // 计算hash方法执行次数
  21. numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);
  22. }
  23. public int[] murmurHashOffset(T value) {
  24. int[] offset = new int[numHashFunctions];
  25. long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong();
  26. int hash1 = (int) hash64;
  27. int hash2 = (int) (hash64 >>> 32);
  28. for (int i = 1; i <= numHashFunctions; i++) {
  29. int nextHash = hash1 + i * hash2;
  30. if (nextHash < 0) {
  31. nextHash = ~nextHash;
  32. }
  33. offset[i - 1] = nextHash % bitSize;
  34. }
  35. return offset;
  36. }
  37. /**
  38. * 计算bit数组长度
  39. */
  40. private int optimalNumOfBits(long n, double p) {
  41. if (p == 0) {
  42. // 设定最小期望长度
  43. p = Double.MIN_VALUE;
  44. }
  45. return (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
  46. }
  47. /**
  48. * 计算hash方法执行次数
  49. */
  50. private int optimalNumOfHashFunctions(long n, long m) {
  51. return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
  52. }
  53. }