springboot整合redisson实现分布式锁及布隆过滤器

下载安装redisson

整合

maven添加依赖

使用的版本是3.13,因为springboot使用的是2.1.3版本,因此按照官方上的提示,移除redisson-spring-data-22

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.redisson</groupId>
                    <artifactId>redisson-spring-data-22</artifactId>
                </exclusion>
            </exclusions>
            <version>3.13.1</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-data-21</artifactId>
            <version>3.13.1</version>
        </dependency>

项目配置

spring:
  redis:
    redisson: 
      config: classpath:SentinelConfig-local.yml

SentinelConfig-local.yml文件在项目的resource目录下,具体参数查看redisson依赖包下面的config/SentinelServersConfig.java

#本地开发使用
sentinelServersConfig:
  #baseConfig
  idleConnectionTimeout: 10000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  password: 123456
  clientName: null
  loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
  #BaseMasterSlaveServersConfig
  slaveConnectionMinimumIdleSize: 24
  slaveConnectionPoolSize: 64
  failedSlaveReconnectionInterval: 3000
  failedSlaveCheckInterval: 180000
  masterConnectionMinimumIdleSize: 24
  masterConnectionPoolSize: 64
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  dnsMonitoringInterval: 5000
  #SentinelServersConfig
  database: 8
  sentinelAddresses:
    - "redis://192.168.1.213:26379"
    - "redis://192.168.1.214:26380"
    - "redis://192.168.1.214:26381"
  masterName: "mymaster"
  scanInterval: 1000
threads: 0
# Netty线程池数量,默认值: 当前处理核数量 * 2
nettyThreads: 0
# 编码
codec: !<org.redisson.codec.JsonJacksonCodec> {}
# 传输模式
transportMode : "NIO"

添加工具类

添加DistributeLocker接口,以及它的实现类RedissonDistributeLocker

public interface DistributeLocker {
    /**
     * 加锁
     *
     * @param lockKey key
     */
    void lock(String lockKey);

    /**
     * 释放锁
     *
     * @param lockKey key
     */
    void unlock(String lockKey);

    /**
     * 加锁锁,设置有效期
     *
     * @param lockKey key
     * @param timeout 有效时间,默认时间单位在实现类传入
     */
    void lock(String lockKey, int timeout);

    /**
     * 加锁,设置有效期并指定时间单位
     *
     * @param lockKey key
     * @param timeout 有效时间
     * @param unit    时间单位
     */
    void lock(String lockKey, int timeout, TimeUnit unit);

    /**
     * 尝试获取锁,获取到则持有该锁返回true,未获取到立即返回false
     *
     * @param lockKey
     * @return true-获取锁成功 false-获取锁失败
     */
    boolean tryLock(String lockKey);

    /**
     * 尝试获取锁,获取到则持有该锁leaseTime时间.
     * 若未获取到,在waitTime时间内一直尝试获取,超过waitTime还未获取到则返回false
     *
     * @param lockKey   key
     * @param waitTime  尝试获取时间
     * @param leaseTime 锁持有时间
     * @param unit      时间单位
     * @return true-获取锁成功 false-获取锁失败
     */
    boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit);

    /**
     * 锁是否被任意一个线程锁持有
     *
     * @param lockKey
     * @return true-被锁 false-未被锁
     */
    boolean isLocked(String lockKey);

    /**
     * lock.isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定
     * @param lockKey
     * @return
     */
    boolean isHeldByCurrentThread(String lockKey);

    /**
     * 添加元素到布隆过滤器中
     * @param bloomFilterName
     * @param value
     */
    void bfAdd(String bloomFilterName, String value);

    /**
     * 初始化布隆过滤器
     * @param bloomFilterName  默认给了expectedInsertions = 1000000L falseProbability = 0.0001
     * @return 布隆过滤器名称
     */
    RBloomFilter<String> init(String bloomFilterName);

    /**
     * 初始化布隆过滤器
     * @param bloomFilterName 名称
     * @param expectedInsertions 预计插入元素总量
     * @param falseProbability 精度(误判率)
     * @return 布隆过滤器名称
     */
    RBloomFilter<String> init(String bloomFilterName, Long expectedInsertions, Double falseProbability);

    /**
     * 删除布隆过滤器(注意不是删除元素)
     * @param bloomFilterName 名称
     */
    void bfDelete(String bloomFilterName);

    /**
     * 判断  
     * @param bloomFilterName 名称
     * @param value false表示则一定不存在,true 则可能存在
     * @return
     */
    boolean bfContains(String bloomFilterName, String value);
}
public class RedissonDistributeLocker implements DistributeLocker {

