Выбрать главу
Вопросы для обсуждения

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

Упражнения

28. Что из нижеследующего лучше представить в виде фрагмента программы, а что вывести за ее пределы в область метаданных?

1. Назначения коммуникационных портов

2. Поддержка выделения синтаксиса различных языков в программе редактирования

3. Поддержка редактора для различных графических устройств

4. Конечный автомат для программы синтаксического анализа или сканера

5. Типовые значения и результаты, используемые в тестировании модулей

28

Временное связывание

Временное связывание – о чем это? – спросите вы. Это – о времени.

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

Обычно мы не приступаем к программированию, держа в голове тот или иной аспект. Когда люди садятся за проектирование, разработку архитектуры или написание программы, события стремятся к линейности. Это и есть способ мышления большинства людей – сначала сделать «это», а потом всегда сделать «то». Но этот способ мышления приводит к связыванию во времени. Метод А всегда вызывается перед методом В; одновременно должен формироваться только один отчет; необходимо подождать перерисовки экрана до получения отклика на щелчок мыши. «Тик» обязан происходить раньше, чем "так".

Этот подход не отличается большой гибкостью и реализмом.

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

Последовательность операций

При работе над многими проектами, нам приходится моделировать и анализировать последовательности операций пользователей, что является частью анализа требований. Мы хотели бы выяснить, что может происходить одновременно, а что – в строгой последовательности. Одним из способов осуществить задуманное является создание диаграммы последовательностей, с помощью системы обозначений наподобие языка UML (унифицированного языка моделирования) [31].

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

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

Подсказка 39: Анализируйте последовательность операций для увеличения параллелизма

Например, в проекте блендера для коктейлей (упражнение 17) пользователи могут вначале описать последовательность операций следующим образом:

1. Открыть блендер

2. Открыть упаковку со смесью "Пинаколада"

3. Засыпать смесь в блендер

4. Отмерить полчашки белого рома

5. Влить ром

6. Добавить 2 чашки льда

7. Закрыть блендер

8. Перемешивать в течение 2 мин

9. Открыть блендер

10. Взять бокалы

11. Украсить

12. Налить

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

Это может открыть вам глаза на реально существующие зависимости. В этом случае задачи высшего уровня приоритета (1, 2, 4, 10 и 11) могут выполняться параллельно, как бы авансом. Задачи 3, 5 и 6 могут выполняться параллельно, но позже.

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

Рис. 5.2. Диаграмма на языке UML: приготовление коктейля "Пинаколада"

Архитектура

Несколько лет назад мы написали систему оперативной обработки транзакций (OLAP – on-line transaction processing). В простейшем варианте все, что должна была сделать система, – это принять запрос и обработать транзакцию в сравнении с БД. Но мы написали трехзвенное, многопроцессорное распределенное приложение: каждый компонент представлял собой независимую единицу, которая выполнялась параллельно со всеми другими компонентами. Хотя при этом возникает впечатление большой работы, это не так: при написании этого приложения мы использовали преимущество временной несвязанности. Рассмотрим этот проект более подробно.

Система принимает запросы от большого числа каналов передачи данных и обрабатывает транзакции в рамках БД.

Проект налагает следующие ограничения:

• Операции с БД занимают сравнительно большое время.

• При каждой транзакции мы не должны блокировать коммуникационные службы в момент обработки транзакции БД.

• Производительность базы ухудшается за счет слишком большого числа параллельных сеансов.

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

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

РИС. 5.3. Общая схема архитектуры системы оперативной обработки транзакций

Каждый прямоугольник обозначает отдельный процесс; процессы связываются через очереди работ. Каждый входной процесс отслеживает состояние одного входного канала связи и осуществляет запросы к серверу приложения. Все запросы являются асинхронными: как только входной процесс осуществляет текущий запрос, он сразу же возвращается к отслеживанию канала на наличие трафика. Точно так же сервер приложения осуществляет запросы процесса БД [32] и уведомляется в момент завершения отдельной транзакции.

На этом примере также демонстрируется способ быстрого и грубого распределения нагрузки между множественными потребительскими процессами: это так называемая модель голодного потребителя.

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