Release it! Проектирование и дизайн ПО для тех, кому не всё равно - Майкл Нейгард - E-Book

Release it! Проектирование и дизайн ПО для тех, кому не всё равно E-Book

Майкл Нейгард

0,0

Beschreibung

Не важно, каким инструментом вы пользуетесь для программной разработки — Java, .NET или Ruby on Rails. Написание кода — это еще только полдела. Готовы ли вы к внезапному наплыву ботов на ваш сайт? Предусмотрена ли в вашем ПО «защита от дурака»? Правильно ли вы понимаете юзабилити? Майкл Нейгард утверждает, что большинство проблем в программных продуктах были заложены в них еще на стадии дизайна и проектирования. Вы можете двигаться к идеалу сами — методом проб и ошибок, а можете использовать опыт автора. В этой книге вы найдете множество шаблонов проектирования, помогающих избежать критических ситуаций и не меньшее количество антишаблонов, иллюстрирующих неправильные подходы с подробным анализом возможных последствий. Любой разработчик, имеющий опыт многопоточного программирования, легко разберется в примерах на Java, которые подробно поясняются и комментируются. Стабильность, безопасность и дружественный интерфейс — вот три важнейших слагаемых успеха вашего программного продукта. Если в ваши планы не входит в течение последующих лет отвечать на недовольные письма пользователей, выслушивать критику заказчиков и постоянно латать дыры, устраняя возникающие баги, то прежде чем выпустить финальный релиз, прочтите эту книгу.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern
Kindle™-E-Readern
(für ausgewählte Pakete)

Seitenzahl: 461

Veröffentlichungsjahr: 2024

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Переводчики И. Рузмайкина, И. Рузмайкина

М. Нейгард

Release it! Проектирование и дизайн ПО для тех, кому не всё равно. — СПб.: Питер, 2024.

ISBN 978-5-496-01611-7

© ООО Издательство "Питер", 2024

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

Оглавление

Предисловие
1. Введение
Часть I. Стабильность
2. Исключение, помешавшее работе авиакомпании
3. Понятие стабильности
4. Антипаттерны стабильности
5. Паттерны стабильности
6. Заключение по теме стабильности
Часть II. Вычислительная мощность
7. Затоптаны клиентами
8. Понятие вычислительной мощности
9. Антипаттерны вычислительной мощности
10. Паттерны вычислительной мощности
Часть III. Общие вопросы проектирования
11. Организация сети
12. Безопасность
13. Доступность
14. Администрирование
15. Заключение по теме проектирования
Часть IV. Эксплуатация
16. Феноменальная мощь и маленькое жизненное пространство
17. Прозрачность
18. Адаптация
Список литературы

Предисловие

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

Или?

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

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

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

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

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

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

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

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

Для кого предназначена эта книга?

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

Структура книги

Книга разделена на четыре части, каждая из которых начинается с практического примера. В части I показано, как сохранить активность системы, обеспечив ее безотказную работу. Несмотря на обещанную надежность за счет избыточности, распределенные системы демонстрируют доступность, выражаемую скорее «двумя восьмерками», чем желанными «пятью девятками»1.

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

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

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

В части IV существование системы рассматривается в рамках общей инфор­мационной экосистемы. Зачастую производственные системы напоминают кота Шрёдингера — они заперты в коробке без возможности наблюдать за их ­состоянием. Здоровью экосистемы это не способствует. Отсутствие инфор­мации делает невозможными целенаправленные улучшения2. В главе 17 обсуждаются факторы, технологии и процессы, которые следует изучать у работающих систем (это единственная ситуация, когда можно изучать определенные вещи). Выяснив показатели работоспособности и производительности конкретной системы, вы сможете действовать на основе этих данных. Более того, подобный подход является обязательным — действия нужно предпринимать только в свете полученной информации. Иногда это проще сказать, чем сделать, и в главе 18 мы рассмотрим препятствия на пути к изменениям и способы их уменьшения и преодоления.

Анализ примеров

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

Благодарности

Идея книги появилась после моего выступления перед группой Object Technology. Поэтому я должен поблагодарить Кайла Ларсона (Kyle Larson) и Клайда Каттинга (Clyde Cutting) — благодаря им я вызвался сделать доклад, который они благосклонно приняли. Неоценимую поддержку мне оказали Том (Tom Poppendieck) и Мэри Поппендик (Mary Poppendieck), авторы двух фантастических книг о «бережливой разработке»3. Это они убедили меня, что я полностью готов к написанию собственной книги. Отдельное спасибо моему другу и коллеге Диону Стюарту (Dion Stewart), который постоянно оценивал написанные фрагменты.

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

1 То есть 88, а не 99,999% времени безотказной работы.

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

3 Книги Lean Software Development и Implementing Lean Software Development. Последняя была переведена на русский язык издательством «Вильямс» и вышла под названием «Бережливое производство программного обеспечения. От идеи до прибыли».

1. Введение

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

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

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

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

1.1. Правильный выбор цели

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

Можно ли быть уверенным, что прошедшая контроль качества система готова к ис­пользованию? Простое прохождение тестов ничего не говорит о стабильности системы в следующие три, а то и десять лет ее жизни. В результате может родиться Toyota Camry от программирования, способная на тысячи часов безотказной работы. А может получиться Chevy Vega, за несколько лет прогнивающий до дыр, или Ford Pinto, норовящий взорваться при каждом ударе в задний бампер. За несколько дней или недель тестирования невозможно понять, что принесут следующие несколько лет эксплуатации.

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

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

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

1.2. Важность интуиции

Решения, принятые на ранней стадии, оказывают сильное влияние на конечный вид­ системы. Чем раньше принимается решение, тем сложнее потом от него отказаться. От того, как вы определите границы системы и как разобьете ее на подсистемы, зависит­ структура рабочей группы, объем финансирования, структура сопровожде­ния программного продукта и даже хронометраж работ. Распределение обязанностей­ внутри группы является первым наброском архитектуры (см. закон Конвея в раз­де­ле 7.2). Ирония состоит в том, что на ранней стадии решения принимаются в ус­ловиях минимальной информированности. Группа еще понятия не имеет о конечной структуре будущего программного продукта, но уже должна принимать необратимые решения.

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

1.3. Качество жизни

Версия 1.0 — это начало жизни вашей программы, но не конец проекта. И качество вашей жизни после предоставления версии 1.0 заказчикам зависит от решений, которые вы приняли в далеком прошлом.

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

1.4. Охват проблемы

«Кризис программного обеспечения» начался более тридцати лет назад. Золото­владельцы считают, что программное обеспечение по-прежнему стоит слишком дорого. (Этой теме посвящена книга Тома де Марко Why Does Software Cost ­So Much?) А по мнению целевыхдоноров, его разработка занимает слишком ­много времени, несмотря на то что теперь она измеряется месяцами, а не годами. Судя по всему, достигнутая за последние тридцать лет продуктивность иллюзорна.

Эти термины появились в сообществе гибкого проектирования. Золотовладельцем (gold owner) называют того, кто платит за программное обеспечение, а целевым донором (goal donor) — того, чьи запросы это программное обеспечение призвано удовлетворять. Как правило, это два разных человека.

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

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

Возросли и требования к сроку эксплуатации. Если раньше «пять девяток» (99,999%) требовались только центральным серверам и обслуживающему их персоналу, то сейчас даже заурядным коммерческим сайтам нужна доступность 24 часа в сутки и 365 дней в году5. Очевидно, мы резко шагнули вперед в масштабах создаваемого программного обеспечения, но одновременно с этим появились новые варианты отказов, обстановка стала более неблагоприятной, а терпимость к дефектам снизилась.

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

1.5. Миллионом больше, миллионом меньше

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

За год разница между 98% и 99,99% времени безотказной работы может дойти до 17 миллионов долларов6. Представьте себе эти 17 миллионов, которые к итоговой сумме можно было бы добавить, всего лишь улучшив проект!

В лихорадке разработки можно легко принять решение, которое оптимизирует затраты на проектирование за счет эксплуатационных расходов. С точки зрения команды разработчиков, которой выделен фиксированный бюджет и указана фиксированная дата сдачи проекта, это имеет смысл. А вот для заказавшей программное обеспечение организации это плохой выбор. Эксплуатируются системы куда дольше, чем разрабатываются, — по крайней мере, те, которыми не прекратили пользоваться. Значит, бессмысленно избегать разовых затрат, вводя в уравнение постоянные расходы на эксплуатацию. Более того, с финансовой точки зрения куда лучше противоположный подход. Потратив 5000 долларов на автоматизированную систему сборки и выпуска, позволяющую избежать простоя при переходе к новой версии, можно сэкономить 200 000 долларов7. Я думаю,­ что большинство финансовых директоров без колебаний санкциони­руют расходы, способные принести 4000% прибыли на инвестированный капитал.

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

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

1.6. Прагматичная архитектура

Термин архитектура применяется к двум разным видам деятельности. Один из них касается более высоких уровней абстракции, способствующих упрощению перехода с одной платформы на другую и большей независимости от аппаратного обеспечения и сетевого окружения. В крайних формах это превращается в «башню из слоновой кости» — населенную надменными гуру чистую комнату в стиле Кубрика, все стены которой украшены ящиками и стрелками. Оттуда исходят указания непрерывно работающим кодерам: «Используйте контейнерно-управляемое сохранение EJB-состояния!», «Для конструирования всех пользовательских интерфейсов используйте JSF!», «Все, что вам нужно, было нужно и когда-либо будет нужно, есть в Oracle!». Если в процессе написания кода по «корпоративным стандартам» вы когда-либо заикнетесь, что другая технология позволяет достичь нужного результата более простым путем, то станете жертвой архитектора, оторванного от реальности. И могу руку дать на отсечение, что архитектор, не слушающий кодеров из собственной рабочей группы, не станет прислушиваться и к пользователям. Думаю, результаты такого подхода вам доводилось видеть — это пользователи, которые бурно радуются падению системы, так как это позволяет им некоторое время от нее отдохнуть.

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

Архитектору из башни больше всего нравится представлять конечный результат в виде идеальных позванивающих кристаллов, в то время как прагматичный архитектор непрерывно учитывает динамику изменений. «Как выполнить раз­вертывание, не заставляя всех перезагружаться?», «Какие параметры нам следует собрать, как они будут анализироваться?», «Какая часть системы больше всего нуждается в усовершенствованиях?». Выстроенная архитектором из башни система не допускает доработки; все ее части строго пригнаны друг к другу и адаптированы к своим задачам. У прагматичного архитектора все компоненты системы хорошо справляются с текущими нагрузками, и при этом он знает, что нужно поменять, если со временем нагрузка распределится по-иному.

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

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

5 Такая формулировка мне никогда не нравилась. Как инженер, я бы больше оценил выражение «24 на 365» или «24 на 7 и на 52».

6 Средняя стоимость 100 000 долларов за час простоя имеет место у поставщиков первого порядка.

7 Предполагается 10 000 долларов на версию (оплата работы плюс стоимость запланированных простоев), четыре версии в год в течение пяти лет. Большинство компаний предпочитают более пяти версий в год, но я консерватор.

Часть I. Стабильность

3. Понятие стабильности

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

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

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

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

Эмпирическое правило гласит, что привлечение одного клиента обходится интернет-магазину в 25–50 долларов. Представьте, что он теряет 10% из 5000 уникальных посетителей в час. Это означает, что на привлечение клиентов было впустую потрачено 12 500–25 000 долларов.

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

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

Реализация стабильного проекта стоит столько же, сколько реализация нестабильного.

3.1. Определение стабильности

Прежде всего я хотел бы дать определение некоторым терминам. Транзакцией (transaction) называется выполненная системой абстрактная часть работы. Это не имеет отношения к транзакциям в базах данных. Одна часть работы может включать в себя несколько транзакций в базе данных. Например, в интернет-магазинах распространен тип транзакции «заказ, сделанный клиентом». Такая транзакция занимает несколько страниц, зачастую включая в себя интеграцию с внешними сервисами, такими как проверка кредитных карт. Системы создаются именно для проведения транзакций. Если система обрабатывает транзакции только одного типа, она называется выделенной. Смешанной нагрузкой (mixed workload) называется комбинация различных типов транзакций, обрабатываемых системой.

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

Отказоустойчивая система продолжает обрабатывать транзакции, даже когда импульсные помехи, постоянная нагрузка или неисправность отдельных узлов наруша­­ют нормальный процесс обработки. Именно такое поведение большинство подразу­ме­ва­ет под словом стабильность (stability). Серверы и приложения не только продолжают функционировать, но и дают пользователю возможность закончить работу.

Термины импульс (impulse) и нагрузка (stress) пришли из машиностроения. Импульс подразумевает резкую встряску системы. Как будто по ней ударили молотком. Нагрузка же, наоборот, представляет собой силу, прикладываемую к системе длительное время.

