Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
440-620.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
4.13 Mб
Скачать

1 Глава 7. Отказоустойчивость

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

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

Более правильным решением будет разрешить участнику р контакт с другим участником, q, чтобы он мог по текущему состоянию q решить, что же ему де­лать дальше. Так, например, пусть q перешел в состояние commit. Это возмож­но только в том случае, если координатор до поломки послал q сообщение global_СОММ1Т. Видимо, до р это сообщение не дошло. Соответственно, р те­перь может прийти к выводу о необходимости локального подтверждения. Точ­но так же если q находится в состоянии abort, р может уверенно прерывать свою часть транзакции.

Теперь предположим, что q пребывает в состоянии init. Такая ситуация мо­жет произойти, если координатор разошлет всем участникам сообщения vote_ request и это сообщение дойдет до р (который ответит на него посылкой со­общения vote_commit), но не дойдет до q Другими словами, координатор может отказать во время рассылки сообщения vote_request. В этом случае правильно будет прервать транзакцию: и р, и q могут осуществить переход в со­стояние abort.

Наиболее сложную ситуацию мы получим, если q также будет находиться в состоянии ready, ожидая ответа от координатора. Так, если окажется, что все участники находятся в состоянии ready, конкретное решение принять невоз­можно. Проблема состоит в том, что все участники готовы подтвердить транзак­цию, но для этого им необходим голос координатора. Соответственно, протокол блокируется до момента восстановления координатора.

Различные варианты иллюстрирует табл. 7.5.

Чтобы гарантировать, что процесс действительно восстановился, нужно что­бы он сохранял свое состояние при помощи средств длительного хранения дан­ных (как сохранять защищенным от отказов способом, мы поговорим позже в этой главе). Так, например, если участник находился в состоянии init, то после восстановления он может решить локально прервать транзакцию и сообщить об этом координатору. Точно так же если он уже принял решение и в момент по­ломки находился в состоянии COMMIT или ABORT, то при восстановлении он снова придет в выбранное состояние и повторно сообщит свое решение коорди­натору.

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

Координатор имеет только два критических состояния, за которыми надо сле­дить. Когда он начинает протокол 2PC, он должен сохранить сведения о том, что он находится в начальном состоянии WAIT, чтобы иметь возможность при необходимости после восстановления повторно разослать сообщение VOTE_ REQUEST всем участникам. Кроме того, если в ходе второй фазы он принял ре­шение, следует сохранить это решение, чтобы после восстановления координато­ра его также можно было разослать повторно.

Схема действий координатора иллюстрирует листинг 7.1. Координатор начи­нает с групповой рассылки всем участникам сообщения VOTE_REQUESTлля то­го, чтобы собрать их голоса. Он записывает это действие, после чего входит в со­стояние WAIT, в котором ожидает прихода голосов участников.

Листинг 7.1. Схема действий, предпринимаемых координатором согласно протоколу двухфазного подтверждения

ДЕЙСТВИЯ КООРДИНАТОРА: записать START_2PC в локальный журнал; разослать V0TE_ REQUEST всем участникам; пока собраны не все голоса { ожидать прихода голосов; если время вышло {

записать GL0BAL_AB0RT в локальный журнал; разослать GL0BAL_AB0RT всем участникам; выход;

}

записать голос;

}

если все участники прислали V0TE_C0MMIT и координатор голосует COMMIT {

записать GL0BAL_C0MMIT в локальный журнал;

разослать GL0BAL_C0MMIT всем участникам; } в противном случае {

записать GL0BAL_AB0RT в локальный журнал;

разослать GL0BAL_AB0RT всем участникам;

}

Если собраны не все голоса, а недостающие в течение заданного времени не присланы, координатор приходит к выводу, что один или более участников на­ходятся в нерабочем состоянии. Это означает, что он должен прервать транзак­цию и разослать остальным участникам сообщение GLOBALABORT.

Если не произошло никаких отказов, координатор в конечном итоге соберет все голоса. Если все участники, так же как и координатор, проголосовали за под­тверждение, сообщение GLOBALCOMMIT будет сначала записано в журнал, а по­том разослано всем процессам. В противном случае координатор разошлет сообще­ние GLOBAL_ABORT (предварительно также записав его в локальный журнал).