    private final RedissonClient redissonClient;

    public RedissonDistributeLocker(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    public void lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
    }

    @Override
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }

    @Override
    public void lock(String lockKey, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, TimeUnit.MILLISECONDS);
    }

    @Override
    public void lock(String lockKey, int timeout, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
    }

    @Override
    public boolean tryLock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.tryLock();
    }

    @Override
    public boolean tryLock(String lockKey, long waitTime, long leaseTime,
                           TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean isLocked(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.isLocked();
    }

    @Override
    public boolean isHeldByCurrentThread(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.isHeldByCurrentThread();
    }

    @Override
    public void bfAdd(String bloomFilterName, String value) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
        bloomFilter.add(value);
    }

    @Override
    public RBloomFilter<String> init(String bloomFilterName) {
        long expectedInsertions = 1000000L;
        double falseProbability = 0.0001;
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
        bloomFilter.tryInit(expectedInsertions, falseProbability);
        return bloomFilter;
    }

    @Override
    public RBloomFilter<String> init(String bloomFilterName, Long expectedInsertions, Double falseProbability) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
        bloomFilter.tryInit(expectedInsertions, falseProbability);
        return bloomFilter;
    }

    @Override
    public void bfDelete(String bloomFilterName) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
        bloomFilter.delete();
    }

    @Override
    public boolean bfContains(String bloomFilterName, String value) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
        return bloomFilter.contains(value);
    }
}

添加工具类

public class RedissonUtils {

    private static DistributeLocker locker;

    public static void setLocker(DistributeLocker locker) {
        RedissonUtils.locker = locker;
    }

    public static void lock(String lockKey) {
        locker.lock(lockKey);
    }

    public static void unlock(String lockKey) {
        locker.unlock(lockKey);
    }

    public static void lock(String lockKey, int timeout) {
        locker.lock(lockKey, timeout);
    }

    public static void lock(String lockKey, int timeout, TimeUnit unit) {
        locker.lock(lockKey, timeout, unit);
    }

    public static boolean tryLock(String lockKey) {
        return locker.tryLock(lockKey);
    }

    public static boolean tryLock(String lockKey, long waitTime, long leaseTime,
                                  TimeUnit unit) {
        return locker.tryLock(lockKey, waitTime, leaseTime, unit);
    }

    public static boolean isLocked(String lockKey) {
        return locker.isLocked(lockKey);
    }


    public static boolean isHeldByCurrentThread(String lockKey) {
        return locker.isHeldByCurrentThread(lockKey);
    }

    public static void bfAdd(String bloomFilterName, String value) {
        locker.bfAdd(bloomFilterName, value);
    }

    public static void bfDelete(String bloomFilterName) {
        locker.bfDelete(bloomFilterName);
    }

    public static RBloomFilter<String> init(String bloomFilterName) {
        return locker.init(bloomFilterName);
    }

    public static RBloomFilter<String> init(String bloomFilterName, Long expectedInsertions, Double falseProbability) {
        return locker.init(bloomFilterName, expectedInsertions, falseProbability);
    }

    public static boolean bfContains(String bloomFilterName, String value) {
        return locker.bfContains(bloomFilterName, value);
    }
}

把工具类注入到spring容器中

    @Bean
    public RedissonDistributeLocker redissonLocker(RedissonClient redissonClient) {    
        RedissonDistributeLocker locker = new RedissonDistributeLocker(redissonClient);
        RedissonUtils.setLocker(locker);
        return locker;
    }

测试

布隆过滤器测试

	public static void main(String[] args) {
        Config config = new Config();
        config.useSentinelServers()
                .setMasterName("mymaster")
                .addSentinelAddress("redis://192.168.1.213:26379", "redis://192.168.1.214:26380", "redis://192.168.1.214:26381")
                .setPassword("123456")
                .setDatabase(11)
                .setRetryInterval(5000)
                .setTimeout(10000)
                .setConnectTimeout(10000);
        RedissonClient redissonClient = Redisson.create(config);
        DistributeLocker locker = new RedissonDistributeLocker(redissonClient);
        RedissonUtils.setLocker(locker);
        String bloomFilterName = "bloomFilter";
        RedissonUtils.bfDelete(bloomFilterName);
        long st = System.currentTimeMillis();
        RedissonUtils.isLocked("123");
        log.debug("test connection used time={} ms", System.currentTimeMillis() - st);
        long st2 = System.currentTimeMillis();
        RedissonUtils.isLocked("123");

        log.debug("test connection used time={} ms", System.currentTimeMillis() - st2);
        RBloomFilter<String> bloomFilter = RedissonUtils.init(bloomFilterName);
        for (int i = 0; i < 1000; i++) {
            RedissonUtils.bfAdd(bloomFilterName, bloomFilterName + i);
        }
        log.debug("insert 1000 data used time={} ms", System.currentTimeMillis() - st);
//        log.debug("current bloomFilter count={}", bloomFilter.count());
        log.debug("bfContains 1001={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1001));
        log.debug("bfContains 1002={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1002));
        log.debug("bfContains 1003={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1003));
        log.debug("bfContains 1004={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1004));
        log.debug("bfContains 1005={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1005));
        for (int i = 1000; i < 2000; i++) {
            RedissonUtils.bfAdd(bloomFilterName, bloomFilterName + i);
        }
        log.debug("bfContains 1001={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1001));
        log.debug("bfContains 1002={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1002));
        log.debug("bfContains 1003={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1003));
        log.debug("bfContains 1004={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1004));
        log.debug("bfContains 1005={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1005));
    }

应用

分布式锁-防重复提交

避免用户同一个操作,因为网络延迟等原因,多次提交到服务端,导致的重复操作。
通过切面方法,在切面内实现,方法进来时,尝试加锁,加锁成功则继续操作,加锁失败则返回操作失败

	@Around(value = "pointcut(logAndNoRepeatSubmit)")
    public Object around(ProceedingJoinPoint point, LogAndNoRepeatSubmit logAndNoRepeatSubmit) throws Throwable {
        String key = LogAspectUtil.getKeyFromRequest();
        if (RedissonUtils.tryLock(key)) {
            Object result = LogAspectUtil.getObject(point, key);            
            return result;
        } else {
            return R.fail("操作太频繁,请稍后再试");
        }
    }
    /**
     * 根据用户的uid+用户请求的url路径作为唯一标识
     * @return 用户请求唯一标识
     */
    public static String getKeyFromRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
//        Assert.notNull(request, "request can not null");
        // 此处可以用token或者JSessionId
//        String token = request.getHeader("Authorization");
        String path = request.getServletPath();
        long uid = JwtUtil.getUid();
        return uid + path;
    }
	public static Object getObject(ProceedingJoinPoint point, String key) throws Throwable {
        Object result = null;
        try {
            result = point.proceed();
        } catch (Throwable e) {
            log.error("不重复提交日志", e);
            throw e;
        } finally {
            //双重检查释放锁
            if (RedissonUtils.isLocked(key)) {
                if (RedissonUtils.isHeldByCurrentThread(key)) {
                    RedissonUtils.unlock(key);
                }
            }
        }
        return result;
    }

布隆过滤器-防缓存穿透

待实现
思路:
1、通过任务调度器或者在项目启动完成之后,把项目中免权限的表查询的id初始化到布隆过滤器
2、查询进来之前,先通过布隆过滤器判断,是否存在,不存在则直接返回不存在,可能存在才去查找数据库
3、对于后续增长的id,则可以通过同步的方式或者查询数据库成功时,动态添加到过滤器中

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值