В идеале RPC должен функционировать правильно и в случае отказов. Рассмотрим такие классы отказов:

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

Утерян запрос от клиента до серверу. Самое простое решение - через определенное время повторить запрос.

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

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

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

Сразу сообщить приложению об ошибке. Этот подход гарантирует, что RPC был выполненный не более однажды.

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

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

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

Как поступать с сиротами? Рассмотрим 4 возможных решения.

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

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

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

Истечение термина. Каждому запросу отводится стандартный отрезок времени Т, на протяжении  которого он должен быть выполнен. Если запрос не выполняется за отведенное время, то выделяется дополнительный квант. Хотя это и нуждается в дополнительной работе, но если после аварии клиента сервер ждет на протяжении интервала Т к перезагрузки клиента, то все сироты обязательно уничтожаются.

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

Синхронизация в распределенных системах

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

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

 

Возможно стоит прочитать: