Чтобы убедиться в пользе семантического управления версиями, рассмотрим простой практический пример. Приложение по поддержке клиентов было создано для работы с версией клиентского сервиса, имеющей номер 1.2.0. Если будет добавлено какое-либо новое свойство, которое станет причиной изменения номера версии сервиса на 1.3.0, приложение не заметит никаких изменений в поведении сервиса и от него не будет ожидаться внесения каких-либо изменений в работе. Но мы не можем гарантировать, что будем в состоянии работать с версией 1.1.0 клиентского сервиса, поскольку можем зависеть от наличия тех функциональных возможностей, которые были добавлены при выпуске версии 1.2.0. Мы также можем ожидать необходимости внесения изменений в приложение, если выйдет новый выпуск клиентского сервиса с номером версии 2.0.0.
Решение о применении семантического управления версиями может быть принято как для всего сервиса, так и для его отдельно взятой конечной точки, если вы допускаете сосуществование сразу нескольких конечных точек, подробно рассматриваемое в следующем разделе.
Такая схема управления версиями позволяет помещать всего лишь в три поля достаточный объем информации и предположений. Полные изложения спецификации в очень простой форме обозначают те предположения, которые могут быть сделаны клиентами при изменении представленных номеров частей, и помогают упростить процесс сообщения о том, должны ли изменения каким-либо образом повлиять на потребителей. К сожалению, мне нечасто приходилось наблюдать применение данного подхода к распределенным системам.
Сосуществование различных конечных точек
После того как сделано все возможное, чтобы избежать появления критических изменений интерфейса, следующим заданием станет ограничение влияния. При этом нужно постараться не допустить принуждения потребителей к созданию обновлений вслед за нами, поскольку мы неизменно стремимся обеспечить возможность выпуска микросервисов независимо друг от друга. Одним из подходов, успешно применявшихся мною при решении этой задачи, являлось сосуществование старого и нового интерфейсов в одном и том же работающем сервисе. То есть, нацелившись на выпуск критических изменений, мы развертываем новую версию сервиса, которая выставляет как старую, так и новую версию конечной точки.
Это позволяет нам получить новый микросервис как можно быстрее и с новым интерфейсом, но дает потребителям время на раскачку. Как только все потребители полностью откажутся от использования старой конечной точки, ее можно будет удалить вместе со всем связанным с ней кодом (рис. 4.5).
Рис. 4.5. Сосуществование различных версий конечных точек, позволяющее клиентам осуществлять постепенный переход
Когда я в последний раз использовал этот подход, мы слегка запутались с количеством имеющихся у нас потребителей и внесенных критических изменений. Это означало, что у нас фактически сосуществовали три различные версии конечных точек. Я бы такое не стал рекомендовать! Дополнительно нагружать себя поддержкой всего требующегося для этого кода и проведением связанного с ним тестирования, призванного убедить в том, что все это работает, совершенно ни к чему. Чтобы привести все в более управляемое состояние, мы внутренне перевели все запросы к конечной точке V1 в запросы к конечной точке V2, а затем все запросы к V2 — в запросы к конечной точке V3. Это означало, что мы могли четко очертить тот код, который подлежал удалению при выходе из употребления той или иной прежней конечной точки.
По сути, это является примером применения шаблона расширения и свертывания (expand and contract pattern), допускающего постепенный ввод критических изменений. Мы расширяем предлагаемые возможности, поддерживая как старый, так и новый путь получения какого-либо результата, и как только старые потребители станут работать по-новому, мы свертываем часть нашего API, удаляя старые функциональные возможности.
Если вы намереваетесь допустить сосуществование конечных точек, то для этого потребуется способ соответствующего перенаправления запросов вызывающих сторон. Я видел, как это делается для систем, использующих HTTP, с помощью указания номеров версий как в заголовках запросов, так и в самих URI, например /v1/customer/ или /v2/customer/. Я не могу сказать, какой из этих подходов рациональнее. С одной стороны, мне не нравятся сложные URI-индикаторы, поскольку я не хочу заставлять клиентов пользоваться жестко закодированными URI-шаблонами, но с другой — такой подход делает многое весьма очевидным и может упростить маршрутизацию запросов.
При использовании RPC все может оказаться несколько сложнее. Я справлялся с этой задачей с помощью буферов протокола, помещая свои методы в различные пространства имен, например v1.createCustomer и v2.createCustomer, но когда делается попытка поддержки различных версий одних и тех же типов, отправляемых по сети, возникают серьезные трудности.
Использование нескольких параллельных версий сервиса
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии