пятница, декабря 29, 2006

Как управлять программистами

Замечательный пост в ITBlogs от Анатолия Тенцера - "Урри, где у него кнопка?" о том, как управлять программистами.
Пролностью согласен с автором, и хотел бы добавить, что проблемма управления коллективом программистов особо остро стоит перед менеджерами, которые сами недавно были программистами. В эту же категорию можно смело относить и архитекторов с проектировщиками. Стремление управлять всем и вся, мелочные разборки по поводу "неправильной" реализации очередного класса - характерные признаки стиля руководства юного тим лида.
Читайте Толика Тенцера и учитесь делегировать полномочия :) И удачи вам в Новом Году!

четверг, декабря 28, 2006

Имперсонация и делегирование в ASP.NET

(ASP.NET delegation and impersonation)

Эта тема будет интересна тем, кто разрабатывает многоуровневые корпоративные .Net приложения. Вот такая узкая и скучная тема.

SSO - Single Sign On

Итак, те самые парни, о которых я сказал выше, всегда озабочены тем, чтобы пользователям не приходилось постоянно вводить логин / пароль при входе в различные приложения. Потому что пользователи грамотные, и они хотят чтобы «один раз ввели пароль при входе в windows, и он потом везде использовался». И я их в этом целиком и полностью поддерживаю. Когда у нас есть такая замечательная вещь, как Windows Integrated Authentication, мы несомненно должны ее использовать. Но это еще не все. В ASP.NET существуют такие мощные механизмы, как имперсонация (impersonation) и делегирование (delegation), о которых я хочу поговорить. Но прежде, давайте рассмотрим, каким образом .net web приложения исполняются под IIS.

Процессная модель IIS

Как известно на одном сервере IIS могут быть развернуто несколько web сайтов (только не под XP и IIS5), а на каждом сайте может выполняться несколько web приложений. Причем это могут быть разные приложения: .Net, ISAPI или скриптовые. Мы будем говорить только о .Net приложениях. Для .Net приложения нужен хост. И поэтому IIS запускает специальный серверный процесс, который служит хостом для CLR. В случае IIS 5 этот процесс называется aspnet_wp.exe, а IIS6 использует процесс w3wp.exe. В этом рабочем процессе и создаются домены приложений (AppDomain) для .Net web приложений. Каждое web приложение исполняется в своем AppDomain. Когда приложение завершается соответствующий AppDomain выгружается. Рабочий процесс IIS исполняется под определенной учетной записью. Для IIS5 это может быть локальная учетная запись ASPNET или SYSTEM. Настраивается это в файле machine.config в секции (непосредственно в секции есть обширный комментарий на эту тему). Кроме этих зарезервированных значений в machine.config можно указать любую другую локальную или доменную учетную запись для рабочего процесса IIS. В IIS6 настройка более гибкая. Производится она в MMC консоли IIS. Там можно создать несколько Application Pools, и для каждого указать свои настройки, в том числе и учетную запись. После этого каждому web приложению можно указать, в каком пуле оно должно выполняться. По умолчанию все приложения выполняются в DefaultAppPool под учетной записью Network Service. Физически для каждого Application Pool запускается свой рабочий процесс w3wp.exe.
То, под какой учетной записью выполняется рабочий процесс, оказывает очень большое влияние на поведение web приложения. Существует масса ресурсов, при доступе к которым осуществляется проверка полномочий учетной записи. К таким ресурсам относятся объекты файловой системы NTFS (файлы и каталоги), реестр, EventLog, базы данных и многое другое. Вот это, пожалуй тот минимум, который нам надо знать о рабочих процессах и их учетных записях для того, чтобы мы могли продолжать наш разговор. А вообще это тема необъятная, взять, к примеру, вопросы, связанные с масштабированием приложений под IIS. Интересующихся, я отсылаю к MSDN.

Имперсонация

При разработке web приложений встречаются разные ситуации. Иногда нам надо «абстрагироваться» от того, кто инициировал вызов данного кода web приложения, и тогда нам надо использовать учетную запись рабочего процесса. Но встречаются противоположенные ситуации, когда нам необходимо знать, какой пользователь инициировал выполнение серверного кода, и на основании этого принимать решение, имеет ли он право доступа к тому или иному ресурсу, либо вообще, может ли он исполнять данный код. Вот такую задачу и решает встроенный в ASP.NET механизм имперсонации (impersonation). В русском переводе его иногда называют «олицетворением», но те кто давно читают MSDN Russian Edition, знают, что данный термин как то не прижился в сообществе разработчиков.
Всем, наверное, известно, что для обработки запроса пользователя IIS выделяет отдельный поток из пула потоков, и в дальнейшем весь серверный код выполняется в этом потоке. Механизм имперсонации ASP.NET позволяет исполнять код на сервере «от имени» клиента. По умолчанию имперсонация выключена. Для того, чтобы включить механизм имперсонации для web приложения необходимо поместить в секцию его web.config следующий тэг:


<identity impersonate="true" />


Кроме того, вам необходимо в свойствах web каталога в консоли IIS включить режим “Integrated Windows Authentication” (вкладка “Directory security”).
Теперь в коде aspx страницы мы можем узнать текущего пользователя вот так:


Context.User.Identity.Name


Либо вот так, результат будет аналогичный:


Thread.CurrentPrincipal.Identity.Name


Теперь мы, к примеру, можем узнать, состоит ли данный пользователь в той или иной группе:


System.Threading.Thread.CurrentPrincipal.IsInRole("Power Users");


Кроме того, все обращения к ресурсам, которые требуют авторизации (NTFS, реестр, Event Log и т.д.) будут производиться от имени пользователя пославшего web запрос.
Вот, пожалуй, и все об имперсонации в ASP.NET, более подробные сведения можно почерпнуть в MSDN. Однако у этой темы есть интересное продолжение - это механизм делегирования.

Делегирование

Рассмотрим, к примеру, систему, в которой различные уровни (tiers) разнесены по разным физическим серверам (см. рисунок).


Имеется Front-end server на котором размещены aspx страницы с UI и имеется Back-end server с бизнес логикой. Front-end сервер общается с Back-end сервером посредством вызова его web сервисов. То есть, клиент заходит на aspx страничку, которая расположена на Front-end сервере. В коде aspx странички есть вызов web сервиса, который расположен на Back-end сервере. Если на Front-end сервере у нас включен режим имперсонации, то серверный код aspx страницы выполняется под учетной записью клиента. Было бы здорово, если бы учетные данные клиента передались и на Back-end сервер с вызовом web сервиса, и в результате там тоже сработала имперсонация. И такая возможность у нас есть! Реализуется она при помощи механизма делегирования.
Делегирование в ASP.NET базируется на использовании аутентификации по протоколу Kerberos, который позволяет аутентифицировать не только клиента но и сервер, к которому обращается клиент. Вообще, Kerberos достаточно сложная штука. Однако нам не надо досконально разбираться в деталях этого протокола, чтобы воспользоваться механизмом делегирования. Достаточно знать основные условия, необходимые для работы этого механизма.
Первое и главное, в сети должен использоваться протокол Kerberos. Это автоматически означает, что версия операционной системы должна быть не ниже Windows 2000 и в доменах должна быть установлена Active Directory.
Второе, браузер должен поддерживать протокол Kerberos. Это IE 5.0 и старше (в настройках необходимо включить опцию «Enable Integrated Windows Authentication»). Firefox тоже поддерживает Kerberos.
Далее, настройки сервера и web приложения для делегирования (Front-end server). Для сервера должна быть включена опция «Trust computer for delegation» (настраивается через оснастку MMC «Active Directory Users and Computers»). А для учетных записей пользователей в AD должен быть сброшен флаг «Account is sensitive and cannot be delegated» (настраивается там же).
В самом web приложении должны быть следующие настройки в файле web.config:



<identity impersonate="true" />
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>


Это означает, что должна использоваться Windows аутентификация, имперсонация должна быть включена, и анонимный доступ запрещен. Кроме того, следует в свойствах web каталога приложения на вкладке “Directory Security” следует включить флаг “Integrated Windows authentication”.
Теперь если вы разместите на aspx странице вот вакой код вызова web сервиса:


EchoService echo = new EchoService();
echo.Credentials = System.Net.CredentialCache.DefaultCredentials;
try
{
lbAnswer.Text = echo.Ask(TextBox1.Text);
}
catch(Exception ex)
{
lbAnswer.Text = ex.ToString();
}


Вы задействуете механизм делегации. CredentialCache.DefaultCredentials вернет удостоверения (credentials) клиента вызвавшего aspx страницу и эти удостоверения будут переданы при вызове метода Ask() web сервиса.
Здесь я изложил в самой краткой форме суть понятий имперсонации и делегирования в ASP.NET, а также то, как они могут быть использованы и как настроить environment для их использования. Однако тема далеко не исчерпана. Существуют различные сценарии использования делегирования. Существуют сценарии, когда делегирования надо избежать. Существует масса нюансов при настройке серверов и приложений для делегирования.
Для тех, кто обладает пытливым умом, либо просто заинтересовался данным вопросом, пара ссылок по теме:
How to configure an ASP.NET application for a delegation scenario
Troubleshooting Kerberos Delegation

среда, декабря 27, 2006

Скандалы года

CNews опубликовала забавный рейтинг скандалов года в росийском IT-рынке. Живая иллюстрация проблем и тенденций нашего рынка.
На первое место, я бы поставил - "Инфотехнологии портят кровь поставщикам алкоголя". История с ЕГАИС (или как там ее) - как зеркало автоматизации по российски, когда за дело берутся государственные структуры.
"Запрет иностранного ПО: открытое письмо Путину" - это вообще кандидат на Дарвиновскую премию. Уважаемые депутаты видимо не подозревают насколько неприглядно выглядели в своем дремучем невежестве. Но на первое место не тянет, поскольку реальных последствий это выступление не имело :)
Ну и еще хочется выделить историю о том, как "Американские лейблы подали в суд на российский AllOfMP3.com". Сумма иска в 1.6 трилиона баксов вызывает прилив патриотизма и гордости за нашу державу. Также поражает бычья твердолобость и абсолютная уверенность американцев в том, что все в этом мире должны жить по их законам.
А вот "Отключение мобильной связи и ЖЖ во время проведения „Русского марша" - меня лично даже не удивило. Я три года прожил в союзной Беларуси, так вот там такие вещи происходят на каждые выборы: блокирование сайтов и целых доменов, закрытие сайтов, чистка форумов, отключение мобильных сетей и т.д. Способствует всему этому наличие гос. монополии в лице одного интренет провайдера. В России для всего этого есть "хороший" потенциал роста.

Yahoo пострадал от AJAX

Yahoo не стал сайтом-лидером США по количеству запросов интернет-страниц в ноябре. Причина этого – внедрение технологии AJAX, позволяющей обновлять веб-страницы без перезагрузки.
Забавно. Видимо в Yahoo начали искать крайних за маркетинговые провалы, и нашли их среди разработчиков. Хотя выглядит это не очень убедительно. Я, специально походил по Yahoo и нашел AJAX-овые компоненты только на главной странице. Вряд ли они могли кардинально повлиять на статистику сайта, которая насчитывает десятки миллиардов посещений.
С другой стороны проблема все-же существует. При использовании AJAX запросы на сервер все равно идут, но посколько они направлены не к обычным web страницам а к web сервисам, разместить на них обычные счетчики невозможно. И этот трафик действительно оказывается не охвачен статистикой.
Я думаю системы оценки и ранжирования сайтов ждут большие премены. Ведь место в рейтинге это большие деньги и их никто не хочет терять.

понедельник, декабря 25, 2006

Google == "большой брат" ?

Google выдал полиции результаты поисковых запросов хакера, и они стали основой обвинения. Мужика посадили на 15 месяцев. Если все действительно так, как написано в заметочке, то что-то не весело становится. Хотя возможно репортеры по своей бестолковости все наврали, и для доказательства вины были использованы логи с его машины, а не с сервера.
В гугловском блоге писали, что в главном офисе Google есть большое табло на которм высвечивается бесконечная лента текущих поисковых запросов. Воспаленное воображение дорисовывает картину: после табло, специальный синтаксический анализатор ищет крамолу в вашем запросе и тут же направляет его в ФБР - ЦРУ - Моссад - КГБ. И вот уже ночь, фары, черный воронок, стук в дверь...
Ну что страшно? А может это паранойя? Как вы думаете?

пятница, декабря 22, 2006

Бери шинель, пошли домой.

Сейчас становится модно программировать дома, в тапочках. Тенденция однако. У нее много проявлений. Вот к примеру фриланс наступает, обретая все новые формы. С другой стороны идеи виртуального офиса овладевают массами. Elena Makurochkina рассуждает на эту тему. А есть еще такая штука, именуемая емким словом "удаленка". Это когда компания нанимает разработчиков удаленно. Это не фриланс, потому что человек работает не на себя. Мобильным офисом тут тоже не пахнет. Большинство удаленщиков никогда не видели ни офисов своих компаний, ни даже своих коллег и начальников. Удаленщикам не нужны никакие инновации из области мобильного офиса. Им нужно подключение к интернет и банковская карточка, на которую переводят зарплату. Многие компании, такие как ComponentOne используют труд удаленщиков или полностью построены на такой модели найма сотрудников.
В последнее время мне приходилось проводить много телефонных технических интервью с кандидатами из регионов России, Украины и Беларусии. Вы знаете, я был очень удивлен тем, насколько это явление широко распространилось среди наших разработчиков. До трети от всех кандидатов из регионов работают удаленно.
Многие считают что за этой моделью будущее, многие хотели бы так работать, некоторые думают что в области разработки ПО большие офисы скоро уйдут в прошлое и все разработчики будут сидеть по домам писать код и общаться по скайпу. Я же считаю, что удаленка - это зло. В первую очередь для самого разработчика.
Одна тенденция порождает другую. Беседуя с удаленщиками, я заметил неожиданную особенность, в среднем уровень их профессиональных знаний ниже, чем у людей работавших обычным образом, при условии одинакового по времени опыта работы. Поразмыслив, я понял, что ничего удивительного в этом нет. Человек - существо социальное. Потому, что группа всегда имеет преимущество перед одиночкой. Группа быстрее аккумулирует знание и опыт, группа быстрее и качественнее решает проблемы. Много раз приходилось наблюдать, как молодой, неопытный сотрудник (студент) приходит в крепкую команду разработчиков и буквально через пару месяцев он подтягивается к общекомандному уровню опыта и знаний.
Мне могут возразить, что используя современные технологии, можно находиться дома (где угодно) и продолжать работать в команде. Ничего подобного. Даже с использованием самых последних средств групповой работы, включая аудио и видео телеконы, эффективность удаленных коммуникаций на порядок ниже по сравнению с коммуникациями прямыми. Не забывайте о неформальном общении. Люди общаются в кофейне, в курилке, по дороге на обед. Разработчики очень любят обсуждать проффесиональные темы. И даже в том случае когда двое обсуждают то, какой Вася идиот, и до чего кривой интерфейс он реализовал для ХХХ, и тогда идет обмен полезной информацией и накопление опыта.
Есть и еще один фактор, который стимулирует развитие - это внутренняя конкуренция между сотрудниками. В офисе она существует всегда. Образуется неформальная иерархия, которая опирается на авторитет сотрудников в глазах друг друга, и это становится стимулом профессионального развития.
Удаленщик всего этого лишен. Конкурировать с другими разработчиками он может только заочно. Коммуницировать он может только удаленно. К тому же он в халате и тапочках, т.е. пребывает в расслабленном состоянии. Время удаленщика течет медленнее. Время в офисе спрессовано и сжато - сутки за 8 часов. Результат: удаленщик проигрывает это заочное соревнование. Его уровень хоть и растет, но несравненно медленнее.
Я уже говорил, что при поиске сотрудников среди резюме из регионов до 30% удаленщиков. Знаете о чем это говорит? Нет, не о том, что в Калуге или Волгограде каждый третий программист сидит на удаленке. Просто большинство из них, посидев на удаленке год - два, начинают искать работу в офисе (об этом написано в их резюме).
Так что, не забудьте - в понедельник на работу. А понедельник, как известно, начинается в субботу. :)

вторник, декабря 19, 2006

Односторонние соединения web сервисов и о пользе стандартов.

Не так давно, мне пришлось решать вот такую задачу. Есть две системы A и B. Система A должна обращаться к web сервису системы B, передавая ей некоторые данные и получая назад результаты расчета. Загвоздка была в том, что система A расположена в DMZ (Demilitarized Zone – специальный домен на границе внутренней сети и Интернета), а система B - во внутренней сети компании. Между ними, на границе сетей стоит firewall. Обычный такой firewall, и сконфигурирован как обычно: пропускает только запросы, инициированные из внутренней сети наружу (в DMZ). Т.е. установить соединение от системы A к системе B невозможно. Пришлось придумывать workaround, изменять направление потока сообщений и сигнатуры методов. Теперь система B периодически опрашивает систему A на предмет наличия запросов. При наличии ожидающего запроса система A возвращает его в респонсе. Система B производит вычисления и отправляет результат назад, путем вызова другого метода в системе A. Иными словами простая схема взаимодействия:


Заменена на сложную схему взаимодействия:

И ведь это еще не все. В системе А пришлось организовать внутреннюю очередь для хранения запросов к системе В в промежутках между опросами. Как видно из второй диаграммы, операция расчета стала асинхронной, поэтому пришлось уникально идентифицировать запросы, чтобы установить соответствие между запросами и результатами расчетов. Кроме того, пришлось ввести дополнительный SOAP заголовок, сигнализирующий системе B о наличии в очереди необработанных запросов, для того чтобы инициировать «внеплановые» опросы B-A при наличии ожидающих в очереди запросов. Это необходимо, для того, чтобы система могла адаптироваться к нагрузке.

В общем, все закончилось хорошо, решение проблемы было найдено. Если вы думаете, что это не типичный случай – вы глубоко заблуждаетесь. Все firewall-ы на границах локальных сетей компаний сконфигурированы одинаково. Они пропускают запросы из внутренней сети наружу и задерживают запросы из внешней сети во внутреннюю. И если вам необходимо организовать взаимодействие двух систем в разных сетях вы наверняка столкнетесь с такой ситуацией «одностороннего» соединения.
И вот недавно, мне попалась на глаза статья Дага Дэвиса (Doug Davis) из IBM под мудреным названием «Взаимодействие между WS-сервисом надёжной передачи сообщений (WS-RM) и WS-механизмом опроса (WS-Polling)». Несмотря на название, в статье рассматривается какие методы рабочая группа протокола WS- ReliableMessaging предлагает для борьбы с клиническим случаем, одно из проявлений которого я вам только что описал. И знаете, что меня порадовало в этой статье? То что, зубры из технических комитетов W3C, OASIS и других уважаемых организаций, все-таки не зря получают пэйчеки в своих конторах. О конкретной моей проблеме знают в техническом комитете OASIS, и над ней работают. И это радует. Сколь бы сложными и запутанным не казались нам все эти спецификации сетевых сообществ, они в конечном итоге делают проще нашу с вами жизнь – рядовых разработчиков и проектировщиков. В статье очень подробно расписано, как данная проблема будет решаться на уровне расширения SOAP протокола, которым является спецификация WS-RM. Также приводится альтернативный вариант решения на основе протокола WS-Polling, рассматриваются его преимущества и недостатки.
Насколько я понял, спецификация WS-RM близка к принятию, а ее поддержка уже встроена в WCF (во всяком случае, стандартные привязки с настраиваемым уровнем гарантированной доставки есть). Я, правда, пока не понимаю, как в WCF реализовать поддержку описанного в статье сценария стандартными средствами. Возможно это предмет для будущих версий фрэймворка.
Ссылки по теме

Амбиции и позиции

Япония самостоятельно вывела на орбиту один из самых тяжелых спутников в мире, хотя в этой области (вывод тяжелых нагрузок на стационарные орбиты) мы всегда считали себя непревзойденными. Я помню, лет пять назад, популярные издания пестрили саркастическими заметками о неудачах японского ракетостроения. И теперь вот, и здесь мы уже не первые.
К чему это я? А вот к чему. В последнее время в российском IT сообществе циркулирует мнение о какой-то особой роли России в мировой IT индустрии, о том что наши программисты самые програмистые програмисты в мире, и негоже им браться за обычные проекты - для этого есть индусы и китайцы. А наши должны делать что-то супер инновационное, великое и ужасное.
На чем базируется данное мнение - мне абсолютно не понятно. Отрадно, конечно, что наши специалисты начинают говорить на одном языке с остальным IT миром. Что новые технологии наконец-то презентуются в России следом, или даже одновременно с США. Что в некоторых областях мы остаем уже не "навсегда" а всего лишь на год - два. Так что с того? Это говорит лишь о том что мы наконец начинаем интегрироваться на равных в мировую IT индустрию, но у некоторых видимо закружилась от успехов голова.
Завтрашний день IT технологий формируется сейчас в лабораториях крупнейших брендов и в технических комитетах таких сообществ как W3C и OASIS. Вот когда там появятся представители Yandex или другого русского супермегабренда, тогда можно будет сказать: действительно, русские что то значат в IT.
А пока стоит поучиться у японцев. "Терпение и труд - все претрут" (с) Японская поговорка :)

понедельник, декабря 18, 2006

Вышел MSDN Magazine #12

Online. В русском переводе. Доступен здесь. Особо рекомендую статью "Фабрика программного обеспечения веб-служб"

Управление экземплярами сервиса в WCF

Всем, кто интересуется WCF(Indigo) настоятельно рекомендую к прочтению перевод статьи Джувела Лоуи (Juval Lowy) "Эффективные методики управления экземплярами в WCF-приложениях" (оригинал статьи на английском здесь). В статье очень хорошо изложены вопросы организации Singleton, Rer-Call, Per-Session сервисов; управления активацией и деактивацией экземпляров, и даже такие экзотические возможности как дросселирование. Управление экземплярами сервисов было вообще не характерно для более ранних версий реализации web сервисов от Microsoft (в ASP.NET 1 и 2). А вот в технологии Remoting управление экземплярами удаленных объектов было одним из ключевых моментов. На примере данного материала, отчетливо видно, что WCF действительно является сплавом лучших коммуникационных технологий от Microsoft. По сравнению с Remoting-ом все стало значительно проще и гибче. Управление экземплярами - ключевой механизм обеспеченения таких характеристик приложения как производительность и масштабируемость.
Так что читаем, приобщаемся.

