09 Сен 2011 в 03:00 

Remote Desktop Services (RDS) — одна из тех технологий, которые можно найти практически в любой среде Microsoft. Будь эти службы настроены для предоставления доступа к приложениям или к рабочим компьютерам, RDS является централизованным механизмом для предоставления пользователям доступа к инструментами необходимым им для выполнения своей работы.

В Windows Server 2008 список возможностей RDS значительно вырос. Наиболее очевидным улучшением стала способность предоставлять отдельные приложения в виде RemoteApps, а также в виде традиционного полноценного рабочего стола. Это позволило устранить существовавшую в ранних версиях проблему «двойных рабочих столов». Это также позволило пользователю работать с самим приложением.

В Windows Server 2008 R2 технология RDS снова сделала большой скачок вперед за счет интеграции с виртуальными структурами на основе Microsoft Hyper-V. Объединение RDS с Hyper-V предоставляет «айтишнику на все руки» многие из возможностей виртуализации настольных приложений, какие обычно используются в крупных компаниях. Это сочетание позволяет получить доступ к действительно крутым технологиям, но такая интеграция требует новых навыков для ее использования. Даже принятие решения о том, подходит ли новый путь для вашей среды, добавляет дополнительный этап в процедуру внедрения RDS.

Наряду с другими новыми возможностями RDS в R2 эти решения являются причиной недавнего выпуска в издательстве Microsoft Press книги «Windows Server 2008 R2 Remote Desktop Services Resource Kit» (Microsoft Press, 2010). Это почти 700-страничное руководство было полностью обновлено и теперь содержит рассказ о новых технологиях RDS R2. Оно должно стать настольной книгой каждого системного администратора, отвечающего за внедрение или поддержку RDS в обычной конфигурации или в виде конфигурации VDI (Virtual Desktop Infrastructure).

Мне удалось побеседовать с авторами книги — Кристой Андерсон (Christa Anderson) и Кристин Гриффин (Kristin Griffin). Андерсон является менеджером программ Microsoft в команде разработчиков RDS, а Гриффин носит звание Microsoft MVP по технологии RDS. Мы говорили о работе над книгой и о советах, которыми они хотели бы поделиться с «айтишниками на все руки». Вот что они могут сказать.

Говоря о RDS

Шилдс: Вы член команды Microsoft RDS, поэтому сможете рассказать нам, на какие особенности технологии RDS в Windows Server 2008 R2 следует обратить особое внимание?

Андерсон: RDS теперь предоставляет огромные возможности, предусматривая гораздо больше сценариев, чем это было в Windows Server 2003 и даже больше, чем в Windows Server 2008. Первым делом системному администратору необходимо обдумать, какие задачи он сможет решить с помощью виртуализации. После этого можно сосредоточиться на том, что является наиболее важным из этих потребностей. Например, если все пользователи подключены к локальной сети, то, скорее всего, шлюз RD не нужен.

То есть я бы рекомендовала, чтобы все администраторы обратились к хосту сеансов удаленных рабочих столов — службе роли RDS, которая выполняет основные функции удаленного рабочего стола. Многие люди в первую очередь думают об инфраструктуре виртуальных ПК как о средстве решения пользовательских задач. Во многих случаях сеанс традиционного удаленного рабочего стола работает так же хорошо, как и реальный компьютер. Сеансы традиционного удаленного рабочего стола также позволяют повысить плотности пользователей в расчете на один компьютер. Таким образом, хотя VDI и интересная технология, рассматривайте ее как запасной вариант на тот случай, если выяснится, что более традиционные RDS сеансы не соответствуют вашим потребностям.

Кроме того, изучите возможности Remote Desktop Client [RDC] версий 7 и 7.1. Если вы перешли с Windows Server 2003, то наверняка будете удивлены мощью и развитым интерфейсом для удаленных соединений RDC в Windows Server 2008 R2.

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

Если требуется предоставить сотрудникам доступ к приложению, используя только один сервер, начните со служб ролей RD Session Host, RD Easy Print и RD Licensing.Как только потребности переростают единственный имевшийся сервер хоста сеансов удаленных столов, начните изучать RD Connection Broker.Если необходимо предоставить веб-портал для доступа к приложениям изучите сервер RD Web Access. RD Web Access также полезен для предоставления легкого доступа к удаленному приложению через меню «Пуск» в Windows 7 (посредством функций RemoteApp и Desktop Connection).Следующим объектом изучения должен быть RD Gateway. Изучите его, когда будет нужно предоставлять доступ к приложениям из вне корпоративной сети.Наконец, для приложений, которые работают только на Windows XP, или для переноса удаленных рабочих столов в центр обработки данных, обязательно изучите RD Virtualization Host, RD Licensing и RD Connection Broker.

Шилдс: Такова уж природа работы системного администратора — отвечать за всю вычислительную среду. Это означает, что это профессия широкого профиля, а не узкая специализация. Как превратить RDS в доступную для ИТ-специалиста широкого профиля технологию?

Андерсон: В какой-то мере она более доступна, но с другой стороны, сложнее, поскольку сейчас RDS выполняет гораздо больше функций. Системный администратор должен еще много изучить. Один из способов сделать эту технологию доступнее — заставить разработчиков, чтобы при программировании своих приложений они учитывали возможность их работы в виртуальной среде. Приложения, которые отвечают требованиям Microsoft для Windows Vista и более поздних версий, должны работать на RDS без проблем. Печать упрощается, если не нужно учитывать два разных названия драйвера принтера. Сама по себе функциональность RDS также призвана помогать администратору. Например, анализатор соответствия рекомендациям (Best Practices Analyzer) предназначен для выявления и информирования о базовых ошибках в конфигурации, которые люди часто допускают. Просто базовые вещи работают лучше, чем прежде.

Гриффин: Службы RDS лучше документированы. Это действительно огромный плюс, особенно для системного администратора, который должен быстро обучаться. Печать с использованием RD Easy Print устраняет старые проблемы с работающими в режиме ядра драйверами, сбои которых «роняли» целые серверы. Эта же функциональность позволяет решать проблему соответствия драйверов на сервере и на клиенте. RD Connection Broker также позволяет создавать фермы без предварительной подготовки.

Шилдс: Сертификаты никогда не были простой темой, как и требования к безопасности при работе в демилитаризированной зоне. Тем не менее, обеспечение безопасности трафика RDS и использование его в Интернете требует опыта работы с тем и другим. Какие советы и приемы должен знать системный администратор, чтобы облегчить внедрение этой технологии, которое иногда оказывается исключительно сложным?

Гриффин: Избегайте использования встроенных самоподписанных сертификатов в «живой» среде. В настоящее время сертификаты обходятся дешево. Потратьте деньги и приобретите сертификат у надежного стороннего поставщика. Используйте поставщика, который участвует в программе Microsoft Root Certificate Program. Клиенты Windows 7 автоматически доверяют их сторонним сертификатам, что устраняет источник хлопот для системного администратора. Эта цепочка доверия () обновляется посредством службы обновления Windows.

Я встречала проблемы с использованием сертификатов Server Gated Cryptography (SGC) или когда SGC сертификаты являются частью цепочки сертификатов. Не покупайте эти сертификаты и не допускайте их в вашей цепочке сертификатов. Более подробную информацию вы найдете в двух темах на форуме Microsoft TechNet RDS: и .

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

Шилдс: Сейчас горячо обсуждается VDI и многие новые функции RDS ориентированы на применение VDI. Когда имеет смысл использовать VDI в малых вычислительных средах?

Гриффин: VDI позволяет решить ряд задач, например обеспечение доступа к приложениям, которые не работают в ОС Windows Server 2008 R2. Например, можно использовать VDI для создания пула виртуальных машин Windows XP для запуска несовместимых приложений. Можно также использовать VDI для замены физических настольных компьютеров и перемещения их на централизованную площадку. Это предоставляет системному администратору новые возможности, такие как мгновенные снимки — функция, которая позволяет вернуть виртуальную машину в предыдущее состояние. Это полезно, если что-то пойдет не так во время обновления или установки новой версии приложения. В отличие от физической, в виртуальной среде также проще и быстрее развертывать новые настольные компьютеры.

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

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

Шилдс: Является ли VDI прямой заменой RDS?

Гриффин: Это распространенное заблуждение. Нет, VDI не прямая замена виртуализации на основе сеансов, как думают люди о традиционной RDS. VDI является новым компонентом роли RDS.

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

Например, нет смысла предоставлять всем пользователям персональных виртуальных рабочих столов, если все что им нужно это доступ к приложению, которое прекрасно работает на RD Session Host.

Андерсон: Это определенно не прямая замена. VDI является частью RDS. RDS представляет собой полнофукнциональное решение для виртуализации настольных систем, в том числе виртуализации сеансов и настольных компьютеров, методов обнаружения учетных записей пользователей и доступа к WAN. Это означает, что VDI не всегда является очевидным выбором по сравнению с использованием сессий.

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

Шилдс: Как RDS может помочь с моими планами обновления Windows 7? Где эта технология подходит лучше всего?

Андерсон: Если вы виртуализируете Windows 7, вам не нужно обновлять оборудование, чтобы получить доступ ко всем возможностям Windows 7. Если вы планируете аппаратное обновление, но есть одно проблемное приложение, используйте для запуска этого приложения RemoteApp для Hyper-V. Приятной стороной RDS является то, что эти службы легко стирают грань между локальными и удаленными машинами. Вы можете интегрировать элементы разных машин в одной рабочей области.

Гриффин: Мы все наслышаны об этой истории перехода на Windows 7, когда имеется одно скверное, но необходимое приложение, которое отказывается работать на новой ОС. Возможно, оно работает только в среде Windows XP. Можно для решения этой проблемы использовать режим Windows 7 XP, но в этом случае вы имеете дело с двумя отдельными ОС. Некоторые аппаратные конфигурации могут не поддерживать режим XP или не обеспечивать удовлетворительной производительности.

Альтернативой является создание пула виртуальных машин с помощью VDI и предоставление пользователям доступа к среде Windows XP, когда это необходимо. Вы можете даже реализовать RemoteApp для Hyper-V и размыть границу между средой Windows 7 и Windows XP. В этом случае приложение Windows XP будет представлено как RemoteApp и интегрируется непосредственно в настольный компьютер с Windows 7. RemoteApp for Hyper-V интересный инструмент, но немногие знают о нем.

Между строк

Шилдс: Вы недавно выпустили книгу «Microsoft Windows Server 2008 R2 Remote Desktop Services Resource Kit».” Давайте поговорим о написании книги, уроках, которые вы при этом извлекли, и об изменениях в оценке тех или иных возможностей RDS.

Андерсон: Одним из интересных аспектов при работе с группой разработчиков Microsoft является то, что большинство из нас работает над определенными функциями. Так что мы знаем все об одной части RDS, но не знаем деталей работы других. Работа над такой книгой, как наша, отличный способ увидеть картину в целом. Я благодарен всем экспертам, которые предоставили нам подробную информацию о других функциях.

Вы используете много функций RDS при написании книги. Я не люблю работать в серверной комнате, если этого можно избежать, а RDS позволяет мне делать работу в другом месте. RD Gateway мой персональный фаворит. Я могу иметь доступ к нашей тестовой лаборатории из кафе, когда мне приходится работать по выходным.

Гриффин: Я думаю, что при написании книги я узнала больше о RDS, чем при обычном исследовании продукта. Нет ничего лучше неуклонно надвигающихся сроков, чтобы заставить погрузиться в глубину вопроса. Это может показаться смешным, но я думаю, что журналы событий мне стали нравиться намного больше. Они действительно помогли мне разобраться с собственными неполадками при тестировании продукта. Для тех, кто не знаком с журналами событий RDS, рекомендую изучить это семейство, которое вы найдете в папке Windows Logs/Applications and Services Logs/Microsoft/Windows/Terminal Services.

Как и Криста, я также питаю особые теплые чувства к RD Gateway. Безопасный удаленный доступ — фантастически приятная возможность и ее легко установить. Высокий уровень детализации в Connection Authorization Policies [RD CAPs] и Resource Authorization Policies [RD RAPs] предоставляет системному администратору полный контроль над тем, кто получает доступ к тем или иным ресурсам. Улучшенное взаимодействие RD Gateway с Network Access Protection делает его еще более надежным.

Шилдс: Теперь, когда вы закончили написание книги, можете ли вы сообщить нам совет или секрет «номер один», знание которого абсолютно точно облегчит повседневную жизнь администратора RDS?

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

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

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

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

Таким образом не стоит избегать чтения материалов по теме и ждать, что продукт внедрится сам по себе. Сделайте свое «домашнее задание». Разберитесь, что делает каждая служба роли RDS, и которые из них вам понадобятся для конкретных сценариев. RDS также зависит от других технологий. В процессе изучения RDS прочитайте о таких технологиях, как сертификаты SSL, балансировка сетевой нагрузки (Network Load Balancing), циклическое обслуживание DNS (Round Robin DNS), роуминг и обязательные профили. Если вы уже установили и запустили продукт, используйте журналы событий, которые помогут в устранении неполадок. Наконец, есть форум Microsoft TechNet RDS, который является превосходным ресурсом для получения бесплатной помощи.

Автор:
Последнее редактирование: 09 Сен 2011 в 03:00

EmailСсылкаКомментариев нет
Tags
Рубрика: Новости Microsoft
 08 Сен 2011 в 01:59 

