Redis分布式锁真的安全吗?

【项目开发 | 分布式系统】Redis 分布式锁真的安全吗? 加锁时,先设置一个过期时间,然后我们开启一个「守护线程」,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还未完成,那么就自动对锁进行「续期」,重新设置过期时间。与分布式锁相对应的是「单机锁」,我们在写多线程程序时,避免同时操作一个共享变量产生数据问题,通常会使用一把锁来「互斥」,以保证共享变量的正确性,其使用范围是在「同一个进程」中。 阅读详情

点击上方“芋道源码”,选择“设为星标”

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

 

来源:juejin.cn/post/

7137224260862361637

49ed4c56dee0fd1322f5288c9d4173a6.jpeg


今天我们来聊一聊Redis分布式锁。

首先大家可以先思考一个简单的问题,为什么要使用分布式锁?普通的jvm锁为什么不可以?

这个时候,大家肯定会吧啦吧啦想到一堆,例如java应用属于进程级,不同的ecs中部署相同的应用,他们之间相互独立。

所以,在分布式系统中,当有多个客户端需要获取锁时,我们需要分布式锁。此时,锁是保存在一个共享存储系统中的,可以被多个客户端共享访问和获取。

分布式锁(SET NX)

知道了分布式锁的使用场景,我们来自己简单的实现下分布式锁:

public class IndexController {

    public String deductStock() {

        String lockKey = "lock:product_101";

        //setNx 获取分布式锁
        String clientId = UUID.randomUUID().toString();
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS); //jedis.setnx(k,v)
        if (!result) {
            return "error_code";
        }
        try {
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
            if (stock > 0) {
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
                System.out.println("扣减成功,剩余库存:" + realStock);
            } else {
                System.out.println("扣减失败,库存不足");
            }
        } finally {
            //解锁
            if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
                stringRedisTemplate.delete(lockKey);
            }
    }
}

以上代码简单的实现了一个扣减库存的业务逻辑,我们拆开来说下都做了什么事情:

1、首先声明了lockkey,表示我们需要set的keyName

2、其次UUID.randomUUID().toString();生成该次请求的requestId,为什么需要生成这个唯一的UUID,后面在解锁的时候会说到

3、获取分布式锁,通过stringRedisTemplate.opsForValue().setIfAbsent来实现,该语句的意思是如果存在该key则返回false,若不存在则进行key的设置,设置成功后返回true,将当前线程获取的uuid设置成value,给定一个锁的过期时间,防止该线程无限制持久锁导致死锁,也为了防止该服务器突然宕机,导致其他机器的应用无法获取该锁,这个是必须要做的设置,至于过期的时间,可以根据内层业务逻辑的执行时间来决定

4、执行内层的业务逻辑,进行扣库存的操作

5、业务逻辑执行完成后,走到finally的解锁操作,进行解锁操作时,首先我们来判断当前锁的值是否为该线程持有的,防止当前线程执行较慢,导致锁过期,从而删除了其他线程持有的分布式锁,对于该操作,我来举个例子:

  • 时刻1:线程A获取分布式锁,开始执行业务逻辑

  • 时刻2:线程B等待分布式锁释放

  • 时刻3:线程A所在机器IO处理缓慢、GC pause等问题导致处理缓慢

  • 时刻4:线程A依旧处于block状态,锁过期

  • 时刻5:线程B获取分布式锁,开始执行业务逻辑,此时线程A结束block,开始释放锁

  • 时刻6:线程B处理业务逻辑缓慢,线程A释放分布式锁,但是此时释放的是线程B的锁,导致其他线程可以开始获取锁

看到这里,为什么每个请求需要requestId,并且在释放锁的情况下判断是否是当前的requestId是有必要的。

以上,就是一个简单的分布式锁的实现过程。但是你觉得上述实现还存在问题吗?

答案是肯定的。若是在判断完分布式锁的value与requestId之后,锁过期了,依然会存在以上问题。

那么有没有什么办法可以规避以上问题,让我们不需要去完成这些实现,只需要专注于业务逻辑呢?

我们可以使用Redisson,并且Redisson有中文文档,方便英文不好的同学查看(开发团队中有中国的jackygurui)。

接下来我们再把上述代码简单的改造下就可以规避这些问题:

public class IndexController {

    public String deductStock() {

        String lockKey = "lock:product_101";

        //setNx 获取分布式锁
        //String clientId = UUID.randomUUID().toString();
        //Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS); //jedis.setnx(k,v)
        //获取锁对象
        RLock redissonLock = redisson.getLock(lockKey);
        //加分布式锁
        redissonLock.lock();
        try {
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
            if (stock > 0) {
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
                System.out.println("扣减成功,剩余库存:" + realStock);
            } else {
                System.out.println("扣减失败,库存不足");
            }
        } finally {
            //解锁
            //if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
            //    stringRedisTemplate.delete(lockKey);
            //}
            //redisson分布式锁解锁
            redissonLock.unlock();
    }
}

可以看到,使用redisson分布式锁会简单很多,我们通过redissonLock.lock()和redissonLock.unlock()解决了这个问题,看到这里,是不是有同学会问,如果服务器宕机了,分布式锁会一直存在吗,也没有去指定过期时间?

redisson分布式锁中有一个watchdog机制,即会给一个leaseTime,默认为30s,到期后锁自动释放,如果一直没有解锁,watchdog机制会一直重新设定锁的过期时间,通过设置TimeTask,延迟10s再次执行锁续命,将锁的过期时间重置为30s。下面就从redisson.lock()的源码来看下:

lock的最终加锁方法:

<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        internalLockLeaseTime = unit.toMillis(leaseTime);

        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
                "if (redis.call('exists', KEYS[1]) == 0) then " +
                        "redis.call('hset', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "return redis.call('pttl', KEYS[1]);",
                Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
    }

可以看到lua脚本中redis.call('pexpire', KEYS[1], ARGV[1]);对key进行设置,并给定了一个internalLockLeaseTime,给定的internalLockLeaseTime就是默认的加锁时间,为30s。

接下来我们在看下锁续命的源码:

private void scheduleExpirationRenewal(final long threadId) {
        if (!expirationRenewalMap.containsKey(this.getEntryName())) {
            Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
                public void run(Timeout timeout) throws Exception {
                    //重新设置锁过期时间
                    RFuture<Boolean> future = RedissonLock.this.commandExecutor.evalWriteAsync(RedissonLock.this.getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then redis.call('pexpire', KEYS[1], ARGV[1]); return 1; end; return 0;", Collections.singletonList(RedissonLock.this.getName()), new Object[]{RedissonLock.this.internalLockLeaseTime, RedissonLock.this.getLockName(threadId)});
                    future.addListener(new FutureListener<Boolean>() {
                        public void operationComplete(Future<Boolean> future) throws Exception {
                            RedissonLock.expirationRenewalMap.remove(RedissonLock.this.getEntryName());
                            if (!future.isSuccess()) {
                                RedissonLock.log.error("Can't update lock " + RedissonLock.this.getName() + " expiration", future.cause());
                            } else {
                                //获取方法调用的结果
                                if ((Boolean)future.getNow()) {
                                    //进行递归调用
                                    RedissonLock.this.scheduleExpirationRenewal(threadId);
                                }

                            }
                        }
                    });
                }
            //延迟 this.internalLockLeaseTime / 3L 再执行run方法
            }, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);
            if (expirationRenewalMap.putIfAbsent(this.getEntryName(), task) != null) {
                task.cancel();
            }

        }
    }

从源码层可以看到,加锁成功后,会延迟10s执行task中的run方法,然后在run方法里面执行锁过期时间的重置,如果时间重置成功,则继续递归调用该方法,延迟10s后进行锁续命,若重置锁时间失败,则可能表示锁已释放,退出该方法。

以上,就是关于一个redis分布式锁的说明,看到这里,大家应该对分布式锁有一个大致的了解了。

但是尽管使用了redisson完成分布式锁的实现,对于分布式锁是否还存在问题,分布式锁真的安全吗?

一般的,线上的环境肯定使用redis cluster,如果数据量不大,也会使用的redis sentinal。那么就存在主从复制的问题,那么是否会存在这种情况,在主库设置了分布式锁,但是可能由于网络或其他原因导致数据还没有同步到从库,此时主库宕机,选择从库作为主库,新主库中并没有该锁的信息,其他线程又可以进行锁申请,造成了发生线程安全问题的可能。

