|
|
|
|
# Подсказки к упражениям
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Двухфазный коммит протокол на основе ZooKeeper
|
|
|
|
|
На основе материалов: https://zookeeper.apache.org/doc/r3.4.2/recipes.html#sc_recipes_twoPhasedCommit и https://stackoverflow.com/questions/24635777/how-to-implement-2pc-in-zookeeper-cluster
|
|
|
|
|
|
|
|
|
|
Протокол двухфазной коммита — это алгоритм, позволяющий всем клиентам в распределенной системе договориться либо о коммите транзакции, либо о ее откате.
|
|
|
|
|
|
|
|
|
|
Взаимодействующие компоненты:
|
|
|
|
|
- создатель транзакции,
|
|
|
|
|
- координатор транзакции,
|
|
|
|
|
- исполнители транзакции,
|
|
|
|
|
- zookeeper,
|
|
|
|
|
- (опционально) прямой канал связи создателя транзакции и координатора,
|
|
|
|
|
- (опционально) прямые каналы связи между координатором и исполнителями.
|
|
|
|
|
|
|
|
|
|
### Вариант 1.
|
|
|
|
|
|
|
|
|
|
В ZooKeeper вы можете реализовать двухфазный коммит протокол, если координатор создаст узел транзакции, скажем "/app/Tx", и один дочерний узел для каждого исполнителя транзакции, скажем "/app/Tx/node_i". Когда *координатор* создает дочерний узел, он оставляет его содержимое неопределенным.
|
|
|
|
|
|
|
|
|
|
Первая фаза:
|
|
|
|
|
|
|
|
|
|
Как только каждый исполнитель, участвующий в транзакции, получает транзакцию от координатора, исполнитель читает каждый дочерний узел "node_i" и подписывается на события изменения транзакционного узла. Затем каждый исполнитель обрабатывает запрос и принимает решение "commit" или "abort", записывая данные в свой узел.
|
|
|
|
|
|
|
|
|
|
Вторая фаза:
|
|
|
|
|
|
|
|
|
|
Как только запись завершается, другие исполнители получают уведомление, и как только все исполнители получат все голоса, они могут принять решение либо "commit", либо "abort". Обратите внимание, что узел может принять решение абортировать транзакцию раньше, если какой-либо исполнитель проголосует за "abort".
|
|
|
|
|
|
|
|
|
|
Интересным аспектом этой реализации является то, что единственная роль координатора заключается в определении группы исполнителей, создании узлов ZooKeeper и распространении транзакции на соответствующим исполнителям. Фактически, даже распространение транзакции может быть сделано через ZooKeeper путем записи в узле транзакции.
|
|
|
|
|
|
|
|
|
|
У описанного выше подхода есть два важных недостатка:
|
|
|
|
|
— это сложность сообщений, которая составляет O(n²).
|
|
|
|
|
— невозможность обнаружения аварийного завершения исполнителей.
|
|
|
|
|
|
|
|
|
|
Для решения первой проблемы можно сделать так, чтобы об изменениях в узлах транзакций уведомлялся только координатор, а затем уведомлять исполнителей, как только координатор примет решение. Обратите внимание, что этот подход остаётся масштабируемым, но он медленнее, поскольку требует, чтобы все коммуникации проходили через координатора.
|
|
|
|
|
|
|
|
|
|
Для решения второй проблемы можно сделать так, чтобы координатор распространял транзакцию исполнителям, а каждый исполнитель создавал свой собственный эфемерный узел.
|
|
|
|
|
|
|
|
|
|
### Вариант 2.
|
|
|
|
|
|
|
|
|
|
1. Координатор C регистрирует транзакционный узел /app/tx
|
|
|
|
|
|
|
|
|
|
Первая фаза:
|
|
|
|
|
|
|
|
|
|
2. Координатор уведомляет исполнителей о транзакции
|
|
|
|
|
3. Координатор подписывается на изменения транзакционного узла (устанавливает WATCH на /app/tx)
|
|
|
|
|
4. Каждый исполнитель создает эфемерных узел /app/tx/node_i с решением commit/abort
|
|
|
|
|
5. Исполнитель подписывается на события своего узла для получения решения от координатора ( вторая фаза )
|
|
|
|
|
|
|
|
|
|
Вторая фаза:
|
|
|
|
|
|
|
|
|
|
6. Координатор принимает решение о commit/abort после ожидания таймаута или после создания всех узлов исполнителей с решением commit
|
|
|
|
|
7. Координатор изменяет значение эфемерных узлов для каждого исполнителя на commit / abort
|
|
|
|
|
8. Исполнители применяют / прерывают транзакцию
|
|
|
|
|
9. Исполнители обновляют значение узла на committed
|