前几天我开发了的投票小程序终于上线了,上午都运行的好好的,用户投票操作都流畅的很,突然下午3点的时候产品经理找到了我,说投票报错了。投票系统投票报错,这可相当于系统崩溃了啊。吓得我菊花一紧,虎躯一震,赶紧排查起问题来。我飞快的登录了项目部署服务器,打开日志排查,发现某处报了空指针异常,但是没有打印出堆栈信息。光有一个空指针我也没着啊,我仔细查看了代码,是有打印堆栈信息啊。问了旁边的张大师,才知道是tomcat的配置禁用了堆栈信息的打印,百度了一下,加了了一串配置,成功打印出堆栈信息。才发现是布隆过滤器的bitSet报了空指针异常。我登上redis发现,redis中是有值的啊,为啥为报空指针异常呢?百思不得其解,关键是之前压测也没出这个问题啊!
经过我和另外两个同事的认真排查,最终终于发现了这个坑是怎么回事了!
public RedisBitSet(RedisTemplate jedisCluster, String name, int expire) {
this.jedisCluster = jedisCluster;
this.name = name;
//初始化化 --- add bu whz
Boolean isExits = jedisCluster.opsForValue().getBit(name, 1);
if(isExits==null || isExits==false ){
set(1, false);
jedisCluster.expire(this.name, expire, TimeUnit.DAYS);
}
}
public void set(long bitIndex, boolean value) {
this.jedisCluster.opsForValue().setBit(this.name, bitIndex, value);
}
看代码可以发现,我在初始化RedisBitSet的时候,做了个操作,把1的比特位置设置为了false。这为后面的大坑埋了个大大的伏笔。
Boolean isExits = redisTemplate.opsForValue().getBit(CacheConstant.CACHE_METADATA + "redisbit_" + dateStr, 1);
在投票之前我做了个判断,每次都新建了个bloomFilter,然后判断1的比特位如果为null或false就初始化布隆过滤器。实际上这个地方有个很大的问题,new BloomFilter可以放到外面作为静态成员变量,随着项目启动加载一次就行了。但真正的问题不是这个,而是后面投票的时候,刚好有个key的字节哈希值取余正好是1,把初始化的1的false置为了true。导致后面这个判断if (isExits == null || isExits == false) 一直为false,然后每次都没有bindBloomFilter,bitSet的值始终为空。
这个问题排查了很久才发现,给了我很大的教训。以后写代码一定要考虑的更周全一点。