为了解决这个问题,redis的作者实现了redlock,基于redlock的实现有很大的争论,并且现在已经弃用了,但是我们还是需要了解下原理,以及之后基于这些问题的解决方案。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

分布式锁Redlock

Redlock是基于单Redis节点的分布式锁在failover的时候会产生解决不了的安全性问题而产生的,基于N个完全独立的Redis节点。

下面我来看下redlock获取锁的过程:

运行Redlock算法的客户端依次执行下面各个步骤,来完成获取锁 的操作:

  1. 获取当前时间(毫秒数)。

  2. 按顺序依次向N个Redis节点执行获取锁 的操作。这个获取操作跟前面基于单Redis节点的获取锁 的过程相同,包含随机字符串my_random_value,也包含过期时间(比如PX 30000,即锁的有效时间)。为了保证在某个Redis节点不可用的时候算法能够继续运行,这个获取锁 的操作还有一个超时时间(time out),它要远小于锁的有效时间(几十毫秒量级)。客户端在向某个Redis节点获取锁失败以后,应该立即尝试下一个Redis节点。这里的失败,应该包含任何类型的失败,比如该Redis节点不可用,或者该Redis节点上的锁已经被其它客户端持有

  3. 计算整个获取锁的过程总共消耗了多长时间,计算方法是用当前时间减去第1步记录的时间。如果客户端从大多数Redis节点(>= N/2+1)成功获取到了锁,并且获取锁总共消耗的时间没有超过锁的有效时间(lock validity time),那么这时客户端才认为最终获取锁成功;否则,认为最终获取锁失败。

  4. 如果最终获取锁成功了,那么这个锁的有效时间应该重新计算,它等于最初的锁的有效时间减去第3步计算出来的获取锁消耗的时间。

  5. 如果最终获取锁失败了(可能由于获取到锁的Redis节点个数少于N/2+1,或者整个获取锁的过程消耗的时间超过了锁的最初有效时间),那么客户端应该立即向所有Redis节点发起释放锁 的操作。

好了,了解了redlock获取锁的机制之后,我们再来讨论下redlock会有哪些问题:

问题一:

假设一共有5个Redis节点:A, B, C, D, E。设想发生了如下的事件序列:

  1. 客户端1成功锁住了A, B, C,获取锁 成功(但D和E没有锁住)。

  2. 节点C崩溃重启了,但客户端1在C上加的锁没有持久化下来,丢失了。

  3. 节点C重启后,客户端2锁住了C, D, E,获取锁 成功。

这样,客户端1和客户端2同时获得了锁(针对同一资源)。

在默认情况下,Redis的AOF持久化方式是每秒写一次磁盘(即执行fsync),因此最坏情况下可能丢失1秒的数据。为了尽可能不丢数据,Redis允许设置成每次修改数据都进行fsync,但这会降低性能。当然,即使执行了fsync也仍然有可能丢失数据(这取决于系统而不是Redis的实现)。所以,上面分析的由于节点重启引发的锁失效问题,总是有可能出现的。为了应对这一问题,Redis作者antirez又提出了延迟重启 (delayed restarts)的概念。也就是说,一个节点崩溃后,先不立即重启它,而是等待一段时间再重启,这段时间应该大于锁的有效时间(lock validity time)。这样的话,这个节点在重启前所参与的锁都会过期,它在重启后就不会对现有的锁造成影响。

关于Redlock还有一点细节值得拿出来分析一下:在最后释放锁 的时候,antirez在算法描述中特别强调,客户端应该向所有Redis节点发起释放锁 的操作。也就是说,即使当时向某个节点获取锁没有成功,在释放锁的时候也不应该漏掉这个节点。这是为什么呢?设想这样一种情况,客户端发给某个Redis节点的获取锁 的请求成功到达了该Redis节点,这个节点也成功执行了SET操作,但是它返回给客户端的响应包却丢失了。这在客户端看来,获取锁的请求由于超时而失败了,但在Redis这边看来,加锁已经成功了。因此,释放锁的时候,客户端也应该对当时获取锁失败的那些Redis节点同样发起请求。实际上,这种情况在异步通信模型中是有可能发生的:客户端向服务器通信是正常的,但反方向却是有问题的。