пятница, декабря 15, 2006

Мифология SOA

Вы увлечены идеями SOA? Вы хотите применить их на практике при проектировании своих систем или в интеграционном проекте? Тогда приготовьтесь к трудностям. Существует ряд широко распространенных мнений, которые я, иначе как мифами SOA назвать не могу. На пути практической реализации своих идей вы наверняка столкнетесь с трудностями в самых неожиданных местах, именно там где их быть вроде бы не должно.
Проектирование систем всегда ведется по двум встречным направлениям. С одной стороны, мы движемся от бизнес модели, которая представлена бизнес возможностями и бизнес требованиями, через модель сервисов, которая и есть суть предмета SOA, к технологической модели, в которой раскрывается реализация системы. С другой стороны существует встречное движение от технологических платформ, которые определяют основные возможности и ограничения, к модели сервисов, где эти возможности и ограничения следует грамотно учесть, и к бизнес модели, где мы сверяем полученный результат с предъявляемыми требованиями. При этом основные трудности проектирования всегда возникают на границах, при переходе от одной модели к другой. О том, как трансформировать бизнес модель в модель сервисов, написано очень много. Среди написанного, попадаются здравые идеи, и об этом я расскажу в другой раз. Основные трудности и разочарования поджидают вас на этапе, когда вы приступите к реализации красивой и согласованной модели сервисов. На технологическом уровне, одни идеи оказываются не реализуемыми, другие - бесполезными, а третьи выглядят при реализации совсем по-другому. Все эти вещи я и называю «мифами SOA». Давайте поговорим о них.
Все проектируют сервисы, а кто спроектирует клиентов? «Автономные и независимые сервисы взаимодействуют путем передачи друг другу сообщений» - это одна из основных парадигм SOA. Все сосредоточены на проектировании сервисов, но когда мы спускаемся на уровень реализации, независимо от платформы (java, .Net, PHP) мы обнаруживаем что здесь оперируют другими терминами. Здесь сервис взаимодействует с клиентом. А сервис с сервисом взаимодействовать не может. Т.е. мы обнаруживаем, что в нашей системе, образно говоря, есть только розетки, и нет ни одного штепселя. Проблема концептуальная и достаточно серьезная. Подумайте о том, что контракт сервиса на самом деле имеет две комплиментарные стороны: строну сервиса и строну клиента. Если ваш сервис намерен не только выступать источником каких либо данных, но и получать какие либо данные из вне, то часть его контракта будет носить «клиентский» характер.
Сервисы регистрируются в реестре, для того чтобы другие сервисы могли их найти. Роль реестра сильно преувеличена. Сценарий, в котором сервис - потребитель данных обращается в некий реестр сервисов и находит там для себя реализацию сервиса - источника данных, а затем начинает ее использовать, чрезвычайно сложен в реализации. Более того, он недостаточно подкреплен соответствующими спецификациями отраслевых стандартов. Те спецификации, которые сейчас существуют (UDDI, WS-management) либо не покрывают все проблемы, связанные с данным сценарием, либо недостаточно поддерживаются вендорами платформ. Реестром может пользоваться middleware но сами сервисы - вряд ли. Проблемы тут, одна хуже другой. Проблема поиска – как найти нужный сервис, по каким критериям должен осуществляться поиск. Проблема совместимости – что делать, если нужный сервис найден, а контракты не совпадают. Наконец, проблема безопасности - нужный сервис найден – как установить доверительные отношения; как сервис источник узнает, что нового потребителя можно обслуживать; как сервис потребитель получит удостоверения для обращения к нужному сервису.
Вы не сделаете это сами. Вы не реализуете полноценную SOA архитектуру самостоятельно, потому что не один из технологических стеков (платформ разработки web сервисов) не предоставляет всех возможностей для построения полнофункциональной SOA архитектуры от и до. Вам потребуется middleware. И оно стоит денег. Все, что на сегодняшний день предоставляют вам вендоры платформ, это инструментарий для разработки самих сервисов и более или менее богатый набор средств доставки сообщений (объектные брокеры, очереди сообщений, координаторы транзакций, транспортные и форматные привязки). Если у двух ваших сервисов, которые должны взаимодействовать, формат сообщений различается хоть на один байт, вам потребуется промежуточное ПО (middleware) для преобразования сообщений. Если вам надо организовать управляемую событиями или данными маршрутизацию сообщений - вам потребуется соответствующее middleware. Все наиболее привлекательные особенности SOA, как то, гибкость и адаптивность, композиция, ориентация на процессы, все это на сегодняшний день осуществляется в основном средствами соответствующего middleware. Вы можете предпринять отчаянную попытку все это сделать самостоятельно, но будьте готовы к тому, что ваши затраты вырастут в разы, а решение получится сложным, недостаточно гибким и не соответствующим промышленным стандартам.
Это, пожалуй, наиболее распространенные мифы SOA. Однако, не будем забывать о том, что два - три года назад вся SOA была лишь красивой теорией не подкрепленной даже соответствующими спецификациями и стандартами, не говоря уже о продуктах и платформах. Например в .Net 1.0 в плане реализации web сервисов не поддерживала ни WS-Security, ни WS-Atomic transaction. А Apache Axis даже не имела средств для генерации кода по WSDL описанию. Сегодня все гораздо лучше. Windows Communication Foundation уже не только поддерживают транзакции и шифрование но и оперирует такими понятиями, как контракт сервиса и контракт клиента, конечная точка и привязка. Будем надеяться, что через пару лет мифы превратятся в реальность. И эта реальность будет «сервис – ориентированной».

вторник, декабря 12, 2006

Архитектура, удовлетворение требований и борьба с неопределенностью.

Что такое архитектура программной системы? Это слово уже настолько замылилось от частого и беспорядочного употребления, что на прямо поставленный вопрос - «А что такое программная архитектура» большинство людей, профессионально связанных с программированием, начинают невнятно лепетать что-то, о фундаментальном, основополагающем etc. Видимо виновато в этом само слово. Очень уж сочно и солидно оно звучит, хоть в русском варианте, хоть в английском. Забавно наблюдать на программистских форумах очередное обсуждение архитектуры, описанной в одном абзаце несвязного текста. Во многих головах существует стойкое заблуждение о том, что архитектура существует сама по себе, живет, развивается, обретает новые формы, и можно приложить ту или иную архитектуру к конкретной ситуации и получить работающее решение. И степень успеха при этом зависит лишь от того, «хорошую» или «плохую» архитектуру мы выбрали. Прискорбное заблуждение, приводящее зачастую к плачевным результатам.

Как получается что, использовав казалось бы «блестящую архитектуру» для построения очередной системы, получаем неутешительный результат? Если попытаться размотать клубок причин и следствий в обратную сторону, то мы обнаружим, что неудовлетворительный результат есть следствие того, что система не оправдала ожиданий пользователей, а ожидания пользователей это, по своей сути, есть не формализованные требования к системе. Т.е. примененная архитектура не обеспечила выполнения всех требований, которые позволили бы оценить систему как успешную. Но ведь архитектура то была «блестящая», в чем же дело? Так и есть, все дело в требованиях. «Блестящая архитектура» удовлетворяла определенному набору требований, но оказалась неприемлемой, когда требования изменились.
Блестящее определение архитектуры, авторство которого мне не известно, но которое донес в незамутненном виде до меня Антон Терехов (Антон, спасибо!) звучит так:

Архитектура (программного обеспечения) – это совокупность согласованных технических решений направленная на удовлетворение требований, предъявляемых к программному обеспечению.

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

Вторая проблема похуже первой, и связанна она с тем, что проектирование ПО, это не детерминированный и эвристический процесс. Программу, обладающую фиксированным набором выходных характеристик всегда можно реализовать множеством различных способов. О проектных решениях нельзя сказать ничего определенного до тех пор, пока они не будут реализованы! Часто нельзя быть уверенным будет ли вообще работать то или иное решение, до тех пор пока не сделаешь какой либо макет. Вот что говорит об этом Стив МакКонелл в своей книге “Code Complete”:
«Пректирование - грязная проблема. Хорст Риттел и Мелвин Веббер определили «грязную» проблему как проблему, которую можно ясно определить только путем полного или частичного решения. По сути, данный парадокс подразумевает, что проблему нужно решить один раз, чтобы получить ее ясное определение, а затем еще раз для создания работоспособного решения. Этот процесс уже несколько десятилетий неразрывно связан с разработкой ПО».


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

Подобно тому, как у старого шамана, всегда наготове, помимо бубна конечно, есть целая куча фенечек, типа сушеных летучих мышей, жабьего помета, толченых червяков etc. На любой случай жизни, у бывалого архитектора тоже, помимо бубна конечно, есть наготове набор своих паттернов, снипетов, и просто бесценный багаж негативного опыта прошлых проектов. Бывалый архитектор всегда может усталым голосом сказать: «Ребята, эта фишка не будет работать. Вспомните, на проекте BlueGene восем лет назад они тоже хотели сделать что то подобное, потратили на это полтора мегабакса и у них так ничего не заработало». На то они и бывалый архитектор. Главное, чтобы груз опыта заваленных проектов его совсем не задавил :)

среда, декабря 06, 2006

Проектирование web сервисов

В данной статье мне хотелось бы обсудить вопросы, связанные с проектированием web сервисов, предназначенных для межпрограммного взаимодействия. Так называемых – интеграционных web сервисов. Прежде всего, это сервисы, которым предстоит работать в различных решениях класса Enterprise Applications Integration (EAI), а также в B2B решениях. Вопросам разработки web сервисов с использованием различных платформ посвящено множество книг и статей, а вот вопросы проектирования освещены гораздо хуже. Вы можете найти массу статей с заклинаниями на тему SOA, которые будут совершенно бесполезны, когда вы приступите к проектированию своего web сервиса.
Итак, давайте рассмотрим ситуацию, когда у вас на руках есть две работающие системы (либо проекты двух систем), и перед вами стоит задача организовать взаимодействие между ними. Вам надоели схемы синхронизации на основе файлов импорта / экспорта, и вы остановили свой выбор на модной, современной и удобной технологии web сервисов. Я попытаюсь рассказать о том, на что следует обратить внимание и как избежать типичных ошибок при проектировании web сервиса для взаимодействия двух систем.

Немного SOA


И все же, сначала пару слов о небезызвестной SOA. Термин Service Oriented Architecture (SOA) довольно серьезно пострадал от беспорядочного употребления в маркетинговых целях. Его так часто употребляют в различных контекстах, что теперь сказать что-то определенное о SOA, кроме того, что это - «сервис ориентированная архитектура», уже трудно.
Тем не менее, SOA имеет такое же право на жизнь, как и «клиент – серверная архитектура» или «много уровневая архитектура». Как и любая другая «архитектура», SOA вводит ряд абстракций и правил, которые помогают нам в разработке определенного класса приложений. В данном конкретном случае, руководствоваться принципами SOA полезно при разработке механизмов взаимодействия и интеграции приложений в масштабе предприятия (Enterprise Applications Integration - EAI).
Итак, начнем с того, что SOA рассматривает приложения как сервисы или совокупности сервисов, которые обмениваются сообщениями напрямую, либо посредством некоторых интеграционных механизмов. Эти сервисы должны удовлетворять ряду условий или требований.
Во-первых, сервис должен иметь определенное «business value», иными словами, сервис должен иметь прикладное значение. Если вы не можете простыми словами объяснить заказчику или пользователю системы для чего нужен ваш сервис, то с точки зрения SOA это неудачный сервис. Пример плохого сервиса: «Сервис для получения глобально – уникальных идентификаторов». Пример хороших сервисов: «Сервис данных о сотрудниках предприятия» или «Сервис приема и обработки счетов к оплате» (Accounts Payable).
Во-вторых, с точки зрения SOA, сервисы должны быть автономными и независимыми. Это необходимо для того, чтобы как это принято в SOA, мы могли комбинировать одни сервисы с другими в зависимости от потребностей бизнеса. Если мы захотели использовать «Сервис приема и обработки счетов к оплате» и при этом узнаем, что нам придется установить и использовать «Сервис бюджетирования и финансового планирования», «Сервис ведения главной книги», и еще дюжину сервисов, то с точки зрения SOA все это выглядит просто отвратительно.
В-третьих, сервис должен быть функционально полным с прикладной точки зрения. Это требование комплиментарно предыдущему. Например, «Сервис приема и обработки счетов к оплате» позволяет добавить заявку, а отследить ее статус не позволяет. Но в принципе мы можем получить отчет по заявкам, из которого можно узнать о статусе заявки. Не правда ли, знакомая ситуация? С точки зрения SOA, да и с любой точки зрения – это не правильно. Сервис должен обеспечивать выполнение всех основных бизнес операций, связанных с прикладной областью или бизнес процессом, который он обеспечивает.
В-четвертых, все сервисы должны уметь предоставлять свои описания в некотором стандартизированном формате. WSDL и UDDI - это варианты форматов описания сервисов.
В-пятых, сервисы должны быть ориентированы на распределенное асинхронное взаимодействие без хранения состояния. Это типично для большинства современных распределенных систем, и это очень важное требование. Достаточно вспомнить, что клиент-серверная архитектура ориентирована на синхронное взаимодействие с хранением состояния, и именно это обстоятельство стало ключевым ограничением для дальнейшего развития этой архитектуры.
В-шестых, SOA предполагает, что благодаря выполнению предыдущих требований, сервисы будут способны объединяться для взаимодействия друг с другом при помощи различных интеграционных инструментов, типа брокеров и маршрутизаторов сообщений, серверов workflow и т.д. и т.п. Ключевым моментом здесь является тот факт, что интеграционные сценарии будут основываться на конфигурировании, часто визуальном, а не на разработке специального «интегрирующего» кода, как это делается в большинстве случаев сейчас.
Вот примерно так выглядят основные вехи, на которые мы должны ориентироваться и которые должны ограничивать нашу разыгравшуюся дизайнерскую мысль в процессе проектирования и разработки интеграционных web сервисов.

Проектирование контракта сервиса.


После того, как Microsoft в своей платформе Windows Communication Foundation – WCF (бывшая Indigo) использовала термин «контракт сервиса», он имеет все шансы сделаться стандартным термином де-факто.
Контракт сервиса - это описание сигнатур всех его операций (методов), плюс описание форматов данных, которыми он оперирует в качестве входных и выходных сообщений.
Для web сервисов контракт исчерпывающе описывается WSDL схемой сервиса. Однако согласитесь, что WSDL не очень хорошо приспособлен для человеческого восприятия. Гораздо нагляднее контракт можно изобразить в виде UML диаграммы классов. К счастью, большинство современных платформ для разработки web сервисов обеспечивают функцию автоматической генерации WSDL описания сервисов.
Правильно спроектированный контракт сервиса, залог успеха, и гарантия того, что ваш сервис проживет долгую и счастливую жизнь, и умрет своей смертью, потому что на смену ему придет новая технология, о которой мы сейчас ничего не знаем.
Microsoft предлагает специальный термин – “contract first” для описания подхода к проектированию, ориентированного в первую очередь, на контракт. Он предполагает, что проектирование сервиса должно начинаться с описания XSD схем сообщений (данных), затем создается WSDL описание операций сервиса, по которому генерируется код класса сервиса. И лишь после этого приступают к имплементации логики. Идея здравая, однако, я повторюсь, что работать с XSD и WSDL не очень удобно, даже с использованием визуальных средств типа XmlSpy. Кроме того, сам по себе принцип “contact first” не гарантирует нам получения правильного контракта сервиса.
Для меня принцип “contact first” означает, что при проектировании сервиса мы должны в первую очередь думать о его контракте и в последнюю - о деталях реализации. Следует взглянуть на будущий сервис c внешней стороны, глазами потребителя данных этого сервиса. Такой взгляд упрощает транспонирование требований предъявляемых к сервису в набор предоставляемых им операций. Например, мы разрабатываем сервис, предоставляющий данные о сотрудниках предприятия. Логично предусмотреть в нем операцию, возвращающую список всех сотрудников. Однако, в ходе анализа требований может оказаться, что потребителю данных нашего сервиса будет интересен, в первую очередь, список сотрудников конкретного подразделения. Это говорит нам о том, что в контракт операции, возвращающей список сотрудников, следует добавить параметр, позволяющий отфильтровать сотрудников конкретного подразделения, либо, нам следует выделить это действие в отдельную операцию сервиса. Такой подход будет соответствовать принципу “contract first”. И напротив, мы могли бы возложить на потребителя данных нашего сервиса обязанность выбрать сотрудников нужного ему отдела из общего списка. Придерживаться подобной стратегии при проектировании интеграционных сервисов – плохая практика. Разбирая конкретный данный случай, мы можем говорить о том, что такое решение не оптимально с точки зрения нагрузки на сервис или объема трафика, а также что такое решение снижает безопасность. Обобщая, можно сказать, что основания простоты и универсальности не должны превалировать при проектировании сервиса. Универсальность сервиса должна достигаться тщательным дизайном. Крайне сложно дать какие либо практические рекомендации в данном направлении. Не легко создать сервис, который бы не состоял из одного метода, вываливающего все внутренние данные системы наружу, и в тоже время был достаточно универсальным для того, чтобы его смог использовать не только тот потребитель, для которого вы проектировали сервис, но и любой другой. Чтобы добиться этого, следует обращать внимание на функциональную полноту контракта сервиса.
Давайте рассмотрим пример. Предположим, существует некая система учета и согласования счетов к оплате, из разряда тех, что обозначаются термином Accounts Payable. Система позволяет вводить данные счетов к оплате, обеспечивает бизнес процесс их согласования и, далее взаимодействует с бухгалтерской системой для формирования платежей. Ввод счетов и их согласование осуществляется, через UI системы. Перед нами стоит задача: создать web сервис, посредством которого другие системы могли создавать счета и отслеживать их статус.
Для простоты, будем считать, что счет к оплате имеет следующую структуру:

Где:
Number – уникальный номер счета, присваиваемый при создании
RecipientID – ссылка на справочник контрагентов, в котором хранятся все реквизиты получателя платежа
Amount – сумма к оплате
PaymentDate – дата оплаты
CategoryID – ссылка на справочник категорий платежей
Owner - имя сотрудника, который создал счет
Description – описание
State – состояния счета, вокруг которого строится бизнес процесс согласования счета.
Итак, исходные требования нам ясны. Схема данных для представления счета, тоже понятна. На основе представленной диаграммы будет создана вот такая XML схема, которая нас вполне устраивает:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<xsd:complexType name="Payment">
<xsd:sequence>
<xsd:element minOccurs="1" maxOccurs="1" name="RecipientID" type="xsd:int" />
<xsd:element minOccurs="1" maxOccurs="1" name="Amount" type="xsd:decimal" />
<xsd:element minOccurs="1" maxOccurs="1" name="PaymentDate" type="xsd:dateTime" />
<xsd:element minOccurs="1" maxOccurs="1" name="CategoryID" type="xsd:int" />
<xsd:element minOccurs="0" maxOccurs="1" name="Owner" type="xsd:string" />
<xsd:element minOccurs="0" maxOccurs="1" name="Description" type="xsd:string" />
<xsd:element minOccurs="1" maxOccurs="1" name="State" type="tnxsd:PaymentState" />
</xsd:sequence>
<xsd:attribute name="Number" type="xsd:int" use="required" />
</xsd:complexType>
<xsd:simpleType name="PaymentState">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Draft" />
<xsd:enumeration value="Approved" />
<xsd:enumeration value="Rejected" />
<xsd:enumeration value="Commited" />
</xsd:restriction>
</xsd:simpleType>
</xsd:schema >



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

Выглядит просто ужасно! Давайте разберем, что здесь не так.
Метод добавления счета AddPayment() принимает в качестве параметра структуру Payment, а возвращает номер, присвоенный счету. Структура Payment не очень подходящий тип данных для параметра этого метода, потому что она содержит ряд полей, заполнение которых подчиняется внутренней бизнес логике системы. Это поля Number, State и Owner. Мы не знаем этих бизнес правил, и не знаем, как заполнять эти поля. Для создания счета мы должны указать: получателя, сумму платежа, дату, категорию и описание. Поэтому имеет смысл создать метод именно с такими параметрами, который создаст счет к оплате и вернет его в структуре Payment.
Метод ListPayments() возвращает список счетов в виде массива структур Payment. Возможно, система должна возвращать только те счета, которые были созданы данным пользователем. Однако отсутствие соответствующего параметра в сигнатуре метода не должно вас удивлять. Более надежный и безопасный способ получить данные о пользователе, - взять их из данных аутентификации. Со временем в системе может накопиться огромное количество счетов, и данному методу не помешали бы параметры для фильтрации выдаваемого списка по датам.
Наконец, метод RemovePayment() - принимает номер счета и возвращает true в случае успешного удаления и false в противном случае. Использование Boolean в качестве результата не лучшее решение в данном случае. Причины неудачи операции удаления могут быть самыми различными от простого отсутствия счета с указанным номером, до нарушения логических ограничений системы (например, нельзя удалить счет со статусом Commited). Ситуация довольно распространенная. Ошибка может случиться при выполнении любого метода web сервиса, и, к счастью, инфраструктура web сервисов предлагает стандартный подход к решению этой проблемы. Для передачи данных об ошибке используется SOAP сообщение, в теле которого содержится элемент Fault с информацией об ошибке. Таким образом, нам не нужно расширять контракт нашего web сервиса для передачи сообщений об ошибках, и в случае метода RemovePayment() мы можем вполне обойтись без возвращаемого значения. В случае неудачи удаления счета, информация о причине будет передана в сообщении об ошибке.
После всех изменений наш web сервис будет выглядеть так:

Выглядит значительно лучше, но проблемы остались.
Давайте посмотрим на метод CreatePayment(). Для того чтобы создать счет, нам надо передать пять параметров. С параметрами amount, date, desc, у нас проблем нет. Но вот откуда взять ссылки на получателя (recipient) и категорию (category)? Как я уже упоминал, эти данные представляют собой ссылки на элементы соответствующих справочников нашей системы. И теперь нам становится понятно, что для успешного использования нашего web сервиса, мы должны предоставить доступ к значениям этих справочников.


Таким образом, окончательный вид контракта нашего web сервиса дополнился двумя методами EnumRecipient() и EnumCategory() и двумя типами данных RecipientInfo и CategoryInfo. Их не было в первоначальном контракте и необходимость этих методов не определяется первоначальными требованиями. Они служат для обеспечения синтаксической полноты контракта нашего сервиса. Теперь потребитель данных сервиса может вызвать методы EnumRecipient() и EnumCategory() для получения списков получателей и категорий, выбрать из них необходимые значения, и затем вызвать метод CreatePayment() для создания счета.
Теперь мы можем сказать, что мы спроектировали функционально и синтаксически полный сервис. Также, наш сервис достаточно универсален, и с большой вероятностью его удастся использовать для интеграции с другими системами, перед которыми будут стоять похожие задачи. Кроме того, контракт сервиса и его WSDL описание, включают в себя полные схемы данных всех сообщений. Это очень важно для того, чтобы наш сервис смогли использовать инструменты интеграции типа BizTalk Server, Fiorano ESB, Sonic ESB и др.
На последнем моменте хочется заострить внимание. Формат данных, которыми обменивается сервис, очень важен. Разработчики нередко впадают в одну из двух крайностей. Первая из них, это использование в качестве формата для всех сообщений «голого Xml». Причины тому могут быть самые разные, от попыток использования существующего кода импорта - экспорта Xml данных, до слишком буквального понимания сути web сервисов, как механизма обмена Xml сообщениями. Недостаток такого подхода достаточно очевиден, - схема данных не попадает в WSDL описание контракта сервиса, и это затрудняет его использование. На мой взгляд, существует единственный случай, когда такой подход оправдан. Это случай, когда нам заранее не известен формат передаваемых данных. Во всех остальных случаях, следует использовать типизированные сообщения.
Вторая крайность, это противоположность первой, и выражается она в том, что разработчики пытаются использовать в контракте сервиса существующие в системе внутренние форматы представления данных. Действительно, если в системе уже определены структуры и классы для представления данных (Data Transfer Objects), то почему бы не использовать их в контракте сервиса? Это не всегда это хорошая идея. Внутренние форматы данных могут оказаться не удобными для клиента сервиса. Они могут иметь специфический формат, который не поддерживается клиентской стороной. Пример такого формата DataSet из Microsoft ADO.NET. Данные DataSet прекрасно преобразуются XML и могут быть использованы ASP.NET web сервисами. Однако, это внутренний формат Microsoft, который не является отраслевым стандартом, он имеет свои особенности форматирования Xml представления, которые могут затруднить использование этих данных другими программными платформами web сервисов. Существует масса случаев, в которых использование DataSet оправданно и удобно, но интеграционные сервисы не относятся к их числу.
Таким образом, общие рекомендации по проектированию форматов сообщений web сервисов сводятся к следующему. Всегда старайтесь использовать типизированные сообщения, формат которых описывается определенной Xsd схемой. Проектируйте формат сообщений исходя из функциональных требований, и лишь в том случае, когда полученный формат совпадает с внутренним форматом данных системы, можно подумать об использовании внутреннего формата.

Протокол взаимодействия и его влияние на контракт


Под протоколом взаимодействия web сервиса мы понимаем ту часть требований, которая описывает последовательность взаимодействия сервиса и клиента, направление передачи данных, а также такие аспекты поведения сервиса, как транзакционность, асинхронность, и т.д.
Протокол взаимодействия и контракт сервиса очень тесно связанны. Помимо достаточно простых случаев, когда существует один сервис и его клиент, существуют и более сложные, в которых протокол взаимодействия требует наличия двух и более сервисов. Примером такого протокола может служить протокол асинхронного взаимодействия ASAP
Я не открою большого секрета, если скажу, что отличным способом построить контракт сервиса (по крайней мере, в плане используемых операций) является использование UML диаграмм взаимодействия. Мое личное мнение, наилучшие результаты дает применение Sequence diagram. На рисунке приведена Sequence diagram взаимодействия клиента и сервиса из примера AccountsPayable, рассмотренного выше. К сожалению данный пример достаточно прост, чтобы почувствовать насколько Sequence diagram облегчают проектирование контракта сервиса.

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

Вопросы безопасности


После того, как контракт сервиса разработан, а иногда и раньше, перед проектировщиком в полный рост встают проблемы обеспечения безопасности. Времена, когда web сервисы называли незрелой технологией, не способной обеспечить безопасность данных, давно прошли. Web сервисы могут быть надежными и безопасными. А могут и не быть. Тут все зависит от нас.
Очертим круг вопросов, которые надо решить, для того чтобы спроектировать безопасный сервис:

  • Аутентификация клиентов сервиса

  • Авторизация клиентов сервиса

  • Безопасность передачи данных

  • Хранение удостоверений для подключения клиентов к сервису


