Статьи

Шаблоны содержимого сервисных ошибок (часть I)

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


Введение

Имя более раннего двоюродного брата SOAP, XML-RPC, прекрасно отражает двойственную природу самого SOAP: в некотором смысле, эта технология веб-сервисов ориентирована на обмен XML и документами (посредством буквальное связывание), а в другом смысле это механизм удаленного программирования (через связывание RPC). Нигде эта двойственность не проявляется так явно, как при моделировании ошибок. Являются ли ошибки просто разновидностью данных, которые должны переноситься в наших ответах, подобных документам, или они являются частью процесса процедурного контроля? Глядя только на один стандарт SOAP, мы, кажется, имеем простой ответ: ошибки должны быть выражены как ошибки SOAP. Но взгляд на историю программирования показывает более сложную ситуацию с различными компромиссами, как мы рассмотрим в этой статье.

XML-RPC и SOAP вступили в эру очень масштабного программирования — 1990-х годов. Этот период принес нам операционные системы, превышающие 10 миллионов строк кода, стеки вызовов программирования, вложенные в сотню или более строк, и другие технологические чудеса. Чтобы справиться с сопутствующей сложностью, индустрия предпочитала такие языки, как C ++, Java и C #. Во всех этих языках исключение — скажем так — возникло само по себе, как решение, позволяющее развернуть безумно глубокие следы вызовов и помочь навести порядок и предсказуемость при обработке ошибок.

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

Расслоение программного обеспечения, которое мы видим в больших системах, выявило еще одну проблему, которую мы называем проблемой «внешней ошибки». На любой конкретной границе ошибки можно ожидать, что вызывающие абоненты будут кодировать структуры ошибок программного обеспечения, которое они вызывали. Но чем дальше удалялись слои, тем меньше вероятность того, что программное обеспечение более высокого уровня узнает точную природу ошибки. В многоуровневых системах уровни вызова более высокого уровня каким-то образом были обязаны передавать, преобразовывать или понимать структуры ошибок, о которых они не знали. Вы можете попытаться стандартизировать типизацию или структуру, но поскольку поставщики по всему миру предоставляют программное обеспечение для программ, надежды не было. Нам нужно что-то встроенное в язык. Введите Исключение как тип языка, служащий для моделирования ошибок посредством четко определенной иерархии типов.Будучи гарантированным базовым типом Exception, вы были уверены, что сможете закодировать в некоторый базовый класс ошибок «наименьший общий знаменатель».

Язык программирования C ++ стал первым по-настоящему массовым отраслевым языком, предлагающим функции исключений в шаблоне броска-броска и типизации исключений, хотя корни исключений можно проследить в Паскале, CLU и оригинальных академических концепциях [REF-1]. Подход в C ++ представил качественный скачок в управляемости ошибок в крупномасштабных системах. Java и C # последовали примеру C ++ в дизайне обработки ошибок. Java включала одно слегка противоречивое уточнение (впервые введенное в CLU) посредством использования проверенных исключений, что отличало «ожидаемые» или зарегистрированные исключения от «неожиданных» исключений. Основная идея заключалась в том, чтобы методы объявляли свои ожидаемые исключения с помощью метода throws для метода, помогая вызывающим объектам.Это было подвергнуто критике [REF-2] как по существу повторное введение связи штампов, требуя повторения предложений throws по сигнатурам всего графа зависимостей метода программы. Броски копируют себя как цепное письмо. Однако само ожидаемое / неожиданное различие имеет некоторые полезные аспекты, которые мы позже рассмотрим в контексте веб-сервисов.

Примерно в то же время, что и массовая революция исключений в C ++ и Java, поставщики программных платформ и другие стороны пытались создать лучший RPC / CORBA с дружественным для веб-контента контентом, XML и сетевыми транспортными сетями, такими как HTTP. За исключением исключительной популярности, игнорирование их в стандартах Web-сервисов было бы немыслимо, особенно для поставщиков, предлагающих эти функции на видном месте в своей среде [REF-3]. Итак, SOAP был утвержден W3C с ошибкой SOAP как единственным стандартным механизмом распространения ошибок, и были тщательно разработаны сопоставления от ошибок SOAP с соответствующими моделями программирования, как в стандарте JAX-WS [REF-4]. Ошибки SOAP стали прямым аналогом исключений программирования, а «ошибка» WSDLобъявление операций WSDL стало эквивалентом выражения «throws» метода Java — «проверенных» исключений.

После этого исторического путешествия по обработке ошибок в мире программирования и WSDL-SOAP давайте рассмотрим некоторые практические вопросы.

Ограничения модели исключений

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

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

В веб-сервисах сбои представляют собой только один класс различных состояний фатальной ошибки, что еще больше ухудшает их практическое использование. Согласно профилю совместимости SOAP / WSDL WSI-BP, R1126 [REF-5], «INSTANCE ДОЛЖЕН возвращать код состояния HTTP« 500 Internal Server Error », если конверт ответа является Fault». Но многие реальные серверы возвращают 500 ошибок во всех ужасных ситуациях, таких как ошибки нехватки памяти, глючный код или неправильно инициализированные системы. Из-за этого при практическом использовании многие клиенты очень поверхностно обрабатывают неисправности. В некоторых случаях они просто объединяют все ошибки HTTP 500, давая конечным пользователям очень смутное сообщение о проблеме.

Модель данных для исключений дополнительно игнорирует возврат нескольких ошибок в виде одного события. Для исключений из нескольких ошибок нет языковых функций или общих шаблонов программирования. Исключения обычно содержат и локализуют одну строку сообщения с читабельным текстом. Нет стандартного составного исключения, которое может содержать подчиненные исключения. По сути, исключения являются «особенностями», как в грамматическом, так и в математическом смысле! Аналогично, моделирование сбоев SOAP и WSDL и отображения программирования, такие как JAX-WS, действительно определяют одну полезную нагрузку (например, один компонент сбоев JAX-WS) на ошибку. Вы можете создать полезную нагрузку, которая содержит несколько ошибок, но это не является частью стандарта; это становится специфичным для сервиса моделированием данных.

Второе и, возможно, более существенное ограничение подхода «ошибка / исключение»: любые данные ответа, которые вы создали в ходе обработки результатов, будут потеряны. По определению, если выдается исключение, вы не увидите никакого значения результата из вызванного вами метода. Это становится реальной проблемой, если у вас действительно есть смесь результатов и ошибок — например, если ваши ошибки действительно больше похожи на предупреждения.

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

В-третьих, один только контекст использования может влиять на то, является ли данная проблема предупреждением или ошибкой. Скажем, у меня есть операция для перечисления предмета для продажи и еще одна операция для повторного перечисления ранее перечисленного предмета, который не продавался. Если идентификатор элемента для операции повторного списка не найден, это, вероятно, ошибка — фатальная (рисунок 1). Но в групповой операции, которая может повторно перечислять 10 элементов одновременно, это может быть только предупреждением, если один из них не найден (рисунок 2). Мы должны иметь возможность представлять данные об ошибках, используя аналогичную структуру для ошибок и предупреждений, а затем просто принимать решение о серьезности на основе бизнес-прецедента.

 Рисунок 1: Один запрос с ошибкой.
 Рисунок 2: Несколько элементов запроса, сочетание ошибки и успеха.

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

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

 Рисунок 3: клиент получает неизвестный тип с более нового сервера.

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

Признавая это, стандарты веб-сервисов предоставили такую ​​ошибку «наименьшего общего знаменателя». Для общей ошибки SOAP задан код ошибки (широкая категория ошибок SOAP, такая как проблема клиента, проблема с сервером и т. Д.), Строка ошибок (читаемое сообщение) и подробные сведения (информация о пользовательских типах для вашей ошибки).

Проблема эволюции ошибок обеспечивается расширенными стандартами веб-служб, такими как базовый профиль организации взаимодействия веб-служб (WSI-BP). Правило R2742 мудро советует: «ОБОЛОЧКА МОЖЕТ содержать ошибку [a] с элементом detail, который не описывается элементом soapbind: fault в соответствующем описании WSDL». Это учитывает немоделированные «чужие» неисправности, например, возникающие из более новой версии службы для более старого клиента. Старые клиенты, по крайней мере, будут иметь видимость общего объекта ошибки, хотя они не смогут интерпретировать ваши конкретные типы подробностей ошибок.

Правило R2742 полезно для того, чтобы клиентское программное обеспечение не отвечало его ожиданиям в случае появления неисправностей. Но если рассмотреть его более подробно, это имеет очень интересные последствия для функциональной совместимости: веб-сервисы могут выдавать ошибки, которые не объявлены в WSDL! Стандарт WSI-BP разъясняет, что R2742 предназначен только для решения проблемы внешних ошибок, и предупреждает нас: «Описание веб-службы должно включать все ошибки, известные на момент определения службы». Тем не менее, нормативным словом является «должен», и многие известные отраслевые веб-сервисы, такие как eBay, Amazon и Microsoft MapPoint, не объявляют о каких-либо неисправностях в своих определениях WSDL.

С точки зрения языка программирования, который мы установили ранее в этой статье, необъявленные ошибки представляют собой своего рода «непроверенное исключение», подобное тем, которые существуют в Java. Действительно, исключения Java во время выполнения старательно преобразуются в общие ошибки SOAP с помощью сред выполнения SOAP, таких как эталонная реализация JAX-WS.

Модели моделирования для ошибок обслуживания

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

Чисто основанное на сбое моделирование: мы можем добросовестно придерживаться профиля WSI-BP и моделировать все «известные» ошибки сервиса как объявленные ошибки в WSDL службы (стандартное использование ошибок)
Чисто основанное на ответах моделирование ошибок: мы можем избежать объявлений сбоев WSDL и моделировать наши сервисные ошибки в данных ответа («Резидентные ошибки ответа»).
Кроме того, мы рассмотрим некоторые попытки смешать два подхода:
Объединенные структуры ответа / ошибки: невероятный многоцелевой контейнер результатов!
Смешанное использование, при котором ответы содержат ошибки в специально заданных ситуациях, а ошибки обрабатывают оставшиеся случаи.

Ошибки, объявленные

WSDL Ошибки WSDL объявляются как сообщения SOAP, так же, как входные и выходные данные для операции. Типичным шаблоном для любого сообщения SOAP является объявление элемента верхнего уровня в качестве сообщения, а затем добавление этого элемента в объявление wsdl: message и добавление wsdl: сообщения в объявление операции, как в следующем примере:

<xs:element name="ItemNotFound">
<xs:complexType>
<!-- type information… -->
</xs:complexType>
</xs:element>

<wsdl:message name="ItemNotFound">
<wsdl:part name="ItemNotFound" element="tns:ItemNotFound"/>
</wsdl:message>

...
<wsdl:operation name="FindItem">
<wsdl:input ... />
<wsdl:output ... />
<wsdl:fault message="tns:ItemNotFound"/>
</wsdl:operation>

(Более полная проработка этого примера приведена в конце этой статьи.)

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

<xs:complexType name="BaseServiceFaultType">
<xs:sequence>
<xs:element name="Error" type="tns:ErrorType"/>
</xs:sequence>
</xs:complexType>

Базовый класс должен иметь информацию об ошибках в стандартизированном формате, как показано ниже; мы посетим это более подробно позже.

Основное преимущество отказов WSDL заключается в их статусе «первого класса» в виде выделенных типов сообщений SOAP. Для веб-служб, которые подчеркивают совместимость с инструментами, сетевой инфраструктурой и стандартизированными клиентами, преимущество может быть неоспоримым: взаимодействующее программное обеспечение поймет, что ошибки обнаруживаются, когда они видят ошибки. Промежуточные системы, такие как служебные сервисные шины, могут регистрировать и сообщать об ошибках, и стандартные средства тестирования будут знать, что ошибка была возвращена.

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

Другое преимущество использования ошибок в моделировании ошибок заключается в том, что ошибки могут быть специально объявлены для каждой операции. Например, наша операция «findItem» выше объявляет «ItemNotFound», который специфичен для этой операции. Однако становится громоздким объявлять большое количество различных сообщений об ошибках, и обычно более целесообразно использовать это для объявления типов сбоев (с конкретными структурами сообщений), а не случаев сбоев. Сложные операции могут вызвать десятки конкретных ошибок, связанных с различными проблемами валидации или обработки, и WSDL не является эффективным способом для каталогизации всего этого — по крайней мере, без использования отдельных объявлений wsdl: fault. Это фактически вызывает одно из ограничений WSDL как языка моделирования Web-сервисов:• Обеспечить встроенный способ детализировать поведенческие аспекты веб-сервисов, которые включают в себя проверку на бизнес-уровне, конкретные случаи ошибок и алгоритмическое поведение.

Ограничения использования сбоев на основе WSDL следующие:
1. Решение неполное, из-за проблемы эволюции ошибки.
2. Самый низкий общий знаменатель, встроенный в SOAP, действительно очень минимален. Вам не гарантировано намного больше, чем одна строка, идентифицирующая ошибку.
3. Многочисленные ошибки не учитываются явным образом с помощью этого подхода.
4. Смешения ошибок и нормального вывода ответа невозможны.

Заключение

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

Список литературы

[REF-1] Джон Б. Гудэнаф, Структурная обработка исключений, Материалы 2-го симпозиума ACM SIGACT-SIGPLAN по принципам языков программирования, с.204-224, 20-22 января 1975 г., Пало-Альто, Калифорния.
[REF-2] http://www.mindview.net/Etc/Discussions/CheckedExceptions
[REF-3] http://msdn.microsoft.com/en-us/library/aa480514.aspx
[REF-4] В настоящее время в версии 2.0, JSR 224, http://jcp.org/en/jsr/detail?id=224
[REF-5] http://www.ws-i.org/Profiles/BasicProfile-1.1-2006-04 -10.html

Эта статья была первоначально опубликована в журнале The SOA Magazine ( www.soamag.com ), официально связанном с серией сервис-ориентированных вычислений Prentice Hall от Томаса Эрла ( www.soabooks.com ). Copyright © SOA Systems Inc. ( www.soasystems.com )