问题
中午生产出现故障,排查下来是调用外部接口超时熔断了。接口主要是传一个设备id判断是否是新用户的。没有做缓存导致高峰期请求超时,从而影响了自己团队的服务。
解决方案
本地布隆过滤器
这是我一开始的解决方案,既然只是一个简单的判断是否是新用户。那么我每次请求过来先判断布隆过滤器里有没有这个设备id。有那么就是老用户,没有那么就请求接口去判断是否是老用户,是老用户那么就放入布隆过滤器中。
public class BloomFilterTest {
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(),100,0.01);
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
if (bloomFilter.mightContain(i)){
System.out.println(i+"存在.");
}
service.isNew(1L);
// 省略
bloomFilter.put(i);
}
}
}
这是一个简单的demo,BloomFilter是guava的一个类。
不过领导说本地缓存设备id太多了是否会导致频繁fullgc。不如还是用Redis吧。
Redis
最简单的就是key里放设备id。value放是否为新设备。不过我个人是不太喜欢这样的。对我个人来说key越简单越好,越少越好,其次不能占用大量的内存。设备id很多,很难想像上千万个设备id的key,只为了存一个true和false,这时候BitMap的好处就来了。具体的介绍可以看这篇文章 Bitmap简介。
偏移量 -> 设备id (long类型)
老用户 -> 对应偏移量的值为1。
// 如果存在就是老用户
Boolean isOld = stringRedisTemplate.opsForValue().getBit(CommonConstant.OLD_USER_BIT_KEY, cid);
if (isOld) {
return false;
} else {
boolean isNew = service.isNewDevice(cid);
if (logger.isDebugEnabled()) {
logger.debug("checkIsNewDevice cid {} is {}", cid, isNew);
}
if (isNew) {
return true;
}
// 不是新用户放入 bitmap
stringRedisTemplate.opsForValue().setBit(CommonConstant.OLD_USER_BIT_KEY, cid, true);
return false;
}