Подтвердить что ты не робот

Распределенное управление Concurrency

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

В качестве примера я отправлю пример 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();
        }
    }
}

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

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

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

Ответ 9

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

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

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

Ответ 10

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

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

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

Ответ 11

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

Ответ 12

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

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

Ответ 13

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

Подробнее см. http://code.google.com/p/vitrit/.

Присылайте мне свои ценные отзывы.

Камран