SpringBoot系列之集成Redisson实现布隆过滤器

基于Spring Boot集成Redisson实现布隆过滤器

在高并发和大数据量的场景下,布隆过滤器是一种非常高效的存储结构,可以用于快速判断一个元素是否存在于集合中。本文将介绍如何在Spring Boot中集成Redisson来实现布隆过滤器,并通过一个订单查询的示例来展示其应用。

1. 项目搭建

1.1 创建Spring Boot项目

首先,创建一个Spring Boot项目,并添加以下依赖:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson-spring-boot-starter</artifactId>
        <version>3.23.0</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

1.2 配置Redisson

application.yml文件中配置Redisson的连接信息:

yaml 复制代码
spring:
  application:
    name: redisson-bloom-filter-demo

redisson:
  config:
    single-server-config:
      address: "redis://127.0.0.1:6379"
      password: "your-redis-password"
      database: 0
      connection-pool-config:
        max-idle: 10
        min-idle: 1
        max-active: 100
        min-evictable-idle-time: 300000
        time-between-eviction-runs: 30000
        block-wait-time: 1000
        idle-instance-soft-abandon: false
        idle-instance-close-timeout: 30000

bloom:
  filter:
    expected-insertions: 1000000  # 预期插入数量,默认值为 1000000
    false-probability: 0.03       # 误判率,默认值为 3%

1.3 Redisson配置类

java 复制代码
package com.example.redission.configuration;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.spring.starter.RedissonProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class RedissonConfiguration {

    @Autowired
    private RedissonProperties redissonProperties;

    @Bean
    public RedissonClient redissonClient() throws Exception{
        Config config = Config.fromYAML(redissonProperties.getConfig());
        Redisson.create(config);
        return Redisson.create(config);
    }

}

2. 布隆过滤器实现

2.1 创建布隆过滤器服务

创建一个BloomFilterService类,用于初始化和操作布隆过滤器:

java 复制代码
package com.example.redission.service;

import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class BloomFilterService {

    private static final Logger logger = LoggerFactory.getLogger(BloomFilterService.class);

    @Value("${bloom.filter.expected-insertions:1000000}")
    private long expectedInsertions;

    @Value("${bloom.filter.false-probability:0.03}")
    private double falseProbability;

    private static final String BLOOM_FILTER_NAME = "bloomFilter";

    private final RedissonClient redissonClient;
    private RBloomFilter<String> bloomFilter;

    public BloomFilterService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public synchronized void initBloomFilter() {
        if (bloomFilter == null) {
            bloomFilter = redissonClient.getBloomFilter(BLOOM_FILTER_NAME);

            // 检查布隆过滤器是否已经初始化
            if (!bloomFilter.isExists()) {
                bloomFilter.tryInit(expectedInsertions, falseProbability);
                logger.info("Bloom filter initialized with expected insertions: {} and false probability: {}",
                        expectedInsertions, falseProbability);
            } else {
                logger.info("Bloom filter already exists, using existing instance");
            }
        }
    }

    public void add(String key) {
        if (key == null) {
            logger.warn("Attempt to add null key to Bloom filter");
            return;
        }

        bloomFilter.add(key);
        logger.debug("Added key: {} to Bloom filter", key);
    }

    public boolean contains(String key) {
        if (key == null) {
            logger.warn("Attempt to check null key in Bloom filter");
            return false;
        }

        boolean result = bloomFilter.contains(key);
        logger.debug("Checked key: {} in Bloom filter, result: {}", key, result);
        return result;
    }

    /**
     * 重新初始化布隆过滤器(仅在需要清空时调用)
     */
    public synchronized void reinitialize() {
        bloomFilter.delete();
        bloomFilter.tryInit(expectedInsertions, falseProbability);
        logger.info("Bloom filter reinitialized");
    }

    /**
     * 获取布隆过滤器的错误概率
     */
    public double getFalseProbability() {
        return falseProbability;
    }

    /**
     * 获取布隆过滤器的预期插入数量
     */
    public long getExpectedInsertions() {
        return expectedInsertions;
    }
}

2.2 初始化布隆过滤器

创建一个BloomFilterInitializer类,在应用启动时初始化布隆过滤器:

java 复制代码
package com.example.redission.runner;

