【问题标题】:global distributed lock that can be set to expire Java可设置为过期 Java 的全局分布式锁
【发布时间】:2014-05-11 02:40:47
【问题描述】:

我有一个用例,我想拥有一个全局分布式锁。我们开始使用SELECT .. FOR UPDATE,但随着我们扩大服务器数量,很快就开始出现问题。它也没有考虑检查锁然后死亡并且未能返回锁的进程。

我们需要能够设置锁的过期时间(即如果签出锁的进程在 2 小时内没有返回它,锁会自动返回到池中)。我意识到这引入了我们忽略锁的问题,但我们相当肯定如果没有在 2 小时内完成,该过程已经死亡。而且这项工作是幂等的,所以如果它被多次完成也没什么大不了的。

我查看了许多分布式锁定系统并遇到了非常有用的this questions。所有解决方案都从 Java 的java.util.concurrency.locks.Lock 扩展而来,这实际上可能是我遇到的问题,因为该接口没有我需要的过期功能。我们有与mongo-java-distributed-lock 类似的策略,我们使用MongoDB 的findAndModify。我们正在考虑:

作为我们的分布式锁定机制(都恰好实现了java.util.concurrency.locks.Lock)。

最大的问题是因为java.util.concurrency.locks.Lock 没有使锁过期的选项,所以这些不适合所有目标。 answer 可能与 hazelcast 最接近,但它依赖于整个服务器的故障,而不仅仅是一个线程花费太长时间。另一种选择可能是使用带有 hazelcast 的 Samophore,如 here 所述。我可以有一个收割线程,如果他们花费的时间太长,它可以取消其他人的锁。使用 Mongo 和 Redis,我可以利用它们使对象过期的能力,但这似乎不是这两个库的一部分,因为它们最终只是实现了 java.util.concurrency.locks.Lock

所以这只是一个冗长的询问方式,是否有分布式锁定机制可以在 N 秒后自动过期?在这种情况下,我是否应该考虑与java.util.concurrency.locks.Lock 完全不同的机制?

【问题讨论】:

  • 如果您对自己的滚动感到满意,那么您可以考虑扩展 Lock 并添加一个静态观察线程,该线程保留一个活动锁列表并定期检查过期时间。
  • 我很想自己动手,但如果已经有解决方案可以做到这一点,我也不想重新发明轮子。
  • @flup,我明确要求提供一个提供过期锁的解决方案。所有其他 SO 问题都只是关于分布式锁,而不是分布式过期锁,这是我关心的关键特性。

标签: java concurrency locking semaphore distributed-lock


【解决方案1】:

您可以基于Redis 服务器使用Redisson。它实现了熟悉的 Java 数据结构,包括具有分布式和可扩展能力的 java.util.Lock。包括设置锁定释放超时的能力。使用示例:

Config config = new Config();
// for single server
config.useSingleServer()
      .setAddress("127.0.0.1:6379");
// or 
// for master/slave servers
config.useSentinelConnection()
      .setMasterName("mymaster")
      .addSentinelAddress("127.0.0.1:26389", "127.0.0.1:26379");

Redisson redisson = Redisson.create(config);

Lock lock = redisson.getLock("anyLock");
try {
   // unlock automatically after 10 seconds of hold
   lock.lock(10, TimeUnit.SECONDS);

} finally {
   lock.unlock();
}

...

redisson.shutdown();

【讨论】:

  • 链接到其他答案以及此功能,以防其他人遇到。 stackoverflow.com/a/21072066/311525
  • 如果持有进程死亡,锁会发生什么?
  • @MikeStoddart Redisson 维护锁看门狗,它在锁持有者 Redisson 实例存活时延长锁的过期时间。
  • 谢谢@NikitaKoksharov,我认为这意味着当持有进程终止时锁没有被释放,而是当看门狗清理它时?
  • @MikeStoddart 锁会在看门狗不扩展时自动释放。如果进程死了,那么看门狗也死了,因此锁定将被释放,因为它没有被扩展
【解决方案2】:

您应该考虑使用zookeeper。并且有一个易于使用的库,用于构建在 zookeeper 之上的这些“分布式”东西:curator framework。我认为您正在寻找的是shared reentrant lock。也可以在recipes查看其他锁。

【讨论】:

  • 我没有看到 zookeeper 在哪里提供了过期的锁。我知道它是分布式锁的解决方案,但它似乎没有提供我正在寻找的过期功能。如果该功能确实存在,您能否提供指向它的链接,因为我无法找到它。
  • 在zookeeper中,如果锁所有者会话丢失,就像你的hazelcast示例一样,锁将会过期。我能理解您对“崩溃”的担忧;但是给锁本身超时似乎充其量是奇怪的:)
  • 我得到了“崩溃”部分。用例是我有许多演员锁定资产。其中一个演员死了,但服务器仍然嗡嗡作响。演员正在做的工作需要在一段时间后回到池中,以避免该工作永远不会完成。这项工作是幂等的,所以我不在乎它被完成两次,只是它被完成了。我不认为这很“奇怪”,它已经多次作为我的要求出现了。
【解决方案3】:

这个呢? http://www.gemstone.com/docs/5.5.0/product/docs/japi/com/gemstone/gemfire/distributed/DistributedLockService.html

它的lock 方法似乎有你需要的东西:

public abstract boolean lock(Object name, long waitTimeMillis, long leaseTimeMillis)

尝试获取名为 name 的锁。获取锁后立即返回 true。如果锁当前被这个或分布式系统中的任何其他进程中的另一个线程持有,或者系统中的另一个线程已经锁定了整个服务,则该方法在放弃之前一直尝试获取锁直到 waitTimeMillis 并返回 false .如果获得了锁,它会一直保持到调用 unlock(Object name),或者直到自授予锁起已经过去了 leaseTimeMillis 毫秒 - 以先到者为准。

【讨论】:

    【解决方案4】:

    实际上,据我所知,mongo-java-distributed-lock 可以通过使用DistributedLockOptions.setInactiveLockTimeout() 使锁过期

    我还没有尝试过,但我想我会......

    编辑:我现在也试过了,效果很好……

    String lockName = "com.yourcompany.yourapplication.somelock";
    int lockTimeoutMilliSeconds = 500;
    
    String dbURI = CWConfig.get().getMongoDBConfig().getDbURI();
    DistributedLockSvcFactory lockSvcFactory = new DistributedLockSvcFactory(new DistributedLockSvcOptions(dbURI));
    DistributedLockSvc lockSvc = lockSvcFactory.getLockSvc();
    
    DistributedLock lock = lockSvc.create(lockName);
    lock.getOptions().setInactiveLockTimeout(lockTimeoutMilliSeconds);
    try {
        lock.lock();
        // Do work
    } finally {
        lock.unlock();
    }
    

    【讨论】:

      猜你喜欢
      • 2023-02-09
      • 2017-02-24
      • 2016-01-19
      • 1970-01-01
      • 1970-01-01
      • 2021-11-27
      • 1970-01-01
      • 2015-02-25
      • 1970-01-01
      相关资源
      最近更新 更多