В листинге 7.2 приведены действия, осуществляемые участником. В начале процесс ожидает запроса на голосование от координатора. Отметим, что это ожидание выполняется в отдельном потоке выполнения, выполняющемся в ад­ресном пространстве процесса. Если сообщение не поступает, транзакция просто прерывается. По всей вероятности, причина отсутствия сообщения состоит в том, что координатор отказал.

Листинг 7.2. Схема действий, предпринимаемых процессом-участником протокола 2РС

ДЕЙСТВИЯ УЧАСТНИКА:

записать состояние INIT в локальный журнал; ожидать VOTE_REQUEST от координатора; если время вышло {

записать GL0BAL_AB0RT в локальный журнал;

выход;

}

если участник голосует за COMMIT {

записать V0TE_C0MMIT в локальный журнал; послать V0TE_C0MMIT координатору; ожидать решение от координатора; если время вышло {

разослать DECISION_REQUEST другим участникам;

ожидать прихода решения; /^процесс блокирован*/

записать решение в локальный журнал;

}

если решение == GL0BAL_C0MMIT

записать GL0BAL_C0MMIT в локальный журнал; иначе если решение == GL0BAL_AB0RT

записать GL0BAL_AB0RT в локальный журнал;

} если {

записать V0TE_AB0RT в локальный журнал; послать V0TE_AB0RT координатору;

}

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

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

Каждый участник должен быть готов к тому, что другой участник запросит у него глобальное решение. Для этого каждый из участников должен запустить отдельный поток выполнения параллельно с главным потоком выполнения уча­стника, как показано в листинге 7.3. Этот поток блокируется до получения за­проса о решении. Он может ответить другим процессам, только если ассоцииро­ванный с ним участник уже принял конечное решение. Другими словами, если в локальный журнал было записано сообщение GLOBAL JCOMMITили GLOBAL_ ABORT, можно быть уверенным, что координатор послал свое решение как ми­нимум этому процессу. Кроме того, как мы говорили ранее, что если ассоцииро­ванный с потоком участник находится в состоянии INIT, он может решить по­слать сообщение GLOBAL_ABORT. Во всех остальных случаях этот поток ничем не сможет помочь задавшему вопрос процессу, и пославший запрос участник ос­танется без ответа.

Листинг 7.3. Схема обработки входящих запросов о решении

ДЕЙСТВИЯ ПО ОБРАБОТКЕ ЗАПРОСА РЕШЕНИЯ: /^выполняются в отдельном потоке выполнения*/ пока истина {

ожидать любого сообщения DECISION_REQUEST; /^процесс блокирован*/ считать последнее записанное состояние из локального журнала если состояние GLOBAL-COMMIT

послать GL0BAL_C0MMIT запросившему решение участнику иначе если состояние == INIT или состояние == GL0BAL_AB0RT

послать GL0BALJ\B0RT запросившему решение участнику иначе

пропустить /^участник блокирован*/

}

Как можно заметить, возможна ситуация, когда участнику придется устано­вить блокировку до восстановления координатора. Такая ситуация может воз­никнуть, если все участники получат и обработают сообщение координатора VOTE_REQUEST и в ходе этой работы координатор сломается. В этом случае участники не смогут совместно решить, какое действие им предпринять. По этой причине протокол 2PC также называют протоколом блокирующего подтвержде­ния (blocking commit protocol).

Существует несколько способов избежать блокировки. Одно из них, описан­ное в [22], состоит в использовании примитива групповой рассылки, когда полу­чатель немедленно рассылает ответное сообщение всем остальным процессам. Можно показать, что подобный подход позволяет участнику принять итоговое решение даже в том случае, если координатор еще не восстановился. Другое ре­шение — использование протокола трехфазного подтверждения, который явля­ется последней темой этого раздела.

7.5.2. Трехфазное подтверждение

Проблема протокола двухфазного подтверждения состоит в том, что при поломке координатора участники могут оказаться не в состоянии прийти к итоговому ре­шению. В результате участникам приходится дожидаться восстановления коор­инатора, находясь в заблокированном состоянии. В [422] был предложен вари­ант протокола 2PC, названный протоколом трехфазного подтверждения {Three-phase Commit Protocol, ЗРС), который предотвращает блокировку процессов при появлении ошибок аварийной остановки. Хотя протокол ЗРС часто упоминается в литературе, на практике он используется редко, потому что редко возникают условия, в которых блокируется 2РС. Мы рассмотрим этот протокол, поскольку он представляет собой дальнейшее развитие решений проблем отказоустойчиво­сти в распределенных системах.

