Глобальная распределенная блокировка, для которой можно установить срок действия Java
У меня есть случай, когда я хочу иметь глобально распределенную блокировку. Мы начали использовать SELECT .. FOR UPDATE
, но это быстро стало иметь проблемы, когда мы увеличили количество серверов. Также он не учитывал процессы, которые проверили блокировку, а затем умерли и не смогли вернуть блокировку.
Нам нужно иметь возможность установить срок действия блокировки (т. Е. Если процесс, который снял блокировку, не возвращает ее через 2 часа, блокировка автоматически возвращается в пул). Я понимаю, что это приводит к проблеме, когда мы игнорируем блокировки, но мы совершенно уверены, что процесс завершится, если не завершится через 2 часа. Кроме того, работа идемпотентна, поэтому, если она выполняется не раз, это не имеет большого значения.
Я просмотрел несколько распределенных систем блокировки и натолкнулся на эти вопросы, которые были чрезвычайно полезны. Все решения выходят за рамки Java java.util.concurrency.locks.Lock
, что на самом деле может быть проблемой, с которой я сталкиваюсь, потому что у этого интерфейса нет нужной мне функции истечения срока действия. У нас есть стратегия, аналогичная https://github.com/deftlabs/mongo-java-distributed-lock, где мы используем MongADB findAndModify. Мы рассматриваем:
как наш механизм распределенной блокировки (все происходит, чтобы реализовать java.util.concurrency.locks.Lock
).
Самая большая проблема в том, что, потому что java.util.concurrency.locks.Lock
не имеет возможности для истечения срока блокировки, они не соответствуют всем целям. Этот ответ, вероятно, ближе всего подходит к hazelcast, но он зависит от сбоя всего сервера, а не от потока, занимающего слишком много времени. Другой вариант, возможно, использование Samaphore с фундуком, как описано здесь. У меня могла бы быть нить жнеца, которая затем могла бы отменять блокировки других, если они занимают слишком много времени. С Mongo и Redis я мог бы воспользоваться их способностью истекать объекты, но это, похоже, не является частью какой-либо из библиотек, так как они просто реализуют java.util.concurrency.locks.Lock
в конце.
Так что это был просто долгий способ спросить, есть ли механизм распределенной блокировки, который я мог бы автоматически истечь через N секунд? Должен ли я смотреть на другой механизм, чем java.util.concurrency.locks.Lock
в этой ситуации вообще?
4 ответа
Вы можете использовать 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();
Вы должны рассмотреть возможность использования Zookeeper. И есть простая в использовании библиотека для такого рода "распределенных" вещей, которая построена на основе zookeeper: curator framework. Я думаю, что вы ищете, это общий реентерабельный замок. Вы также можете проверить другие замки в рецептах.
Что насчет этого? 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)
Попытки получить блокировку имени. Возвращает true, как только блокировка получена. Если в данный момент блокировка удерживается другим потоком в этом или любом другом процессе в распределенной системе, или другой поток в системе заблокировал весь сервис, этот метод продолжает пытаться получить блокировку вплоть до waitTimeMillis, прежде чем отказаться и вернуть false., Если блокировка получена, она удерживается до тех пор, пока не будет вызвана разблокировка (имя объекта) или пока не пройдут миллисекунды leaseTimeMillis с момента предоставления блокировки - в зависимости от того, что наступит раньше.
На самом деле, насколько я могу судить, Mongo-Java-распределенный-блокировка имеет возможность истечь блокировки с помощью использования 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();
}