Когда два или более программных модуля обращаются к одному и тому же ресурсу, они должны быть синхронизированы. Это означает, что только один модуль одновременно должен работать с ресурсом. Без такой синхронизации будут столкновения и конфликты. Это особенно верно, когда мы говорим о «ресурсах», которые не поддерживают атомарные транзакции.
Чтобы решить эту проблему и предотвратить конфликты, мы должны ввести еще один элемент в картину. Все программные модули, перед доступом к ресурсу, должны захватить блокировку от централизованного сервера. Когда манипуляции с ресурсом завершены, модуль должен снять блокировку. Пока блокировка захватывается одним модулем, другие модули не смогут ее захватить. Подход очень прост и хорошо известен. Однако я не нашел ни одного облачного сервиса, который бы предоставлял такой сервис блокировки и разблокировки через API RESTful. Поэтому я решил создать один — stateful.co .
Вот практический пример. У меня есть веб-приложение на Java, которое размещено на Heroku. Есть три сервера (также известные как dynos), на которых запущено одно и то же .war
приложение. Почему три? Потому что веб-трафик довольно активный, а один сервер недостаточно мощный. Так что у меня должно быть три из них. Все они запускают одинаковые приложения.
Каждое веб-приложение работает с таблицей в Amazon DynamoDB. Он обновляет таблицу, помещает в нее новые элементы, иногда удаляет некоторые элементы и выбирает их. Пока все хорошо, но конфликты неизбежны. Вот пример типичного сценария взаимодействия между веб-приложением и DynamoDB (я использую jcabi-динамо):
Table table = region.table("posts"); Item item = table.frame() .where("name", "Jeff") .iterator().next(); String salary = item.get("salary"); item.put("salary", this.recalculate(salary));
Логика здесь очевидна. Сначала я извлекаю элемент из таблицы posts
, затем читаю его salary
, а затем изменяю в соответствии с моим алгоритмом пересчета. Проблема в том, что другой модуль может начать делать то же самое, пока я пересчитываю. Он прочитает то же самое начальное значение из таблицы и начнет точно такой же пересчет. Тогда это сохранит новое значение, и я сохраню его тоже. В конечном итоге зарплата Джеффа будет изменена только один раз, в то время как пользователи будут ожидать двойного изменения, поскольку две из них инициировали две транзакции с двумя разными веб-приложениями.
Правильный подход заключается в том, чтобы сначала заблокировать таблицу DynamoDB даже до считывания зарплаты. Затем внесите изменения и, в конце концов, разблокируйте его. Вот как мне помогает stateful.co . Все, что мне нужно сделать, это создать новую именованную блокировку в веб-панели stateful.co , получить ключи аутентификации и изменить мой код Java:
Sttc sttc = new RtSttc( new URN("urn:github:526301"), // my Github ID "9FF3-4320-73FB-EEAC" // my secret key! ); Locks locks = sttc.locks(); Lock lock = locks.get("posts-table-lock"); Table table = region.table("posts"); Item item = table.frame() .where("name", "Jeff") .iterator().next(); new Atomic(lock).call( new Callable<Void>() { @Override public void call() { String salary = item.get("salary"); item.put("salary", this.recalculate(salary)); return null; } } );
Как видите, я заключаю ту критическую транзакцию Callable
, которая будет выполняться изолированно. Этот подход, очевидно, не гарантирует атомарность транзакции — если часть транзакции завершится неудачно, автоматического отката не будет, и таблица DynamoDB останется в «поврежденном» состоянии.
Блокировки от stateful.co гарантируют изоляцию в использовании ресурсов, и вы можете использовать любые типы ресурсов, включая таблицы NoSQL, файлы, объекты S3, встроенное программное обеспечение и т. Д.
Я не должен забывать добавить эту зависимость к моей pom.xml
:
<dependency> <groupId>co.stateful</groupId> <artifactId>java-sdk</artifactId> </dependency>
Конечно, вы можете сделать то же самое; услуга абсолютно бесплатна. И вы можете использовать любые другие языки, не только Java. Кстати, если вы заинтересованы, внесите свой собственный SDK на предпочитаемом языке Я добавлю это в коллекцию Github .