用户服务主要开放了会员,乘车人相关的接口,主要涉及到了责任链,数据脱敏,布隆过滤器(优化)。

会员接口

判断用户名是否存在

采用布隆过滤器以及redis中的set数据结构(用于解决无法删除的问题),快速的判断用户名是否存在。

其中为了防止该set成为大key,热key,对其进行分片处理。

整体流程如下:

    public Boolean hasUsername(String username) {
        boolean hasUsername = bl.contains(username);
        // 布隆过滤器存在哈希冲突的问题,如果判断存在是不一定存在的,需要调整碰撞率
        if (hasUsername) {
            // 采用set来存储已经注销的用户名,解决布隆过滤器无法删除的问题
            return StringRedisTemplate.opsForSet().isMember(USER_REGISTER_REUSE_SHARDING + hashShardingIdx(username), username);
            // hashShardingIdx,就是对username的hashCode进行取模
        }
        // true 代表用户名可用,也就是不存在
        return true;
    }

注册接口

注册接口中遇到的重点就是缓存穿透,在注册流程中,第一步就需要判断用户名是否存在,对于大量用户的并发注册,很容易就对数据库造成过大压力。所以采用布隆过滤器+分布式锁减轻数据库压力,整体流程如下:

public UserRegisterRespDTO register(UserRegisterReqDTO requestParam) {
        // 责任链:用户注册参数必填检验--->用户名是否存在检验--->用户是否被拉入黑名单检验
        abstractChainContext.handler(UserChainMarkEnum.USER_REGISTER_FILTER.name(), requestParam);
        RLock lock = redissonClient.getLock(LOCK_USER_REGISTER + requestParam.getUsername());
        boolean tryLock = lock.tryLock();
        if (!tryLock) {
            throw new ServiceException(HAS_USERNAME_NOTNULL);
        }
        // 确保只有一个线程能够操作数据库
        try {
            // 新增user表
            ...
            // 新增user_phone表
			...
            // 新增user_mail表
            ...
            String username = requestParam.getUsername();
            // 删除用户名可重复利用表中的记录
            userReuseMapper.delete(Wrappers.update(new UserReuseDO(username)));
            // 删除用户名可重复利用分片缓存中的记录
            StringRedisTemplate.opsForSet().remove(USER_REGISTER_REUSE_SHARDING + hashShardingIdx(username), username);
            // 将用户名加入缓存穿透布隆过滤器
            userRegisterCachePenetrationBloomFilter.add(username);
        } finally {
            lock.unlock();
        }
        return BeanUtil.convert(requestParam, UserRegisterRespDTO.class);
    }

登录和注销接口

登录和常规的接口相同,采用了redis共享session。

注销接口也是一样,不过有一个注意的业务点是,如果注销的用户和当前登录的用户不相同,需要上传风控中心。

乘车人接口

  • 乘车人接口的重点主要体现在数据脱敏处理上,在向前端返回时,需要使用Jackson的JsonSerializer自定义序列化处理,采用hutool对身份证号和手机号之类的信息进行加密处理。对于后端的RPC调用,我们不需要进行加密处理,所以采用了一个新的实体类进行复制。

  • 对于乘车人的增删改使用了幂等组件库中的SpEL来保证幂等。

  • 采用先操作数据库,后删缓存的做法保证数据的一致性

上次更新:
Contributors: YangZhang