Как и 2РС, ЗРС формулируется в терминах координатора и набора участни­ков. Соответствующие им конечные автоматы показаны на рис. 7.14. Сущность протокола состоит в том, что состояния координатора и любого из участников удовлетворяют двум условиям.

  • Не существует такого состояния, из которого может быть осуществлен пря­мой переход как в состояние COMMIT, так и в состояние ABORT.

  • Не существует такого состояния, в котором невозможно принять итоговое решение, но возможен переход в состояние COMMIT.

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

Координатор ЗРС начинает с рассылки всем участникам сообщения VOTE_ REQUEST после чего ожидает прихода ответов. Если хотя бы один участник голосует за прерывание транзакции, это становится итоговым решением и коор­динатор рассылает участникам сообщение GLOBALABORT. Однако если тран­закция может быть подтверждена, рассылается сообщение PREPAREjCOMMIT. Только после того, как все участники подтвердят свою готовность к подтвержде­нию, координатор посылает итоговое сообщение GLOBAL COMMIT, в результа­те которого транзакция действительно подтверждается.

И снова существует лишь несколько ситуаций, в которых процесс блокирует­ся и ожидает сообщений. Во-первых, когда участник, находясь в состоянии INIT, ожидает прихода запроса на голосование, он может в итоге перейти в состояние

ABORT, что будет означать поломку координатора. Эта ситуация идентична си­туации для протокола 2РС. Также аналогично координатор может находиться в состоянии WAIT, ожидая голосов участников. По истечении заданного времени координатор решает, что участник отказал, и обрывает транзакцию путем рас­сылки сообщения GLOBALABORT.

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

Участник Р может блокироваться в состоянии READY или PRECOMMIT. По­сле окончания отведенного участнику Р на ожидание времени, Р в состоянии по­нять, что координатор отказал и нужно решать, что делать дальше. Как и в 2PC, если Р при контакте с другим участником обнаружит, что он находится в состоя­нии COMMIT (или ABORT), Р также переходит в это состояние. Кроме того, ес­ли все участники находятся в состоянии PRECOMMIT, транзакция может быть без проблем подтверждена.

Опять-таки, аналогично схеме 2РС, если другой участник Q по-прежнему на­ходится в состоянии INIT, транзакция может быть совершенно спокойно пре­рвана. Важно отметить, что Q может находиться в состоянии INIT только в том случае, если ни один из участников не находится в состоянии PRECOMMIT. Уча­стник может перейти в состояние PRECOMMIT, только если координатор перед сбоем сам перешел в состояние PRECOMMIT, а значит, получил голоса всех уча­стников. Другими словами, участник не может оставаться в состоянии INIT, ко­гда другой участник переходит в состояние PRECOMMIT.

Если каждый из участников, с которым может связаться Р, находится в со­стоянии READYвместе они образуют большинство), транзакция должна быть прервана. Здесь следует отметить, что если один из участников отказывает, поз­же он будет восстановлен. Однако ни Р, ни любой другой активный участник не знают, в каком состоянии будет находиться отказавший участник после восста­новления. Если процесс будет восстановлен в состоянии INIT, решение о преры­вании транзакции — единственно верное. В наихудшем случае процесс может восстановиться в состоянии PRECOMMIT, но и в этом случае он не сможет поме­шать прерыванию транзакции.

Этим ситуация сильно отличается от протокола 2РС, где отказавший участ­ник может восстановиться в состоянии COMMIT, тогда как все прочие участни­ки будут находиться в состоянии READY. В этом случае оставшиеся рабочие процессы не смогут принять итогового решения и вынуждены будут ожидать восстановления отказавшего процесса. В ЗРС, если какой-то из рабочих процес­сов находится в состоянии READY, отказавшие процессы не смогут восстано­виться ни в каком другом состоянии, кроме INIT, ABORT или PRECOMMIT. По этой причине оставшиеся невредимыми процессы всегда смогут прийти к итого­вому решению.

И, наконец, если процессы, такие как Р, могут достичь состояния PRECOMMIT (и они составляют большинство), то можно без проблем подтвердить транзак­цию. И снова мы можем видеть, что все остальные процессы либо находятся в состоянии READY, либо, как минимум, если они отказали, будут восстановлены в состоянии READY, PRECOMMIT или COMMIT

Дополнительные подробности по ЗРС можно найти в [47, 103].

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]