Управление Распределенным Параллелизмом

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

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

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

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

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

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

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

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

13 ответов


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

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

Hazelcast-Распределенная Очередь, Карта, Набор, Список, Блокировка


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

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

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


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

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

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

в качестве примера я опубликую пример ReentrantReadWriteLock (обратите внимание, что нет" терракотовой " версии блокировки - вы просто используете обычный 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();
        }
    }
}

Я рекомендую использовать Рэдиссон. Он реализует более 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();

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

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

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


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

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


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

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


Я сделал простой сервис RMI с двумя методами: lock и release. оба метода берут ключ (моя модель данных использовала UUIDs как pk, так что это также был ключ блокировки).

RMI является хорошим решением для этого, потому что он централизован. вы не можете сделать это с EJBs (особенно в кластере, поскольку вы не знаете, на какой машине ваш вызов приземлится). к тому же, это просто.

Это сработало для меня.


Если вы можете настроить балансировку нагрузки так, чтобы запросы для одного клиента всегда сопоставлялись с одним и тем же сервером, вы можете справиться с этим через локальную синхронизацию. Например, возьмите ID клиента mod 10, чтобы найти, какой из 10 узлов использовать.

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

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


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

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

полное раскрытие информации: я разработчик Cacheonix.


поскольку вы уже подключаетесь к базе данных, перед добавлением другой части infra, взгляните на 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 библиотека.


в тот же день мы использовали бы определенный "сервер блокировки" в сети для обработки этого. Бле.

ваш сервер баз данных может иметь ресурсы специально для такого рода вещей. MS-SQL Server имеет блокировки приложений, используемые через процедура sp_getapplock/процедура sp_releaseapplock процедур.


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

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

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

Камран