1、引入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
·
2、写一个布隆过滤器类
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.nio.charset.Charset;
@Component
public class BloomFilterUtil {
/**
* expectedInsertions:期望添加的数据个数
* fpp:期望的误判率,期望的误判率越低,布隆过滤器计算时间越长
* @return
*/
@Bean
public BloomFilter<String> goodsIDBloom(){
BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000,0.00001);
return filter;
}
@Bean
public BloomFilter<String> orderBloom(){
BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000,0.00001);
return filter;
}
}
·
3、项目启动时,就把数据存储进去,put()方法
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.hash.BloomFilter;
import com.liu.seckill.entity.SeckillGood;
import com.liu.seckill.service.SeckillGoodService;
import com.liu.seckill.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Slf4j
@Configuration
public class InitConfig implements InitializingBean {
@Autowired
UserService userService;
@Autowired
SeckillGoodService seckillGoodService;
@Autowired
RedisTemplate redisTemplate;
@Autowired
RedisConfig redisConfig;
@Autowired
@Qualifier("goodsIDBloom")
BloomFilter<String> goodsIDBloom;
/**
* 把商品库存加载到 Redis中
* 每天更新
* @throws Exception
*/
@Override
@Scheduled(fixedRate = 1000*60*60*24)
public void afterPropertiesSet() throws Exception {
//只缓存秒杀还没结束或秒杀还没开始的商品
List<SeckillGood> seckillGoods = seckillGoodService.list(new QueryWrapper<SeckillGood>().ge("end_time", LocalDateTime.now()));
if (seckillGoods == null) {
log.info("暂无秒杀商品");
return;
}
//将秒杀商品ID和库存分别存入redis中
List<Long> seckillGoodIDList = new ArrayList<>();
for (SeckillGood seckillGood : seckillGoods) {
seckillGoodIDList.add(seckillGood.getSgId());
//设置过期时间
long millis = Duration.between(LocalDateTime.now(), seckillGood.getEndTime()).toMillis();
redisTemplate.opsForValue().set("seckillGoodID:" + seckillGood.getSgId(), seckillGood.getSgStock(), millis, TimeUnit.MILLISECONDS);
//存储值到布隆过滤器中
goodsIDBloom.put(seckillGood.getSgId()+"");
}
redisTemplate.opsForValue().set("seckillGoodIDList:", seckillGoodIDList,1,TimeUnit.DAYS);
}
}
·
4、使用布隆过滤器,mightContain()方法
@RestController
@Slf4j
@RequestMapping("/seckillOrder")
public class SeckillOrderController {
//如果有多个布隆过滤器,就同时使用@Qualifier和@Autowired
@Autowired
@Qualifier("goodsIDBloom")
BloomFilter<String> goodsIDBloom;
@Autowired
@Qualifier("orderBloom")
BloomFilter<String> orderBloom;
@GetMapping("/seckillGoods/{goodId}")
public Result seckillGoods(@PathVariable("goodId") Long goodId) {
ValueOperations valueOperations = redisTemplate.opsForValue();
//判断秒杀商品是否存在
//如果商品id在布隆过滤器中存在,那么就要再去判断在不在redis中,在,才能证明真的在
if (goodsIDBloom.mightContain(goodId+"")){
ArrayList<Long> seckillGoodIDList = (ArrayList<Long>) valueOperations.get("seckillGoodIDList:");
Assert.isTrue(seckillGoodIDList.contains(goodId),"未找到该商品,商品ID有误或此商品不参与秒杀或此商品秒杀已结束");
}else {
Assert.isTrue(false,"未找到该商品,商品ID有误或此商品不参与秒杀或此商品秒杀已结束");
}
}
}
·
参考:https://blog.csdn.net/weixin_49390750/article/details/125139084