Распределенный контроль параллелизма - PullRequest
54 голосов
/ 18 сентября 2008

Я работаю над этим уже несколько дней, и я нашел несколько решений, но ни одно из них не было невероятно простым или легким. Основная проблема заключается в следующем: у нас есть кластер из 10 машин, на каждом из которых работает одно и то же программное обеспечение на многопоточной платформе ESB. Я могу довольно легко справиться с проблемами параллелизма между потоками на одной машине, но как насчет параллелизма на одних и тех же данных на разных машинах?

По сути, программное обеспечение получает запросы на передачу данных клиента из одного предприятия в другое через веб-службы. Однако клиент может существовать или не существовать в другой системе. Если это не так, мы создаем его с помощью метода веб-службы. Так что это требует своего рода тестирования и установки, но мне нужен какой-то семафор, чтобы блокировать другие машины от возникновения условий гонки. Раньше у меня были ситуации, когда удаленного клиента создавали дважды для одного локального клиента, что на самом деле нежелательно.

Решения, с которыми я концептуально играл:

  1. Использование нашей отказоустойчивой общей файловой системы для создания «блокирующих» файлов, которые будут проверяться каждой машиной в зависимости от клиента

  2. Использование специальной таблицы в нашей базе данных и блокировка всей таблицы, чтобы выполнить «проверку и установку» для записи блокировки.

  3. Использование Terracotta, серверного программного обеспечения с открытым исходным кодом, которое помогает в масштабировании, но использует модель со спицами.

  4. Использование EHCache для синхронной репликации моих "блокировок" в памяти.

Я не могу себе представить, что я единственный человек, у которого когда-либо были подобные проблемы. Как ты это решил? Вы готовили что-то на месте или у вас есть любимый продукт стороннего производителя?

Ответы [ 13 ]

34 голосов
/ 19 сентября 2008

вы можете рассмотреть возможность использования Hazelcast распределенных блокировок. Супер легкий и легкий.

java.util.concurrent.locks.Lock lock = Hazelcast.getLock ("mymonitor");
lock.lock ();
try {
// do your stuff
}finally {
   lock.unlock();
}

Hazelcast - распределенная очередь, карта, набор, список, блокировка

13 голосов
/ 17 октября 2008

Мы используем терракоту, поэтому я бы хотел проголосовать за это.

Я следил за Hazelcast, и это похоже на другую многообещающую технологию, но я не могу голосовать за нее, так как я не использовал ее, и зная, что она использует систему на основе P2P, я действительно не стал бы доверять это для больших нужд масштабирования.

Но я также слышал о Zookeeper, который вышел из Yahoo и движется под зонтиком Hadoop. Если вы пытаетесь попробовать какую-то новую технологию, это действительно многообещающе, так как она очень скудная и злая, фокусируясь только на координации. Мне нравится видение и обещание, хотя оно все еще может быть слишком зеленым.

4 голосов
/ 19 сентября 2008

Terracotta ближе к «многоуровневой» модели - все клиентские приложения взаимодействуют с массивом серверов Terracotta (и, что более важно, для масштабирования они не общаются друг с другом). Серверный массив Terracotta способен кластеризоваться как для масштабирования, так и для доступности (зеркальный, для доступности и полосатый, для масштаба).

В любом случае, как вы, вероятно, знаете, Terracotta дает вам возможность выражать параллелизм во всем кластере так же, как вы делаете это в одной JVM, используя синхронизированный POJO / wait / notify или любой из примитивов java.util.concurrent. такие как ReentrantReadWriteLock, CyclicBarrier, AtomicLong, FutureTask и т. д.

Существует множество простых рецептов, демонстрирующих использование этих примитивов в Терракотовая кулинарная книга .

В качестве примера я опубликую пример ReentrantReadWriteLock (обратите внимание, что нет версии Terracotta для блокировки - вы просто используете обычный Java ReentrantReadWriteLock)

import java.util.concurrent.locks.*;

public class Main
{
    public static final Main instance = new Main();
    private int counter = 0;
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(true);

    public void read()
    {
        while (true) {
            rwl.readLock().lock();
                try {
                System.out.println("Counter is " + counter);
            } finally {
                rwl.readLock().unlock();
            }
            try { Thread.currentThread().sleep(1000); } catch (InterruptedException ie) {  }
        }
    }

    public void write()
    {
        while (true) {
            rwl.writeLock().lock();
            try {
               counter++;
               System.out.println("Incrementing counter.  Counter is " + counter);
            } finally {
                 rwl.writeLock().unlock();
            }
            try { Thread.currentThread().sleep(3000); } catch (InterruptedException ie) {  }
        }
    }

    public static void main(String[] args)
    {
        if (args.length > 0)  {
            // args --> Writer
            instance.write();
        } else {
            // no args --> Reader
            instance.read();
        }
    }
}
3 голосов
/ 12 января 2014

