2

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

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

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

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

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

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

4

4 回答 4

3

您可以使用基于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();
于 2014-07-15T18:12:55.153 回答
2

这个如何? 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 毫秒——以先到者为准。

于 2014-04-16T21:20:19.740 回答
2

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

于 2014-04-16T08:42:49.190 回答
1

实际上,据我所知,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();
}
于 2016-05-31T13:03:57.200 回答