Огромное число посещений страницы с информацией об игровой приставке Xbox 360, случившееся после слухов о распродаже, вызвало импульс. Десять тысяч новых сеансов в течение одной минуты выдержать трудно. Мощный всплеск посещаемости сайта является импульсом. Выгрузка в очередь 12 миллионов сообщений ровно в полночь 21 ноября — это тоже импульс. Подобные вещи ломают систему в мгновение ока.

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

продление срока службы

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

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

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

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

Каким же образом выяснить ошибки данного типа? Единственное, что вы можете сделать до того, как они причинят вам проблемы при эксплуатации, — это собственноручно провести испытания на долговечность. По возможности выделите для разработчика отдельную машину. Запустите на ней JMeter, Marathon или другой инструмент нагрузочного тестирования. Не делайте ничего экстремального, просто все время выполняйте запросы. Также обязательно сделайте несколько часов временем бездействия сценариев, имитируя отсутствие нагрузки ночью. Это позволит протестировать таймауты пула соединений и фаервола.

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

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

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

Проведите тестирование срока службы. Это единственный способ обнаружить ошибки, проявляющиеся со временем.

3.2. Режимы отказов

К катастрофическому отказу могут привести как внезапные импульсы, так и чрезмерная нагрузка. Но в любом случае какой-то компонент системы начинает отказывать раньше, чем все остальное. В своей книге Inviting Disaster Джеймс Р. Чайлз называет это трещинами (cracks) системы. Он сравнивает сложную систему, находящуюся на грани отказа, со стальной пластинкой с микроскопической трещиной. Под нагрузкой эта трещина может начать расти быстрее и быстрее. В конечном счете скорость распространения дефекта превысит скорость звука, и металл с резким треском сломается. Фактор, послуживший триггером, а также способ распространения дефекта в системе вместе с результатом повреждения называют режимом отказа (failure mode).

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

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

3.3. Распространение трещин

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

Так как пул был настроен таким образом, что блокировал потоки с запросами в отсутствие ресурсов, в конечном счете он заблокировал все обрабатывающие запросы потоки. (Это произошло независимо друг от друга на всех экземплярах серверов приложений.) Можно было настроить пул на открытие новых соединений в случае его истощения или после проверки всех соединений на блокировку вызывающих потоков на ограниченное время, а не навсегда. Любой из этих вариантов остановил бы распространение трещины. На следующем уровне проблема с единственным вызовом в программе CF привела к сбою вызывающих приложений на остальных хостах. Так как службы для CF построены на технологии Enterprise JavaBeans (EJB), для них используется RMI. А по умолчанию вызовы этого интерфейса лишены таймаута. Другими словами, вызывающие потоки блокировались в ожидании, пока будут прочитаны их ответы от EJB-компонентов программы CF. Первые двадцать потоков для каждого экземпляра получили исключения, точнее SQLException, в оболочке исключения InvocationTargetException, которое в свою очередь располагалось в оболочке исключения RemoteException. И после этого вызовы начали блокироваться.

Можно было написать клиент с таймаутами на RMI-сокетах14. В определенный момент времени можно было принять решение строить веб-службы для CF на основе HTTP, а не EJB. После этого осталось бы задать на клиенте таймаут на HTTP-запросы15. Еще вызовы клиентов можно было настроить таким образом, чтобы блокированные потоки отбрасывались, вместо того чтобы заставлять обрабатывающий запрос поток делать внешний интеграционный вызов. Ничего из этого не было сделано, и сбой в программе CF распространился на все использующие эту программу системы.

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

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

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

3.4. Цепочка отказов

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

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

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

Одним из способов подготовки ко всем возможным отказам является учет всех внешних вызовов, всех операций ввода-вывода, всех использований ресурсов и всех ожидаемых результатов в поиске ответа на вопрос, какими способами может возникнуть сбой? Вспомните все возможные типы импульсов и напряжений:

Что, если не удастся установить начальное соединение?

Что, если установка соединения займет десять минут?

Что, если после установки соединения произойдет разрыв связи?

Что, если, установив соединение, я все равно не получу ответа?

Что, если ответ на мой запрос займет две минуты?

Что, если одновременно придут 10 000 запросов?

Что, если мой диск переполнится в момент, когда я пытаюсь записать сообщение об ошибке SQLException, возникшей потому, что работа сети парализована вирусной программой?