Рекомендую использовать Redisson . Он реализует более 30 распределенных структур данных и сервисов, включая java.util.Lock. Пример использования:

Config config = new Config();
config.addAddress("some.server.com:8291");
Redisson redisson = Redisson.create(config);

Lock lock = redisson.getLock("anyLock");
lock.lock();
try {
    ...
} finally {
   lock.unlock();
}

redisson.shutdown();
2 голосов
/ 17 октября 2008

Я собирался посоветовать использовать memcached в качестве очень быстрого распределенного хранилища ОЗУ для хранения журналов; но похоже, что EHCache - похожий проект, но более ориентированный на Java.

Любой из них - путь, если вы уверены, что используете атомарные обновления (memcached поддерживает их, не знает об EHCache). Это, безусловно, самое масштабируемое решение.

В качестве связанной точки данных Google использует «Chubby», быстрое распределенное хранилище с блокировкой памяти в качестве корня нескольких систем, в том числе BigTable.

1 голос
/ 18 сентября 2008

Не уверен, что я понимаю весь контекст, но, похоже, у вас есть одна база данных, поддерживающая это? Почему бы не использовать блокировку базы данных: если создание клиента - это одна INSERT, то только этот оператор может служить блокировкой, поскольку база данных отклонит вторую INSERT, которая нарушит одно из ваших ограничений (например, тот факт, что имя клиента является например, уникальный).

Если операция «вставка клиента» не является атомарной и представляет собой пакет утверждений, то я бы ввел (или использовал) начальную INSERT, которая создает некоторую простую базовую запись, идентифицирующую вашего клиента (с необходимыми ограничениями UNIQUEness), а затем сделать все другие вставки / обновления в той же транзакции. Снова база данных позаботится о согласованности, и любые одновременные изменения приведут к сбою одного из них.

1 голос
/ 18 сентября 2008

Я проделал большую работу с Coherence, что позволило применить несколько подходов к реализации распределенной блокировки. Наивным подходом было запросить блокировку одного и того же логического объекта на всех участвующих узлах. С точки зрения согласованности это блокировало ключ на реплицируемом кэше. Этот подход не так хорошо масштабируется, поскольку сетевой трафик увеличивается линейно по мере добавления узлов. Более разумным способом было использование распределенного кэша, где каждый узел в кластере, естественно, отвечает за часть пространства ключей, поэтому блокировка ключа в таком кэше всегда включала связь максимум с одним узлом. Вы можете применить свой собственный подход, основанный на этой идее, или, что еще лучше, получить согласованность. Это действительно инструментарий масштабируемости вашей мечты.

Я бы добавил, что любой полуприличный многоузловой механизм блокировки на основе сети должен быть достаточно изощренным, чтобы действовать правильно в случае любого сбоя сети.

0 голосов
/ 23 августа 2016

Поскольку вы уже подключаетесь к базе данных, перед добавлением еще одного компонента инфраструктуры посмотрите JdbcSemaphore , его просто использовать:

JdbcSemaphore semaphore = new JdbcSemaphore(ds, semName, maxReservations);
boolean acq = semaphore.acquire(acquire, 1, TimeUnit.MINUTES);
if (acq) {
 // do stuff
 semaphore.release();
} else {
  throw new TimeoutException();
}

Он является частью spf4j библиотеки.

0 голосов
/ 26 августа 2011

Вы также можете рассмотреть Cacheonix для распределенных блокировок. В отличие от всего, что упомянуто здесь, Cacheonix поддерживает блокировки ReadWrite с эскалацией блокировки от чтения до записи при необходимости:

ReadWriteLock rwLock = Cacheonix.getInstance().getCluster().getReadWriteLock();
Lock lock = rwLock.getWriteLock();
try {
  ...
} finally {
  lock.unlock();
}

Полное раскрытие: я разработчик Cacheonix.

0 голосов
/ 08 февраля 2010

Мы разрабатываем инфраструктуру распределенной синхронизации с открытым исходным кодом, в настоящее время реализованы блокировки DistributedReentrantLock и DistributedReentrantReadWrite, но они все еще находятся на стадии тестирования и рефакторинга. В нашей архитектуре ключи блокировки делятся на сегменты, и каждый узел отвечает за определенное количество сегментов. Таким образом, для успешных запросов блокировки существует только один сетевой запрос. Мы также используем класс AbstractQueuedSynchronizer в качестве состояния локальной блокировки, поэтому все неудавшиеся запросы на блокировку обрабатываются локально, что значительно снижает трафик сети. Мы используем JGroups (http://jgroups.org) для групповой связи и Hessian для сериализации.

для деталей, пожалуйста, проверьте http://code.google.com/p/vitrit/.

Пожалуйста, пришлите мне ваш ценный отзыв.

Камран

...