Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Кластеризация согласно консенсусу

Оригинал: Clustering by Consensus
Автор: Dustin J. Mitchell
Дата публикации: July 12, 2016
Перевод: Н.Ромоданов
Дата перевода: январь 2017 г.

Creative Commons

Перевод был сделан в соответствие с лицензией Creative Commons. С русским вариантом лицензии можно ознакомиться здесь.

Дастин (Dustin) является разработчиком приложений с открытым исходным кодом и выпускающим инженером компании Mozilla. Он работал над разнообразными проектами, такими как система конфигурации хостов в Puppet, веб-фреймворк на базе Flask, модульные тесты для конфигураций брандмауэра и фреймворк непрерывной интеграции в Twisted Python. Дастина можно найти на GitHub по ссылке @djmitche или отправить ему письмо по адресу dustin@mozilla.com.

Введение

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

Мотивирующий пример

Внимание этой главы сосредоточено на реализации протоколов, но в качестве мотивирующего примера давайте рассмотрим простой сервис управления банковскими счетами. В таком сервисе у каждого счета есть текущий баланс и он идентифицируется номером счета. Пользователи получают доступ к счетов путем запроса таких операции, как "deposit" ("депозит"), "transfer" ("трансфер") или "get-balance" ("получить-баланс"). Операция "transfer" работает одновременно с двумя счетами — счетом-источником и счетом-получателем - и ее выполнение должно быть отклонено в случае, если баланс счета-источника является слишком маленьким.

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

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

По сути дела, такие сбои происходят, когда серверы для выполнения операций используют свои локальные состояния, не убедившись в том, что локальная машина состояний согласована с машинами состояний на других серверах. Например, представьте, что сервер А получает задание выполнить трансфер со счета 101 на счет 202, когда сервер В уже обработал другую операцию трансфера всего баланса банковского счета 101 на счет 202, но еще не сообщил об этом серверу А. Локальное состояние на сервере A отличается от состояния на сервере B, поэтому сервер А неправильно разрешит выполнить полный трансфер счета даже в том случае, если результатом по счету 101 будет овердрафт.

Распределенные машины состояний

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

Машина состояний для этого приложения проста:

  def execute_operation(state, operation):
        if operation.name == 'deposit':
            if not verify_signature(operation.deposit_signature):
                return state, False
            state.accounts[operation.destination_account] += operation.amount
            return state, True
        elif operation.name == 'transfer':
            if state.accounts[operation.source_account] < operation.amount:
                return state, False
            state.accounts[operation.source_account] -= operation.amount
            state.accounts[operation.destination_account] += operation.amount
            return state, True
        elif operation.name == 'get-balance':
            return state, state.accounts[operation.account]

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

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

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

Перейти к следующей части статьи.