所以,如果不进行延迟重启,或者对于同一个主节点进行多个从节点的备份,并要求从节点的同步必须实时跟住主节点,也就是说需要配置redis从库的同步策略,将延迟设置为最小(主从同步是异步进行的),通过min-replicas-max-lag(旧版本的redis使用min-slaves-max-lag)来设置主从库间进行数据复制时,从库给主库发送 ACK 消息的最大延迟(以秒为单位),也就是说,这个值需要设置为0,否则都有可能出现延迟,但是这个实际上在redis中是不存在的,min-replicas-max-lag设置为0,就代表着这个配置不生效。redis本身是为了高效而存在的,如果因为需要保证业务的准确性而使用,大大降低了redis的性能,建议使用的别的方式。

问题二:

如果客户端长期阻塞导致锁过期,那么它接下来访问共享资源就不安全了(没有了锁的保护)。在RedLock中还是存在该问题的。

虽然在获取锁之后Redlock会去判断锁的有效性,如果锁过期了,则会再去重新拿锁。但是如果发生在获取锁之后,那么该有效性都得不到保障了。

9f5defad22cc762c59e53bcd19223f70.jpeg

在上面的时序图中,假设锁服务本身是没有问题的,它总是能保证任一时刻最多只有一个客户端获得锁。上图中出现的lease这个词可以暂且认为就等同于一个带有自动过期功能的锁。客户端1在获得锁之后发生了很长时间的GC pause,在此期间,它获得的锁过期了,而客户端2获得了锁。当客户端1从GC pause中恢复过来的时候,它不知道自己持有的锁已经过期了,它依然向共享资源(上图中是一个存储服务)发起了写数据请求,而这时锁实际上被客户端2持有,因此两个客户端的写请求就有可能冲突(锁的互斥作用失效了)。

初看上去,有人可能会说,既然客户端1从GC pause中恢复过来以后不知道自己持有的锁已经过期了,那么它可以在访问共享资源之前先判断一下锁是否过期。但仔细想想,这丝毫也没有帮助。因为GC pause可能发生在任意时刻,也许恰好在判断完之后。

也有人会说,如果客户端使用没有GC的语言来实现,是不是就没有这个问题呢?质疑者Martin指出,系统环境太复杂,仍然有很多原因导致进程的pause,比如虚存造成的缺页故障(page fault),再比如CPU资源的竞争。即使不考虑进程pause的情况,网络延迟也仍然会造成类似的结果。

总结起来就是说,即使锁服务本身是没有问题的,而仅仅是客户端有长时间的pause或网络延迟,仍然会造成两个客户端同时访问共享资源的冲突情况发生。

那怎么解决这个问题呢?Martin给出了一种方法,称为fencing token。fencing token是一个单调递增的数字,当客户端成功获取锁的时候它随同锁一起返回给客户端。而客户端访问共享资源的时候带着这个fencing token,这样提供共享资源的服务就能根据它进行检查,拒绝掉延迟到来的访问请求(避免了冲突)。如下图:

在上图中,客户端1先获取到的锁,因此有一个较小的fencing token,等于33,而客户端2后获取到的锁,有一个较大的fencing token,等于34。客户端1从GC pause中恢复过来之后,依然是向存储服务发送访问请求,但是带了fencing token = 33。存储服务发现它之前已经处理过34的请求,所以会拒绝掉这次33的请求。这样就避免了冲突。

但是,对于客户端和资源服务器之间的延迟(即发生在算法第3步之后的延迟),antirez是承认所有的分布式锁的实现,包括Redlock,是没有什么好办法来应对的。包括在我们到生产环境中,无法避免分布式锁超时。

在讨论中,有人提出客户端1和客户端2都发生了GC pause,两个fencing token都延迟了,它们几乎同时到达了文件服务器,而且保持了顺序。那么,我们新加入的判断逻辑,即判断fencing token的合理性,应该对两个请求都会放过,而放过之后它们几乎同时在操作文件,还是冲突了。既然Martin宣称fencing token能保证分布式锁的正确性,那么上面这种可能的猜测也许是我们理解错了。但是Martin并没有在后面做出解释。

问题三:

Redlock对系统记时(timing)的过分依赖,下面给出一个例子(还是假设有5个Redis节点A, B, C, D, E):

  1. 客户端1从Redis节点A, B, C成功获取了锁(多数节点)。由于网络问题,与D和E通信失败。

  2. 节点C上的时钟发生了向前跳跃,导致它上面维护的锁快速过期。

  3. 客户端2从Redis节点C, D, E成功获取了同一个资源的锁(多数节点)。

  4. 客户端1和客户端2现在都认为自己持有了锁。

上面这种情况之所以有可能发生,本质上是因为Redlock的安全性(safety property)对系统的时钟有比较强的依赖,一旦系统的时钟变得不准确,算法的安全性也就保证不了了。

但是作者反驳到,通过恰当的运维,完全可以避免时钟发生大的跳动,而Redlock对于时钟的要求在现实系统中是完全可以满足的。哪怕是手动修改时钟这种人为原因,不要那么做就是了。否则的话,都会出现问题。

说了这么多关于Redlock的问题,到底有没有什么分布式锁能保证安全性呢?我们接下来再来看看ZooKeeper分布式锁。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

基于ZooKeeper的分布式锁更安全吗?

很多人(也包括Martin在内)都认为,如果你想构建一个更安全的分布式锁,那么应该使用ZooKeeper,而不是Redis。那么,为了对比的目的,让我们先暂时脱离开本文的题目,讨论一下基于ZooKeeper的分布式锁能提供绝对的安全吗?它需要fencing token机制的保护吗?

Flavio Junqueira是ZooKeeper的作者之一,他的这篇blog就写在Martin和antirez发生争论的那几天。他在文中给出了一个基于ZooKeeper构建分布式锁的描述(当然这不是唯一的方式):

  • 客户端尝试创建一个znode节点,比如/lock。那么第一个客户端就创建成功了,相当于拿到了锁;而其它的客户端会创建失败(znode已存在),获取锁失败。

  • 持有锁的客户端访问共享资源完成后,将znode删掉,这样其它客户端接下来就能来获取锁了。

  • znode应该被创建成ephemeral的。这是znode的一个特性,它保证如果创建znode的那个客户端崩溃了,那么相应的znode会被自动删除。这保证了锁一定会被释放。

看起来这个锁相当完美,没有Redlock过期时间的问题,而且能在需要的时候让锁自动释放。但仔细考察的话,并不尽然。

ZooKeeper是怎么检测出某个客户端已经崩溃了呢?实际上,每个客户端都与ZooKeeper的某台服务器维护着一个Session,这个Session依赖定期的心跳(heartbeat)来维持。如果ZooKeeper长时间收不到客户端的心跳(这个时间称为Sesion的过期时间),那么它就认为Session过期了,通过这个Session所创建的所有的ephemeral类型的znode节点都会被自动删除。

设想如下的执行序列:

  1. 客户端1创建了znode节点/lock,获得了锁。

  2. 客户端1进入了长时间的GC pause。

  3. 客户端1连接到ZooKeeper的Session过期了。znode节点/lock被自动删除。

  4. 客户端2创建了znode节点/lock,从而获得了锁。

  5. 客户端1从GC pause中恢复过来,它仍然认为自己持有锁。

最后,客户端1和客户端2都认为自己持有了锁,冲突了。这与之前Martin在文章中描述的由于GC pause导致的分布式锁失效的情况类似。

看起来,用ZooKeeper实现的分布式锁也不一定就是安全的。该有的问题它还是有。但是,ZooKeeper作为一个专门为分布式应用提供方案的框架,它提供了一些非常好的特性,是Redis之类的方案所没有的。像前面提到的ephemeral类型的znode自动删除的功能就是一个例子。

还有一个很有用的特性是ZooKeeper的watch机制。这个机制可以这样来使用,比如当客户端试图创建/lock的时候,发现它已经存在了,这时候创建失败,但客户端不一定就此对外宣告获取锁失败。客户端可以进入一种等待状态,等待当/lock节点被删除的时候,ZooKeeper通过watch机制通知它,这样它就可以继续完成创建操作(获取锁)。这可以让分布式锁在客户端用起来就像一个本地的锁一样:加锁失败就阻塞住,直到获取到锁为止。这样的特性Redlock就无法实现。