import com.example.redission.dto.OrderDetailDTO;
import com.example.redission.service.BloomFilterService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

@Component
public class BloomFilterInitializer implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(BloomFilterInitializer.class);

    private final BloomFilterService bloomFilterService;

    @Autowired
    public BloomFilterInitializer(BloomFilterService bloomFilterService) {
        this.bloomFilterService = bloomFilterService;
    }

    @Override
    public void run(String... args) throws Exception {
        // 在应用启动时初始化布隆过滤器
        bloomFilterService.initBloomFilter();
        logger.info("Bloom filter initialized during application startup");

        // 模拟订单数据并写入布隆过滤器
        List<OrderDetailDTO> orderData = generateSampleOrderData();
        initializeBloomFilterWithOrderData(orderData);
    }

    private void initializeBloomFilterWithOrderData(List<OrderDetailDTO> orderData) {
        // 将订单数据写入布隆过滤器
        for (OrderDetailDTO order : orderData) {
            // 将订单号写入布隆过滤器
            bloomFilterService.add(order.getOrderNumber());

            // 将订单中的商品 SKU ID 写入布隆过滤器
            for (OrderDetailDTO.OrderItemDTO item : order.getItems()) {
                bloomFilterService.add(item.getSkuId());
            }
        }

        logger.info("Bloom filter initialized with {} orders and their items", orderData.size());
    }

    private List<OrderDetailDTO> generateSampleOrderData() {
        // 使用 Arrays.asList 替代 List.of(JDK 8 兼容)
        return Arrays.asList(
                new OrderDetailDTO(
                        "1",
                        "ORD-001",
                        new BigDecimal("100.00"),
                        "COMPLETED",
                        "PAID",
                        "2024-01-01 12:00:00",
                        Arrays.asList(
                                new OrderDetailDTO.OrderItemDTO(
                                        "SKU-001",
                                        "Product 1",
                                        "image1.jpg",
                                        2,
                                        new BigDecimal("50.00"),
                                        new BigDecimal("100.00")
                                )
                        )
                ),
                new OrderDetailDTO(
                        "2",
                        "ORD-002",
                        new BigDecimal("200.00"),
                        "COMPLETED",
                        "PAID",
                        "2024-01-02 12:00:00",
                        Arrays.asList(
                                new OrderDetailDTO.OrderItemDTO(
                                        "SKU-002",
                                        "Product 2",
                                        "image2.jpg",
                                        1,
                                        new BigDecimal("200.00"),
                                        new BigDecimal("200.00")
                                )
                        )
                )
        );
    }
}

3. 订单查询功能实现

3.1 创建订单查询服务

创建一个OrderDetailDTO类:

java 复制代码
package com.example.redission.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class OrderDetailDTO {

    private String orderId; // 订单ID
    private String orderNumber; // 订单号
    private BigDecimal totalAmount; // 总金额
    private String orderStatus; // 订单状态
    private String paymentStatus; // 支付状态
    private String createTime; // 创建时间
    private List<OrderItemDTO> items; // 订单商品项

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class OrderItemDTO {
        private String skuId; // 商品SKU ID
        private String skuName; // 商品名称
        private String skuImage; // 商品图片
        private int quantity; // 商品数量
        private BigDecimal price; // 商品单价
        private BigDecimal subtotal; // 商品小计
    }

}

创建一个OrderQueryService类,用于查询订单信息:

java 复制代码
package com.example.redission.service;

import com.example.redission.dto.OrderDetailDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Arrays;

@Service
public class OrderQueryService {

    private static final Logger logger = LoggerFactory.getLogger(OrderQueryService.class);

    private final BloomFilterService bloomFilterService;

    @Autowired
    public OrderQueryService(BloomFilterService bloomFilterService) {
        this.bloomFilterService = bloomFilterService;
    }

    /**
     * 查询订单信息
     *
     * @param orderNumber 订单号
     * @return 订单信息,如果不存在则返回 null
     */
    public OrderDetailDTO getOrderDetails(String orderNumber) {
        // 先通过布隆过滤器判断订单号是否存在
        if (!bloomFilterService.contains(orderNumber)) {
            logger.info("Order {} does not exist in Bloom filter", orderNumber);
            return null;
        }

        // 模拟订单数据
        OrderDetailDTO order = generateSampleOrderData(orderNumber);
        if (order != null) {
            logger.info("Order {} found in simulated data", orderNumber);
            return order;
        } else {
            logger.info("Order {} exists in Bloom filter but not found in simulated data", orderNumber);
            return null;
        }
    }

