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来实现布隆过滤器,并通过订单查询功能展示了其应用。布隆过滤器可以有效地减少数据库查询的次数,提高系统的性能和响应速度。在实际应用中,可以根据业务需求调整布隆过滤器的参数,以达到最佳的性能和误判率平衡。

相关推荐
喜欢便码9 分钟前
JS小练习0.1——弹出姓名
java·前端·javascript
王磊鑫1 小时前
重返JAVA之路-初识JAVA
java·开发语言
半兽先生2 小时前
WebRtc 视频流卡顿黑屏解决方案
java·前端·webrtc
南星沐3 小时前
Spring Boot 常用依赖介绍
java·前端·spring boot
代码不停3 小时前
Java中的异常
java·开发语言
老李不敲代码3 小时前
榕壹云外卖跑腿系统:基于Spring Boot+MySQL+UniApp的智慧生活服务平台
spring boot·mysql·微信小程序·uni-app·软件需求
Justice link4 小时前
部署redis cluster
数据库·redis·缓存
何似在人间5754 小时前
多级缓存模型设计
java·jvm·redis·缓存
多云的夏天4 小时前
ubuntu24.04-MyEclipse的项目导入到 IDEA中
java·intellij-idea·myeclipse
Fanxt_Ja4 小时前
【数据结构】红黑树超详解 ---一篇通关红黑树原理(含源码解析+动态构建红黑树)
java·数据结构·算法·红黑树