小结一下,基于ZooKeeper的锁和基于Redis的锁相比在实现特性上有两个不同:

  • 在正常情况下,客户端可以持有锁任意长的时间,这可以确保它做完所有需要的资源访问操作之后再释放锁。这避免了基于Redis的锁对于有效时间(lock validity time)到底设置多长的两难问题。实际上,基于ZooKeeper的锁是依靠Session(心跳)来维持锁的持有状态的,而Redis不支持Session。

  • 基于ZooKeeper的锁支持在获取锁失败之后等待锁重新释放的事件。这让客户端对锁的使用更加灵活。

总结

综上所述,我们可以得出两种结论:

  • 如果仅是为了效率(efficiency),那么你可以自己选择你喜欢的一种分布式锁的实现。当然,你需要清楚地知道它在安全性上有哪些不足,以及它会带来什么后果,这也是为什么我们需要了解实现原理的原因,大多数情况下不会出问题,但是就万一的情况,处理起来可能需要大量的时间定位问题。

  • 如果你是为了正确性(correctness),那么请慎之又慎。就目前来说ZooKeeper的分布锁相对于redlock更加合理。

最后,由于redlock的出现其实是为了保证分布式锁的可靠性,但是由于实现的种种问题其可靠性并没有ZooKeeper分布式锁来的高,对于可容错的希望效率的场景下,redis分布式锁又可以完全满足,这也是导致了redlock被弃用的原因。

参考: http://zhangtielei.com/posts/blog-redlock-reasoning.html



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢:

771fd7dc96a38cd2186e98b70cd8b0cc.png

已在知识星球更新源码解析如下:

823363c9678a54497e176069ce878ce9.jpeg

5ffe7b55bce4b9d66d7ebe7e08602cdd.jpeg

9e321b900dcc9e8c19cbb9fab8f2306f.jpeg

c898ded37533971e066b5d70f143d2ef.jpeg

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
FastDDS之共享内存 transport层负责为DDS用户数据收发和服务发现提供通信。包含UDP,TCP,SHM。 阅读详情

相关推荐

DySAT+: Enhancing Dynamic Graph Representation Learning with Multi-Head Self-Attention and Temporal-

本文深入探讨了DySAT+模型在动态图表示学习中的创新应用,通过多头时空注意力机制和层级信息融合策略,有效解决了动态图数据中的时间维度信息捕捉和结构演变动态适应问题。该模型在社交网络异常检测和金融交易网络分析等场景中表现出色,显著提升了预测准确率和响应速度。

weixin_29035405的博客 337

Redis 实现分布式锁真的安全吗?

Spring Boot 作为主流微服务框架,拥有成熟的社区生态。市场应用广泛,为了方便大家,整理了一个基于spring boot的常用中间件快速集成入门系列手册,涉及RPC、缓存、消息队列、分库分表、注册中心、分布式配置等常用开源组件,大概有几十篇文章,陆续会开放出来,感兴趣同学可以关注&收藏 锁的种类非常多。之前写过一篇文章,对工作中常用锁做了总结,如:乐观锁、悲观锁、分布式锁、可重入锁、自旋锁、独享锁、共享锁、互斥锁、读写锁、阻塞锁、公平锁、非公平锁、分段锁、对象锁、类锁、信号量、行锁。 .

公众号:微观技术 1258

解决Cursor AI工具地域限制报错的方法与原理

地理围栏(GEO-fencing)是现代云服务常用的访问控制技术,通过IP地址库匹配、支付系统验证等方式实现区域限制。在AI编程工具领域,这种技术常导致类似Cursor出现的'unsupported country region'报错。从技术实现看,服务商会综合HTTP头信息、时区设置等多维度数据进行地理位置判断。开发者可通过调整网络出口、配置本地模型或使用企业版等方案解决。本文以Cursor为例,详细分析了AI工具地域限制的底层原理,并提供了包括Ollama本地部署在内的多种工程实践方案,特别适合需要稳定

weixin_30820077的博客 979

基于Redis的分布式锁到底安全吗(上)

网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词“Redis 分布式锁”随便到哪个搜索引擎上去搜索一下就知道了。这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多。实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间就发生过一场争论。由于对这个问题一直以来比较关注,所以我前些日子仔细阅读了与这场争论相关的资料。

Eden 的博客 997

QGIS:安装Deepness插件+应用

