Глобальная распределенная блокировка, для которой можно установить срок действия 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();
}
Другие вопросы по тегам