Skip to content

Latest commit

 

History

History
78 lines (56 loc) · 2.32 KB

案例-贴子楼层号.md

File metadata and controls

78 lines (56 loc) · 2.32 KB

贴子楼层号


问题

  • 和分库分表的全局计数器区别的是,它要连续的且自增,所以不方便预分配区间的形式来削峰
  • 要保证原子性。不能出现重复的楼层

实现

1.Manager层的调用入口

  private long getNextFloor(Long tid) {
        if (replyCacheManager.getMaxFloor(tid) == null) {
            long maxFloor = replyDao.getMaxFloor(tid);
            replyCacheManager.initFloor(tid, maxFloor);
        }
        return replyCacheManager.getNextFloor(tid);
    }

2.首先根据当前的key判断cache中是否有楼层值

 public Long getMaxFloor(Long tid) {
        String key = getKeyBbsFloorTid(tid);
        try {
            String value = bbsRedisClient.get(key);
            return StringUtils.isEmpty(value) ? null : Long.parseLong(value);
        } catch (RedisException e) {
            logger.error("[ReplyCacheManager.getMaxFloor] invoke error!", e);
            throw ExceptionUtils.newServiceException(ResultCode.PARAM_SERVICE_ERROR, e);
        }
    }

3.如果缓存没有值会进行初始化,从数据库表查询当前最大的楼层号,然后预热到缓存

StringCommands.setnx(String, String): 将字符串值value关联到key,如果key已存在则不做任何改变。

借助redis这一特性可以避免多条评论并发创建,但都因为缓存无值经过初始化这一步带来的脏数据

 public void initFloor(Long tid, Long floor) {
        String key = getKeyBbsFloorTid(tid);
        try {
             bbsRedisClient.setnx(key, String.valueOf(floor));
        } catch (RedisException e) {
            logger.error("[ReplyCacheManager.initFloor] invoke error!", e);
            throw ExceptionUtils.newServiceException(ResultCode.PARAM_SERVICE_ERROR, e);
        }
    }

4.获取楼层号

StringCommands.incr(String):将key中储存的数字值加1,如果key不存在,以0为key的初始值,然后执行INCR操作。该方法线程安全。

 public long getNextFloor(Long tid) {
        String key = getKeyBbsFloorTid(tid);
        try {
            return bbsRedisClient.incr(key);
        } catch (RedisException e) {
            logger.error("[ReplyCacheManager.getNextFloor] invoke error!", e);
            throw ExceptionUtils.newServiceException(ResultCode.PARAM_SERVICE_ERROR, e);
        }
    }