В прошлый раз я познакомил вас с программными контрактами в том виде, в каком они реализованы в Microsoft .NET Framework 4. Программные контракты, известные как контракты кода (Code Contracts), позволяют выражать формальные условия, необходимые вашему коду для корректной работы. Code Contracts позволяют генерировать исключение, если метод не получил ожидаемые данные или если он вернул данные, не соответствующие постусловиям. Обзор пред- и постусловий прочитайте в моей статье по ссылке (msdn.microsoft.com/magazine/gg983479).

Code Contracts являются частью .NET Framework 4, но используют и некоторые средства Visual Studio 2010, например интеграцию с MSBuild и страницу свойств в окне Project Properties. Важно отметить, что простого написания пред- и постусловий недостаточно. Помимо этого, в каждом проекте, где применяются программные контракты, нужно включить соответствующие средства проверки в исполняющей среде. Это делается на странице Code Contracts свойств проекта в Visual Studio 2010.

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

Страница свойств Code Contracts

Следует ли вводить в действие пред- и постусловия контракта кода во всех сборках или только в отладочных? На практике все зависит от вашей концепции программного контракта. Является ли он частью проекта? Или это просто отладочный инструмент?

Если это часть проекта, тогда нет причин отключать контракты в рабочих сборках. Если же это просто элемент вашей методологии отладки, тогда контракты вам не нужны в режиме компиляции рабочих сборок (release mode).

Code Contracts в .NET Framework являются не более чем частью инфраструктуры и не включены ни в один .NET-язык. В результате этого контракты проще конфигурировать индивидуально для каждой сборки в рамках вашего проекта. Реализация программных контрактов в .NET Framework оставляет на ваш выбор, где и когда имеет смысл использовать контракты.

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

Рис. 1. Страница свойств Code Contracts в Visual Studio 2010

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

Проверки в период выполнения

Чтобы включить Code Contracts, вы должны установить флажок Perform Runtime Contract Checking. Без него любые инструкции контракта в исходном коде не дадут никакого эффекта (кроме Contract.Assert и Contract.Assume в любой сборке, где определен символ DEBUG). Данный элемент управления контролирует, будет ли запускаться утилита Rewriter в конце каждого этапа компиляции. Rewriter — это внешний инструмент, который выполняет постобработку программных контрактов и модифицирует MSIL-код, помещая в нужные места проверки предусловий, постусловий и инвариантов.

Однако заметьте: в случае предусловия, подобного показанному ниже, вы получите ошибку проверки в период выполнения, если оставите Rewriter отключенным:

Contract.Requires<TException>(condition)

Сообщение об ошибке приведено на рис. 2.

Рис. 2. Данный код требует проверки контракта в период выполнения

Чтобы в деталях увидеть, как работает проверка в период выполнения, рассмотрим следующий код:

public Int32 Sum(Int32 x, Int32 y) { // Проверка входных значений ValidateOperands(x, y); ValidateResult(); // Выполняем операцию if (x == y) return 2 * x; return x + y;}

Детали контракта хранятся в методах ValidateXxx с использованием атрибутов ContractAbbreviator, как обсуждалось в прошлой статье. Вот исходный код для методов ValidateXxx:

[ContractAbbreviator]private void ValidateOperands(Int32 x, Int32 y) { Contract.Requires(x >= 0 && y >= 0);}[ContractAbbreviator]private void ValidateResult() { Contract.Ensures(Contract.Result<Int32>() >= 0);}

Если вы используете Contract.Requires вместо Contract.Requires<TException>, то избавите себя от ошибки, показанной на рис. 2, на случай отключения Rewriter в одной из сборок. Сообщение на рис. 2 связано с внутренней реализацией Contract.Requires, который выглядит так:

[Conditional("CONTRACTS_FULL")]public static void Requires(bool condition, string userMessage){ AssertMustUseRewriter( ContractFailureKind.Precondition, "Requires");}public static void Requires<TException>(bool condition) where TException: Exception { AssertMustUseRewriter( ContractFailureKind.Precondition, "Requires<TException>");}

Атрибут Conditional указывает компиляторам, что такой вызов метода следует игнорировать, если только не определен символ CONTRACTS_FULL. Этот символ определяется, только когда вы включаете параметр Perform Runtime Contract Checking. Поскольку Contract.Requires<TException> определяется без условия и у него нет соответствующего атрибута, осуществляется проверка Rewriter с неудачным результатом, если проверка контрактов в период выполнения отключена.

Теперь рассмотрим эффект от применения Rewriter к предыдущему коду. Вы можете легко проверить то, что я говорю, просто установив точки прерывания и нажав Ctrl+F11 для вызова представления Disassembly в Visual Studio 2010. Содержимое этого представления, когда вы пошагово проходите метод Sum, компилируемый без включенной проверки контрактов в период выполнения, показано на рис. 3.

Рис. 3. Представление Disassembly при выключенной проверке контрактов в период выполнения

Когда вы включаете проверку в период выполнения, утилита Rewriter изменяет MSIL-код. Если вы будете пошагово проходить тот же код с включенными Code Contracts, то увидите нечто вроде, показанного на рис. 4.

Рис. 4. Постусловия, проверяемые после выражения return

Заметная разница в том, что ValidateResult вызывается непосредственно перед выходом из метода Sum и после выражения return. Не нужно быть гуру в MSIL-коде, чтобы увидеть, что происходит в коде на рис. 4. Операнды проверяются до того, как метод начнет выполнять самое первое предусловие. Код, содержащий постусловия, перемещается в самый конец метода, и MSIL-код для заключительного выражения return просто относится к нему. Более интересно первое выражение return — то, которое в методе Sum реализует сокращение: теперь просто происходит переход к адресу, с которого начинается ValidateResult:


return 2 * x;
00000054  mov         eax,dword ptr [ebp-8]
00000057  add         eax,eax
00000059  mov         dword ptr [ebp-0Ch],eax
0000005c  nop
0000005d  jmp         0000006B

ValidateResult();
0000006b  push        dword ptr ds:[02C32098h]

Вернемся к рис. 1 и обратим внимание на раскрывающийся список рядом с флажком Perform Runtime Contract Checking. Этот список позволяет указывать количество программных контрактов, которые вы хотите разрешить: Full, Pre and Post, Preconditions, ReleaseRequires и None.

Full означает, что поддерживаются все типы программных контрактов, а None — что ни один из них не принимается во внимание. Pre and Post исключает инварианты. Уровень Preconditions также исключает выражения Ensures.

ReleaseRequires не поддерживает метод Contract.Requires и позволяет лишь указывать предусловия, используя Requires<TException> или унаследованный формат If-Then-Throw.

На странице свойство можно включать или отключать проверки в период выполнения индивидуально для каждого проекта, но как быть, если нужно отключить такие проверки только в нескольких разделах кода? В этом случае вы можете просто отключать проверки программным способом, используя атрибут ContractRuntimeIgnored. Однако в более свежем выпуске (1.4.40307.0) был добавлен новый параметр Skip Quantifiers, позволяющий не выполнять любые контракты, которые содержат ссылки на Contract.ForAll или Contract.Exists.

Вы можете применять этот атрибут к членам, используемым в выражениях Contract. Если член дополняется этим атрибутом, то все выражение Contract, в котором он содержится, исключается из проверки в период выполнения. Этот атрибут не распознается в таких методах Contract, как Assert и Assume.

Assembly Mode

В свойствах Code Contracts также можно настроить Assembly Mode для контрактов. Этот параметр относится к тому, как вы намерены выполнять проверку аргументов. Допустимы два значения: Standard Contract Requires и Contract Reference Assembly. Значения Assembly Mode помогают утилитам вроде Rewriter в тонкой настройке кода и выдаче необходимых предупреждений. Допустим, вы используете Assembly Mode, чтобы объявить, что вы собираетесь задействовать Code Contracts для проверки параметров. Выбирая значение Assembly Mode, вы должны понимать, что они вводят пару простых правил, которые нужно соблюдать, а иначе вы получите ошибку при компиляции.

Assembly Mode следует установить в Standard Contract Requires, если вы используете методы Requires и Requires<T> для проверки аргументов своих методов. А если вы применяете в качестве предусловий любые выражения If-Then-Throw, выберите Custom Parameter Validation. При значении, отличном от Custom Parameter Validation, выражение If-Then-Throw будет интерпретироваться как Requires<T>. Комбинация Custom Parameter Validation и любой формы выражений Requires приведет к ошибке при компиляции.

В чем отличие выражений Requires и If-Then-Throw? Выражение If-Then-Throw всегда генерирует заданное вами исключение, если проверка заканчивается неудачей. В этом отношении оно отличается от Requires, но аналогично Requires<T>. Обычное выражение If-Then-Throw также не обнаруживается утилитами контрактов (Rewriter и Static Checker), если только вы не ставите за ним вызов EndContractBlock. При использовании EndContractBlock должен быть последним методом контракта кода, который вы вызываете в своем методе. Никакие другие вызовы Code Contracts после EndContractBlock недопустимы:

if (y == 0) throw new ArgumentException();Contract.EndContractBlock();

In addition, Requires statements are automatically inherited. An If-Then-Throw statement is not inherited unless you also use EndContractBlock. In legacy mode, If-Then-Throw Contracts are not inherited. Instead you must manually do the Contract inheritance. The tools will try to warn if they do not detect that the preconditions are repeated in overrides and interface implementations.

Наконец, обратите внимание, что атрибуты ContractAbbreviator не могут содержать выражения If-Then-Throw, но вы можете использовать с этой целью верификаторы контракта (Contract validators). Атрибуты ContractAbbreviator могут включать лишь обычные выражения Contract для проверки аргументов.

Другие параметры

На странице свойств Code Contracts, если вы выберете параметр Perform Runtime Contract Checking, можно будет включить некоторые другие полезные параметры.

Если вы установите параметр Assert on Contract Failure, любые нарушения контракта будут приводить в утверждение (assert), где описывается контекст ошибки. Вы увидите сообщение, аналогичное показанному на рис. 2, и получите возможность выбора нескольких вариантов дальнейших действий. Например, вы можете попытаться снова подключить отладчик, отменить выполнение приложения или просто проигнорировать ошибку и продолжить.

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

Contract.ContractFailed += CentralizedErrorHandler;

Here’s some code that illustrates the handler:

static void CentralizedErrorHandler( Object sender, ContractFailedEventArgs e) { Console.WriteLine("{0}: {1}; {2}", e. FailureKind, e.Condition, e.Message); e.SetHandled();}

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

Параметр Only Public Surface Contract относится к тому, где бы вы хотели вводить в действие Code Contracts: в каждом методе или только в открытых. Если вы установите флажок этого параметра, тогда Rewriter будет игнорировать выражения Code Contract в закрытых и защищенных членах и обрабатывать только контракты в открытых членах.

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

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

Параметр Call-site Requires Checking обеспечивает еще одну оптимизацию. Допустим, вы пишете библиотеку, которая будет использоваться модулями в других сборках. По соображениям производительности вы отключаете проверку контрактов в период выполнения. Однако, если вы также создаете ссылочную сборку контрактов (Contract reference assembly), вы даете возможность вызывающему коду проверять контракт для каждого вызываемого метода.

Ссылочная сборка контрактов может существовать для каждой сборки, в которой находятся классы, использующие Code Contracts. Она содержит только открытый интерфейс родительской сборки с выражениями контрактов (Contract statements) — никакого другого кода в ней нет. Создание такой сборки можно контролировать со страницы свойств Code Contracts.

Любой код, который будет вызывать вашу библиотеку, может ссылаться на вашу ссылочную сборку контрактов и автоматически импортировать контракты — для этого достаточно включить параметр Call-site Requires Checking. При обработке вызывающего кода утилита Rewriter импортирует контракт для каждого вызываемого метода во внешнюю сборку, которая помещается туда же, куда и ссылочная сборка контрактов. В этом случае контракт проверяется на стороне вызывающего кода, и такой вариант является оптимальным, чтобы включать/отключать проверки для кода, который вы не можете контролировать напрямую.

Заключение

Code Contracts — такая область .NET Framework, которую стоит исследовать гораздо детальнее. В этой статье я лишь поверхностно затронул некоторые конфигурационные параметры и даже не добрался до использования Static Checker. Code Contracts помогают лучше проектировать приложения и писать более ясный код.

Чтобы узнать больше о Code Contracts, прочитайте рубрику «CLR с изнанки» за июнь 2009 г. (msdn.microsoft.com/magazine/ee236408) и посетите сайт DevLabs Code Contracts (msdn.microsoft.com/devlabs/dd491992).

Кроме того, массу интересной информации о разработке с применением Code Contracts можно найти на сайте Microsoft Research Code Contracts (research.microsoft.com/projects/contracts).

Автор:
Последнее редактирование: 08 Сен 2011 в 01:59

EmailСсылкаКомментариев нет
Tags
Рубрика: Новости Microsoft
 04 Сен 2011 в 09:00 

В начале января Дэвид Браун (David Browne) и я работали над решением для балансировки нагрузки на внутренние точки сервиса в рабочих ролях Windows Azure. Как правило, конечные точки сервиса в рабочих ролях публикуются, чтобы средство балансировки нагрузки мог распределять вызовы между экземплярами. Однако заказчику, с которым мы работали, требовалось, чтобы конечные точки не были общедоступными. Кроме того, заказчик не хотел мириться с задержками при выполнении некоторых типов операций в очереди. Как удовлетворить эти требования?

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

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

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

Каждая из этих стратегий имеет свои плюсы и минусы.

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

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

Централизованное управление

Типичное средство балансировки нагрузки, которое принимает информацию о работоспособности узлов и распределяет запросы к сервису на основе этой информации, использует централизованное управление. Оно собирает информацию об узлах, сведения о назначениях и любые данные о текущей работе, и направляет запросы, отправленные на Virtual IP (VIP), определенному узлу.

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


Увеличить

Рис. 1. Централизованное управление

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

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

В некоторых роботизированных и матричных системах (robotic and matrix systems) рабочие узлы сами выбирают основной контроллер и, если он перестает отвечать, они просто выбирают новый основной контроллер. Хотя это хорошая архитектура, поскольку она сочетает в себе централизованное и кооперативной управление, она также сильно усложняет реализацию механизма распределения нагрузки.

Кооперативное управление

Любой человек с опытом управления, которому приходилось заставлять кого-либо что-то сделать, знает, насколько это непросто — добиться того, чтобы этот некто действительно выполнил свою работу. Гораздо рациональнее прямо спросить человека, есть ли у него время сделать то-то и то-то (конечно, при условии, что он может оценить необходимые для этого усилия). Такой модели я и решил последовать.

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


Увеличить

Рис. 2. Кооперативное управление


Увеличить

Рис. 3. Перераспределение нагрузки на другой узел

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

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

Прототип

Прототип делает следующее:

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

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

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

Рис. 4. Добавление внутренней конечной точки сервиса к рабочей роли

Я также пометил рабочие роли в развернутой системе как PrivateServices. Используя API объекта RoleEnvironment и эту метку, можно легко получать узлы:

if (_CurrentUriString == null) { System.Collections.ObjectModel.ReadOnlyCollection<RoleInstance> ServiceInstances = null; System.Collections.ObjectModel.ReadOnlyCollection<RoleInstance> WebInstances = null; ServiceInstances = RoleEnvironment.Roles["PrivateServices"].Instances; WebInstances = RoleEnvironment.Roles["ServiceBalancingWeb"].Instances;

Чтобы найти начальный узел для проверки доступности, я использую порядковый номер узла. Если веб-ролей больше, чем рабочих, то вместо порядкового номера применяется функция вычисления по модулю. После этого я могу выполнять перебор и проверку конечных точек (рис. 5).

Рис. 5. Проверка конечных точек

while (!found && !Abort) { string testuri = ServiceInstances[idxSvcInstance].InstanceEndpoints[ "EndPointServices"].IPEndpoint.ToString(); found = CheckAvailability(testuri); if (found) { ServiceUriString = testuri; } else { idxSvcInstance++; if (idxSvcInstance >= ServiceInstances.Count) { idxSvcInstance = 0; } loopCounter++; if (loopCounter == ServiceInstances.Count) { Abort = true; } }}

Обратите внимание на вызов функции CheckAvailability (рис. 6). В этой функции я создаю привязку, используя None в качестве режима защиты, так как конечная точка является исключительно внутренней. Я создаю экземпляр клиента сервиса, задаю разумный интервал ожидания и возвращаю это значение.

Рис. 6. Функция CheckAvailability

static public bool CheckAvailability(string uri) { bool retval = true; Binding binding = new NetTcpBinding(SecurityMode.None); EndPointServicesRef.EndPointServicesClient endpointsvc = new EndPointServicesRef.EndPointServicesClient(binding, new EndpointAddress(@”net.tcp://” + uri)); endpointsvc.InnerChannel.OperationTimeout = new System.TimeSpan(0,0,0,0, 5000); try { retval = endpointsvc.IsAvailable(); } catch (Exception ex) { // Todo: handle exception retval = false; } return retval;}

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

Другой способ создания списка узлов — перебор всех узлов с идентификацией порядковой позиции текущего узла, выполняющего код, в списке или простое упорядочение их по последнему октету IP-адресов. Любой из этих двух способов был бы чуть более «защищенным от дурака», но в данном конкретном примере я просто использовал идентификатор экземпляра.

Еще одна закавыка связана с тем, что структура идентификатора в реальной развернутой системе отличается от таковой в инфраструктуре разработки, что вынуждает учитывать это в коде разбора идентификаторов:

string[] IdArray = RoleEnvironment.CurrentRoleInstance.Id.Split('.');int idxWebInstance = 0;if (!int.TryParse((IdArray[IdArray.Length - 1]), out idxWebInstance)) { IdArray = RoleEnvironment.CurrentRoleInstance.Id.Split('_'); idxWebInstance = int.Parse((IdArray[IdArray.Length - 1]));}

Этот код должен вернуть IP-адрес доступной конечной точки, который можно кешировать в статической переменной. Затем я устанавливаю таймер. Когда срабатывает событие таймера, я обнуляю адрес конечной точки, заставляя код снова искать допустимую конечную точку для использования сервисами:

System.Timers.Timer invalidateTimer = new System.Timers.Timer(5000);invalidateTimer.Elapsed += (sender, e) => _CurrentUriString = null;invalidateTimer.Start();

Этот код должен вернуть IP-адрес доступной конечной точки, который можно кешировать в статической переменной. Затем я устанавливаю таймер. Когда срабатывает событие таймера, я обнуляю адрес конечной точки, заставляя код снова искать допустимую конечную точку для использования сервисами:

Запуск демонстрации

Теперь я модифицирую исходную страницу и ее отделенный код просто для того, чтобы она показывала узел, с которым у нее установлена связь. Я также добавлю кнопку для отключения узла. Обе части кода весьма тривиальны. Кроме того, я добавлю в UI метку и командную кнопку. В метке я буду выводить идентификатор назначенной конечной точки, а кнопка позволит мне отключать узел и наблюдать, как все веб-роли остаются сопоставленными с единственной конечной точкой до тех пор, пока отключенный узел вновь не будет включен. В обработчик события загрузки страницы (в отделенном коде) я вставлю несколько строк для получения конечной точки (рис. 7).

Рис. 7. Код демонстрационной страницы

protected void Page_Load(object sender, EventArgs e) { string UriString = EndpointManager.GetEndPoint(); LastUri=UriString; Binding binding = new NetTcpBinding(SecurityMode.None); EndPointServicesRef.EndPointServicesClient endpointsvc = new EndPointServicesRef.EndPointServicesClient(binding, new EndpointAddress(@”net.tcp://” + UriString)); lblMessage.Text = “WebInstacne ID: ” + RoleEnvironment.CurrentRoleInstance.Id.ToString() + ” is Calling Service @ ” + UriString + ” & IsAvailable = ” + endpointsvc.IsAvailable().ToString(); cmdDisable.Enabled=true;}

Поскольку я лишь хочу продемонстрировать кооперативную балансировку, я не стал реализовать другой метод или интерфейс сервиса, а просто повторно использовал метод IsAvailable.

Прототип приложения в действии показан на рис. 8. Сначала вы видите идентификатор (в данном случае в инфраструктуре разработки), IP-адрес и доступность узла. Обновление страницы приводит к выдаче запроса на балансировку. При щелчке кнопки отключения запускается небольшой кусок кода, настраивающий вызов DisableNode для текущей конечной точки:

protected void cmdDisable_Click(object sender, EventArgs e) { Binding binding = new NetTcpBinding(SecurityMode.None); EndPointServicesRef.EndPointServicesClient endpointsvc = new EndPointServicesRef.EndPointServicesClient(binding, new EndpointAddress(@”net.tcp://” + LastUri)); endpointsvc.DisableNode();}

Рис. 8. Демонстрационное приложение в действии

Метод DisableNode просто присваивает булевы значения паре переменных, а затем настраивает таймер так, чтобы при его срабатывании вызывался метод EnableNode. Значение таймера немного превышает срок устаревания кешированной конечной точки, чтобы это было проще продемонстрировать в тестовом прогоне:

public void DisableNode() { AvailabilityState.Enabled = false; AvailabilityState.Available = false; System.Timers.Timer invalidateTimer = new System.Timers.Timer(20000); invalidateTimer.Elapsed += (sender, e) => EnableNode(); invalidateTimer.Start();}

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

За рамками примера

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

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

Чтобы все это работало в производственной среде, нужно учитывать некоторые вещи, перечисленные ниже.

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

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

А теперь пара слов о подходе Дэвида. Он формирует матрицу между доменами Fault и Upgrade в попытке обеспечить соответствие доступности вызывающего и конечной точки, отдавая предпочтение конечным точкам в тех же доменах. Я считаю это отличной идеей. Комбинация наших реализаций могла бы гарантировать, что ваша веб-роль по возможности обрабатывается рабочей ролью в соответствии с тем же соглашением об уровне обслуживания (SLA), что и сервис, но в случае недоступности выделенных рабочих ролей можно было бы перераспределять нагрузку на любой другой узел.

Заключение

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

Автор:
Последнее редактирование: 04 Сен 2011 в 09:00

EmailСсылкаКомментариев нет
Tags
Рубрика: Новости Microsoft

 Последние 50 записей
 Назад
Изменить тему...
  • Пользователей » 80
  • Записей/Страниц » 4,624
  • Комментариев » 1
Изменить тему...
  • ПустотаПустота « По умолчанию
  • ЖизньЖизнь
  • ЗемляЗемля
  • ВетерВетер
  • ВодаВода
  • ОгоньОгонь
  • СветСвет

Карта сайта



    Подстраницы отсутствуют.