我有一个用例,我想要一个全局分布的锁。我们开始使用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最接近,但它依赖于整个服务器失败,而不仅仅是一个线程花费太长时间。另一个选择是使用描述为here的淡褐色信号量。我可以有一个收割者线程,如果他们花了太长时间就可以取消其他人的锁定。使用Mongo和Redis,我可以利用它们使对象到期的能力,但这似乎不是任何一个库的一部分,因为它们最终只实现了java.util.concurrency.locks.Lock
。
所以这只是一个冗长的问题,是否有一个分布式锁定机制,我可以在N秒后自动过期?在这种情况下,我是否应该考虑与java.util.concurrency.locks.Lock
不同的机制?
答案 0 :(得分:3)
您可以根据Redisson服务器使用Redis。它实现了熟悉的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();
答案 1 :(得分:2)
您应该考虑使用zookeeper。并且有一个易于使用的库,用于构建在zookeeper上的这种“分布式”的东西:curator framework。我认为你要找的是shared reentrant lock。您还可以在recipes中查看其他锁定。
答案 2 :(得分:2)
它的lock
方法似乎有你需要的东西:
public abstract boolean lock(Object name,
long waitTimeMillis,
long leaseTimeMillis)
尝试获取名为lock的锁。获取锁定后立即返回true。如果锁当前由分布式系统中的此进程或任何其他进程中的另一个线程持有,或者系统中的另一个线程已锁定整个服务,则此方法在放弃并返回false之前一直尝试获取最多等待waitMillis的锁。如果获取了锁,则会一直保持锁定,直到调用unlock(对象名称),或者直到从授予锁定开始起已经过了leaseTimeMillis毫秒 - 以先到者为准。
答案 3 :(得分: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();
}