Аутентификация – это процесс подтверждения подлинности пользователя на основе предъявленных им удостоверений. Надежность механизмов аутентификации является критичной для обеспечения безопасности приложения в целом. Поэтому сразу стоит забыть об «анонимном доступе». Наилучший сценарий обеспечения аутентификации, воспользоваться средствами, предоставляемыми платформой web сервисов, которую вы используете. Обычно для аутентификации используются специализированные протоколы (Kerberos, NTLM) и их поддержка встраивается в платформу. Реализация собственных механизмов аутентификации наверняка потребует значительных затрат и чревата потенциальными сложностями реализации и дальнейшей поддержки.
Авторизация - это процесс предоставления пользователю полномочий по доступу к функциям и данным. Естественно, для того чтобы авторизовать пользователя, предварительно он должен быть аутентифицирован. Уловите разницу между аутентификацией и авторизацией. Аутентификация отвечает на вопрос «кто» такой клиент, а авторизация – на вопрос «что» может делать этот клиент. В подавляющем большинстве случаев, бизнес правила по которым тому или иному пользователю предоставляются права доступа к тем или иным данным и операциям определяются в самом приложении. И, следовательно, авторизация – это область ответственности приложения, предоставляющего сервис. Существует множество методов, практик и шаблонов реализации механизмов авторизации, описание которых может быть предметом отдельной статьи.
Помимо аутентификации и авторизации, защита данных при транспортировке их между сервисом и его клиентом является третьей составной частью в обеспечении безопасности web сервиса. Степень защиты определяется характером данных. Различают два способа защиты данных: цифровая подпись и шифрование.
Цифровая подпись используется для подтверждения подлинности данных. Она не защищает данные от несанкционированного прочтения, потому что данные не шифруются. Цифровая подпись позволяет удостовериться в том, что данные принадлежат именно владельцу подписи, а также в том, что данные не были изменены с момента подписи. Механизм цифровой подписи довольно прост. Он основан на использовании не симметричных алгоритмов шифрования, в которых используется пара ключей, один из которых приватный (закрытый) а второй публичный (открытый). Особенность не симметричных алгоритмов состоит в том, что данные, которые зашифрованы одним ключом, можно расшифровать только другим ключом. Цифровая подпись формируется следующим образом. Для данных вычисляется контрольная сумма (хэш код), которая шифруется приватным ключом владельца цифровой подписи. Это и есть цифровая подпись, она добавляется к данным. Так же, к данным может быть добавлен открытый ключ. С помощью открытого ключа, любой пользователь может расшифровать цифровую подпись и получить контрольную сумму, которую можно сравнить с контрольной суммой рассчитанной над текущими данными и, таким образом узнать, изменились ли данные.
В случае, когда нам необходимо не только обеспечить неизменность данных, но и скрыть от посторонних их содержимое, применяется шифрование. Обычно, при передаче данных применяется схема с сеансовым ключом, на механизме которой я не буду останавливаться, поскольку обычно она обеспечивается на инфраструктурном уровне. Итак, цифровая подпись и шифрование являются основными механизмами защиты данных при передаче. Мы можем задействовать эти механизмы на уровне транспортного протокола, либо на уровне сообщений SOAP. Каждый из способов имеет свои преимущества и недостатки.
Для обеспечения защиты данных на уровне транспортного протокола обычно применяют хорошо зарекомендовавший себя механизм SSL. При этом между клиентом и сервером устанавливается защищенное соединение, в котором шифруется весь трафик. Преимущество данного подхода состоит в том, что он практически не требует дополнительных усилий со стороны разработчика (за исключением, может быть некоторых особенностей при установлении соединения). Основная работа по настройке защищенного SSL соединения производится при развертывании приложения. К недостаткам такого подхода можно отнести то, что шифруется весь трафик, а не только те данные, которые действительно являются конфиденциальными, и это требует больших затрат вычислительных ресурсов. Описание того, как использовать SSL совместно с web сервисами ASP.NET 1.1 вы можете найти в моей статье по адресу http://stump-workshop.blogspot.com/2006/10/web-https-ssl.html
Механизмы защиты данных на уровне SOAP сообщений строятся на использовании таких расширений SOAP протокола, как WS-SecureConversation, WS-Trust, WS-SecurityPolicy и WS-Security. Обычно поддержка этих спецификаций встроена на уровне платформы, реализующей web сервисы, и для разработчика они доступны как на декларативном уровне (атрибуты или конфигурирование) так и в виде API. К преимуществам использования данных механизмов относится их гибкость и универсальность. Вы можете использовать как шифрование, так и цифровую подпись. Можно обеспечить защиту только сообщений отдельных операций сервиса, и даже отдельных частей SOAP сообщения. К сожалению не все платформы web сервисов в полной мере поддерживают данные спецификации. Например, в широко распространенном .NET Framework 1.1 нет поддержки WS-Security. Поэтому при использовании данной платформы придется реализовывать защиту на транспортном уровне.
Наконец есть третий путь, - реализовать свой механизм защиты как, например, описано здесь. Следует, однако, понимать, что такой путь резко сокращает возможности использования вашего сервиса.
В заключении, стоит остановиться на таком вопросе, как хранение удостоверений для подключения к web сервисам. Мы уже говорили о том, что доступ к web сервисам следует предоставлять только аутентифицированным пользователям. Обратная сторона медали состоит в том, что нам необходимо указывать удостоверения (credentials) при подключении клиента к сервису. А следовательно, нам необходимо где-то хранить эти удостоверения. Когда нашей системе приходится взаимодействовать с несколькими сервисами, каждый из которых требует свои удостоверения, проблема приобретает особую остроту.
Что такое удостоверения. В большинстве случаев - это пара значений логин – пароль. Иногда это может быть клиентский сертификат или другой тип удостоверений, - все зависит от используемого протокола аутентификации. Следует четко понимать, что клиентские удостоверения относятся к данным, которые могут быть использованы для атаки злоумышленника на систему. Следовательно, эти данные требуют защиты. Вам следует учитывать это обстоятельство еще на стадии проектирования. При реализации интеграционных web сервисов, вам наверняка придется затратить некоторые усилия на разработку механизмов защищенного хранения клиентских удостоверений и их настройки / редактирования.

Вопросы реализации


Вопросы реализации web сервисов сильно зависят от платформы, с которой вы работаете, а их на сегодняшний день существует великое множество. Только Microsoft предлагает 4 платформы (ASP.NET 1.1, ASP.NET 2.0, WSE 1.0-3.0, WCF-Indigo). В мире Java их еще больше. Поэтому давать конкретные рекомендации весьма затруднительно. Однако есть моменты, на которые следует обратить внимание в любом случае.
Большинство платформ берут на себя формирование WSDL описания сервиса, предоставляя разработчику возможность работать с сервисом как с обычным классом. Поэтому, следует уделять постоянное внимание тем деталям, которые оказывают влияние на формирование правильного WSDL описания и XSD схем данных. К этим вещам относится использование Xml Namespace и префиксов, управление порядком сериализации данных, порядком и стилем генерации WSDL.
Следует стараться отделить бизнес логику от реализации сервиса. Хорошо, если у вас получится использовать единую бизнес логику внутри приложения и в web сервисе. В идеальном случае методы вашего web сервиса будут лишены всякой логики, кроме проверки параметров, формирования результатов и обработки ошибок, как показано на рисунке:

Заключение


Итак, в данной статье мы рассмотрели круг вопросов, связанных с проектированием интеграционных web сервисов.
Мы остановились на общих принципах концепции SOA. Рассмотрели практические вопросы проектирования контракта сервиса, обеспечения его функциональной и синтаксической полноты. Мы уделили внимание влиянию протокола взаимодействия на контракт сервиса. Рассмотрели варианты решения вопросов безопасности web сервисов. И в конце остановились на некоторых практических аспектах проектирования. Я надеюсь, что данная статья будет полезна вам при проектировании интеграционных web сервисов.

пятница, декабря 01, 2006

Проблема TimeZone при использовании web сервисов ASP.NET 1.1

Сразу оговорюсь, что все ужасы, о которых пойдет речь, имеют место только в .NET Framework 1. . Так вот, если ваш web сервис предает или принимает в своих сообщениях данные типа DateTime, то вы можете недосчитаться в них часов. Или наоборот, найдете там лишние часы. Происходит это следующим образом.


Предположим у нас есть web сервис с вот таким методом



[WebMethod]
public Report GetReport(DateTime from, DateTime to)
{
//…
}

И вот клиент вызывает его следующим образом



Report rep = service.GetReport(new DateTime(2006, 12, 1), new DateTime(2006, 12, 2));

Если этот клиент находится где-то неподалеку, то все будет Ok. А вот если он будет достаточно далеко на восток, настолько далеко, чтобы быть в следующей часовой зоне по сравнению с нами, то знаете, что мы получим на сервере? В первом параметре будет дата 31 ноября 2006 года, а во втором 1 декабря 2006 года. И знаете почему? Виноват SOAP форматер, который считает себя самым умным. При сериализации наших параметров типа DateTime он добавляет информацию о локальной временной зоне (заметьте, его об этом никто не просил). При десериализации на сервере, SOAP форматер видит, что временная зона в пришедшей дате больше чем локальная, и корректирует значение даты, выбрасывая «лишний» час. В результате оба наши параметра перескочили на один день назад! Причем полученные на сервере даты не несут никакой информации о временных зонах! Восстановить реальные значения уже нельзя.


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



// клиент
Report rep = service.GetReport(new DateTime(2006, 12, 1).ToLocalTime(),
new DateTime(2006, 12, 2).ToLocalTime());

// сервер
[WebMethod]
public Report GetReport(DateTime from, DateTime to)
{
from = from.ToUniversalTime();
to = to.ToUniversalTime();
//…
}