简单的描述了deepness的安装应用历程,重点是记录博主本次遇到的坑

fengyi157的博客 2342

FreeSWITCH实战:5分钟搞定阿里云SDM(MRCP-SERVER)语音机器人搭建(含Docker部署技巧)

本文详细介绍了如何利用FreeSWITCH与阿里云SDM(MRCP-SERVER)快速搭建高可用语音机器人,包括Docker部署技巧、系统调优及高可用架构设计。通过实战案例和优化建议,帮助开发者5分钟内完成部署,提升语音交互系统的性能和稳定性。

stone的博客 505

Redission分布式锁知识讲解大全

Redisson 是基于 Redis 的高性能 Java 客户端,它提供了丰富的工具来简化分布式系统中的常见问题。分布式锁是其中一个重要的功能。Redisson 分布式锁允许在分布式环境中保证对共享资源的独占访问。通过 Redisson 提供的RLock类,用户可以很容易地实现分布式锁机制。Redisson 分布式锁为分布式系统提供了一个高效、可靠的锁机制,确保了在多个节点并发访问共享资源时的同步与互斥。

weixin_73144915的博客 2142

7.19华为OD机试真题 新系统 - 物流仓储多维度成本利润综合查询系统 (Java/Py/C/C++/Js/Go)

7.19华为OD机试真题 新系统 - 物流仓储多维度成本利润综合查询系统 (Java/Py/C/C++/Js/Go)

算法大师! 618

Redisson实现分布式锁原理

Redisson实现分布式锁原理 一、高效分布式锁 当我们在设计分布式锁的时候,我们应该考虑分布式锁至少要满足的一些条件,同时考虑如何高效的设计分布式锁,这里我认为以下几点是必须要考虑的。 1、互斥 在分布式高并发的条件下,我们最需要保证,同一时刻只能有一个线程获得锁,这是最基本的一点。 2、防止死锁 在分布式高并发的条件下,比如有个线程获得锁的同时,还没有来得及去释放锁,就因为系统故障或者其它原因使它无法执行释放锁的命令,导致其它线程都无法获得锁,造成死锁。所以分布式非常有必要设置锁的有效时间,确保系统

程序猿进阶 3001

【用ArcGIS制作一张好看的中国月度气温图

要最后选中那个事先调整好的那个数据框,这样其他两个数据框才能和其大小一致。

J_Giser的博客 1018

自动化上位机开发C#100例:如何用面向对象的方式封装雷赛运动控制卡EtherCAT总线卡(C#代码)

自动化上位机开发C#100例:雷赛运动控制卡EtherCAT总线卡C#封装类。

十多年IT经验,专注机器视觉,智能制造半导体行业 5035

浅谈Redis分布式锁——分布式锁真的能做到安全吗

浅谈Redis分布式锁——分布式锁真的能做到安全吗

m0_38041658的博客 1141

多模态论文笔记——BLIP

本文详细介绍这几年AIGC火爆的隐藏功臣,多模态模型:BLIP。

haopinglianlian的博客 2454

Redis分布式锁到底安全吗?你真的会用 Redis 分布式锁么?

关于 Redis 分布式锁的话题,很多文章已经写烂了,我为什么还要写这篇文章呢? 因为今天这篇文章想探讨下,关于 Redis 分布式锁的使用 ,以及redis 处理问题的思路,包括「安全性」问题。 因为我发现网上 99% 的文章,并没有把这个问题真正讲清楚。导致很多读者看了很多文章,依旧云里雾里。例如下面这些问题,你能清晰地回答上来吗? 基于 Redis 如何实现一个分布式锁? Redis 分布式锁真的安全吗? Redis 的 Redlock 有什么问题?一定安全吗? 业界争论 Redlock,到底在争论

Yang_yangyang的博客 434

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

基于Redis的分布式锁真的安全吗?

说明: 我前段时间写了一篇用consul实现分布式锁,感觉理解的也不是很好,直到我看到了这2篇写分布式锁的讨论,真的是很佩服作者严谨的态度, 把这种分布式锁研究的这么透彻,作者这种技术态度真的值得我好好的学习。 建议大家去看看原文(上) (下),我这里是做一个备份。 基于Redis的分布式锁真的安全吗?(上) From: 张铁蕾 DBAplus社群基于Redis的分布式锁真的安全...

