Reactive manifesto
Начать изучение reactive programming как подхода к программированию, конечно же, стоит с reactive manifesto. Этот документ объясняет базовые концепции, которые лежат в основе реактивного программирования. Или даже лучше сказать, свойства системы, которые хочется достичь.
Тут я не буду пересказывать подробно весь документ, поэтому настоятельно рекомендую его к самостоятельному прочтению. Однако, приведу самые важные части в качестве основы для дальнейшего описания.
Итак, reactive manifesto говорит нам, что мир быстро меняется и на смену системам, работающих на десяти серверах, отвечающих за секунды, оперирующими гигабайтами данных и недоступными часами в моменты обслуживания, приходят системы, развернутые на всем, чем только можно, начиная с мобильных устройств и заканчивая облаками, работающие на сотнях многоядерных процессорах и оперирующих петабайтами, при том, что отклик должен измеряться миллисекундами. Ну и вслед на этим должны меняться наши подходы к построению таких систем.
Мы хотим, как утверждается в манифесте, строить системы, обладающие свойствами Отзывчивости (Responsive), Упругости (Resilient), Эластичности (Elastic) и Управления с помощью сообщений (Message Driven). Собственно, такие системы и есть Reactive systems.
Рассмотрим вкратце каждое свойство:
-
Responsive: система отвечает очень быстро, и это время ответа, находясь в определенных границах, не выходит за верхний предел. Тем самым достигается постоянное качество обслуживания (quality of service)
-
Resilient: система отвечает даже в момент сбоя. Это достигается за счет репликации, локальности, изоляции и делегирования. Сбои локализуются внутри компонента, тем самым он может быть восстановлен отдельно, не влияя на систему в целом. Восстановлением занимается отдельный компонент
-
Elastic: система отвечает под изменяющейся нагрузкой. Реактивные системы могут увеличивать или уменьшать свои ресурсы. Дизайн системы построен так, чтобы не допускать неоднозначного поведения или центральных узлов-bottleneck-ов, что позволяет распределять входящую нагрузку. Реактивные системы поддерживают алгоритмы масштабирования, измеряя производительность в реальном времени
-
Message Driven: все основано на асинхронной (неблокирующей) передаче сообщений, позволяя тем самым прочертить границы между компонентами и достичь слабой связанности, изоляции, независимости от расположения компонентов (location transparency), а также воспринимать ошибки тоже как сообщения
Вот как эти свойства связаны:
Далее, небольшие системы складываются в большие, которые также сохраняют эти Реактивные свойства на всех уровнях масштабирования.
Также рекомендую почитать глоссарий терминов и подписать манифест, если вы со всем согласны. Если не согласны - давайте обсудим это в комментариях.
Observer pattern
Плавно переходя к библиотеке для .NET, созданной для построения систем на перечисленных принципах, - Reactive Extensions, важно заметить, какие концепции там использованы. Всё базируется на паттерне Observer. Кратко пробежимся по нему.
Итак, observer, он же наблюдатель, он же publisher/subriber, “создает механизм у класса, который позволяет получать экземпляру объекта этого класса оповещения от других объектов об изменении их состояния, тем самым наблюдая за ними”. Вот диаграмма классов с википедии:
Также приведу код на C#, тоже с википедии, но с пояснениями.
Пусть у нас есть абстракция IObserver
(он же subcriber), которая выполняет некоторое действие Update
- обновление своего состояния.
interface IObserver
{
void Update(string state);
}
По сути, это функциональная абстракция, которую вызывающий код должен использовать для выполнения действия. Пока все просто.
Дальше, пусть есть еще одна абстракция IObservable
(он же publisher), которая позволяет что-то делать с IObserver
, а именно - добавлять/удалять и нотифицировать.
interface IObservable
{
void AddObserver(IObserver observer);
void RemoveObserver(IObserver observer);
void NotifyObservers(string s);
}
Имея эти абстракции, можем написать такой псевдокод:
IObserver observer1;
IObserver observer2;
IObservable observable;
observable.AddObserver(observer1);
observable.AddObserver(observer2);
observable.NotifyObservers("some event");
Тут мы имеем публикатора observable и двух его подписчиков (наблюдателей) observer1 и observer2. Публикатор для нотификации подписчиков вызывает у них метод Update
. Опять же все просто.
Как гласит википедия, “шаблон «наблюдатель» применяется в тех случаях, когда система обладает следующими свойствами:
- существует, как минимум, один объект, рассылающий сообщения;
- имеется не менее одного получателя сообщений, причём их количество и состав могут изменяться во время работы приложения;
- нет надобности очень сильно связывать взаимодействующие объекты, что полезно для повторного использования.
Данный шаблон часто применяют в ситуациях, в которых отправителя сообщений не интересует, что делают получатели с предоставленной им информацией.”
Таким образом, паттерн позволяет чётко выделить отвественности и достичь слабой связанности. Причем топология системы может меняться в рантайме.
Summary
Подводя здесь черту, стоит еще раз обратить внимание на тот факт, что
-
Реактивные системы обладают свойствами Отзывчивости (Responsive), Упругости (Resilient), Эластичности (Elastic) и Управления с помощью сообщений (Message Driven)
-
Существует библиотека Rx для множетва языков программирования, которая призвана помогать делать системы с такими свойствами, то есть Реактивные системы
-
В основе Rx лежит шаблон проектирования Observer
В следующем посте разберёмся с основными артефактами Reactive Extensions. Stay tuned!
P.S. Сколько не пытался добавить риббон, так и не смог подружить его с общим дизайном. Вместо этого вот: we are reactive!
P.P.S. Aren’t we?