    /**
     * 模拟生成订单数据
     *
     * @param orderNumber 订单号
     * @return 模拟的订单数据
     */
    private OrderDetailDTO generateSampleOrderData(String orderNumber) {
        // 模拟的订单数据
        if ("ORD-001".equals(orderNumber)) {
            return new OrderDetailDTO(
                    "1",
                    "ORD-001",
                    new BigDecimal("100.00"),
                    "COMPLETED",
                    "PAID",
                    "2024-01-01 12:00:00",
                    Arrays.asList(
                            new OrderDetailDTO.OrderItemDTO(
                                    "SKU-001",
                                    "Product 1",
                                    "image1.jpg",
                                    2,
                                    new BigDecimal("50.00"),
                                    new BigDecimal("100.00")
                            )
                    )
            );
        } else if ("ORD-002".equals(orderNumber)) {
            return new OrderDetailDTO(
                    "2",
                    "ORD-002",
                    new BigDecimal("200.00"),
                    "COMPLETED",
                    "PAID",
                    "2024-01-02 12:00:00",
                    Arrays.asList(
                            new OrderDetailDTO.OrderItemDTO(
                                    "SKU-002",
                                    "Product 2",
                                    "image2.jpg",
                                    1,
                                    new BigDecimal("200.00"),
                                    new BigDecimal("200.00")
                            )
                    )
            );
        }
        return null;
    }
}

3.2 创建订单查询控制器

创建一个OrderQueryController类,用于处理订单查询的HTTP请求:

java 复制代码
package com.example.redission.controller;

import com.example.redission.dto.OrderDetailDTO;
import com.example.redission.service.OrderQueryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/orders")
public class OrderQueryController {

    private static final Logger logger = LoggerFactory.getLogger(OrderQueryController.class);

    private final OrderQueryService orderQueryService;

    @Autowired
    public OrderQueryController(OrderQueryService orderQueryService) {
        this.orderQueryService = orderQueryService;
    }

    /**
     * 查询订单信息
     *
     * @param orderNumber 订单号
     * @return 订单信息,如果不存在则返回 null
     */
    @GetMapping("/{orderNumber}")
    public OrderDetailDTO getOrderDetails(@PathVariable String orderNumber) {
        OrderDetailDTO order = orderQueryService.getOrderDetails(orderNumber);
        logger.info("Query order {}: result = {}", orderNumber, order);
        return order;
    }
}

4. 测试与验证

启动应用后,可以通过以下API接口进行测试:

  • 查询订单信息:GET /api/orders/{orderNumber}

示例请求

查询订单信息
复制代码
GET /api/orders/ORD-001

示例响应

如果订单存在:

json 复制代码
{
  "orderId": "1",
  "orderNumber": "ORD-001",
  "totalAmount": 100.00,
  "orderStatus": "COMPLETED",
  "paymentStatus": "PAID",
  "createTime": "2024-01-01 12:00:00",
  "items": [
    {
      "skuId": "SKU-001",
      "skuName": "Product 1",
      "skuImage": "image1.jpg",
      "quantity": 2,
      "price": 50.00,
      "subtotal": 100.00
    }
  ]
}

5. 总结

通过本文的示例,我们展示了如何在Spring Boot中集成Redisson来实现布隆过滤器,并通过订单查询功能展示了其应用。布隆过滤器可以有效地减少数据库查询的次数,提高系统的性能和响应速度。在实际应用中,可以根据业务需求调整布隆过滤器的参数,以达到最佳的性能和误判率平衡。

相关推荐
咩咩啃树皮8 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
鱟鲥鳚8 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
大模型码小白10 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司10 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地11 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415911 小时前
TCP超时重传机制是为了解决什么问题?
java
二宝哥12 小时前
CentOS 7.9 系统下 Redis 5.0.4 单机安装与源码编译安装指南
redis·centos
莫逸风14 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
隔窗听雨眠14 小时前
Spring Boot在云原生时代的编程范式革新研究
spring boot·后端·云原生
z1234567898614 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程