weixin_30735745的博客 166

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

基于Redis的分布式锁真的安全吗?(下)

自从我写完这个话题的上半部分之后,就感觉头脑中出现了许多细小的声音,久久挥之不去。它们就像是在为了一些鸡毛蒜皮的小事而相互争吵个不停。的确,有关分布式的话题就是这样,琐碎异常,而且每个人说的话听起来似乎都有道理。 今天,我们就继续探讨这个话题的后半部分。本文中,我们将从Antirez反驳Martin Kleppmann的观点开始讲起,然后会涉及到Ha...

weixin_33795743的博客 399

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

基于Redis的分布式锁真的安全吗?(上)

网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词“Redis 分布式锁”随便到哪个搜索引擎上去搜索一下就知道了。这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多。 实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin...

weixin_34335458的博客 97

Redis分布式锁真的安全吗?Java环境下常见漏洞及修复指南

深入剖析Java连接Redis实现分布式锁的安全隐患,提供可落地的漏洞修复方案。涵盖Redlock算法、过期时间控制与原子性操作等核心方法,适用于高并发场景。避免常见误用导致的锁失效问题,提升系统可靠性,值得收藏。

VarFun的博客 368

艾体宝干货 | 【Redis实用技巧#3】Redis分布式锁真的安全吗?可靠性深度剖析

先想想这些问题,你能清晰回答吗?为什么需要分布式锁?如何正确实基于 Redis 的分布式锁Redis 分布式锁中的死锁预防机制如何防止锁被其他线程释放如何解决分布式锁过期时间的不确定性Redlock:它真的安全吗?Redis 和 ZooKeeper 在分布式锁中的对比:该选哪一个?构建容错的分布式锁:完整的CheckList包含哪些看完本文,你不仅能彻底搞懂分布式锁,还会对分布式系统有更深的理解。

虹科网络安全的博客 1148

生产事故复盘:你写的Redis分布式锁真的安全吗?万字图解Redisson锁底层原理

│ 分布式锁最佳实践清单 ││ ││ 【必须做】 ││ 1. 使用Redisson提供的分布式锁(不要自己实现) ││ 2. 始终在finally块中释放锁 ││ 3. 释放锁前检查是否是当前线程持有 ││ 4. 设置合理的等待时间和持有时间 ││ 5. 使用看门狗机制处理业务执行时间不确定的场景 ││ ││ 【不要做】 ││ 1. 不要使用简单的SETNX命令 ││ 2. 不要在业务代码中直接使用Thread.sleep测试 │。

2401_83440791的博客 1211

艾体宝干货 |【Redis实用技巧#4】Redis分布式锁真的安全吗?可靠性深度剖析(Part 2)

好,现在我们来到了分布式锁讨论的核心部分。如果还没读过前一篇,我强烈建议先读一遍,了解加锁和解锁的基本流程。后面我不仅会解释 Redlock 的原理,还会抛出很多与分布式系统相关的疑问。最好能跟着我的思路,在心里一起分析答案。现在,我们来看看 Redis 创始人提出的 Redlock 方案,如何解决主从切换后锁失效的问题。Redlock 方案基于两个前提:不再部署**从库(replica)和哨兵(sentinel)实例,只部署主库(master)**实例但需要部署多个主库实例,官方建议至少 5 个换句话说,

虹科网络安全的博客 1214

基于Redis的分布式锁到底安全吗(下)

我们前面提到过,当各方的争论在激烈进行的时候,Martin几乎始终置身事外。但是Martin在这件事过去之后,把这个事件的前后经过总结成了一个很长的故事线。在这个故事总结的最后,Martin写下了很多感性的评论:while.(译文:对我来说最重要的一点在于:我并不在乎在这场辩论中谁对谁错 ——我只关心从其他人的工作中学到的东西,以便我们能够避免重蹈覆辙,并让未来更加美好。前人已经为我们创造出了许多伟大的成果:站在巨人的肩膀上,我们得以构建更棒的软件。

Karka_的博客 1530
上一篇: 总结如何画一手好的架构图
下一篇: Java 云原生微服务框架 Quarkus 入门实践
公众号-芋道源码
博客等级 码龄10年 1634粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值