Статьи

Локальные микросервисы: первоклассные процедуры

Это третья статья из трех частей о локальных микросервисах. Первые две статьи смотрели на:

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

В этой статье дается подробное описание локальных (передаваемых по ссылке) микросервисов.

Часть третья: локальные микросервисы через первоклассные процедуры

Первые две статьи в этой серии определены:

  1. Ссылки на объекты — это хороший граф узлов (объектов) и линий (полей)
  2. Методы объекта имеют существенную проблему связи, создающую мозаику поведения
  3. Микросервисы разбивают пару методов, возвращая поведение графу узлов (микросервисы) и линий (HTTP-запросы, / сообщения очереди)

Существует основополагающий образец для представления этого разъединенного поведения. Это HTTP URL / имя очереди и тип сообщения / тип сообщения. Этот разъединенный образец вызова клиента может быть представлен следующим общим интерфейсом:

1
2
3
interface ClientCall<T> {
  void invokeService(T singleObject);
}

Этот интерфейс вызова клиента затем реализуется соответствующим HTTP-запросом.
сервис (…) метод или очередь
Метод onMessage (…). Эти методы обычно находятся на следующих объектах:

01
02
03
04
05
06
07
08
09
10
public void SomeHttpServicerImpl {
  @Inject SomeRepository someRepository;
  @Inject AnotherRepository anotherRepository;
  @Inject ClientCall<SomeArgument> anotherMicroservice;
  // other dependencies
 
  public void service(SomeObject httpRequestEntity) {
    // service HTTP request with injected dependencies
  }
}
01
02
03
04
05
06
07
08
09
10
public void SomeQueueConsumerImpl {
  @Inject SomeRepository someRepository;
  @Inject AnotherRepository anotherRepository;
  @Inject ClientCall<SomeArgument> anotherMicroservice;
  // other dependencies
 
  public void onMessage(SomeQueueMessage message) {
    // service Queue message with injected dependencies
  }
}

Кроме того, то, что не показано четко, это модель потоков. Поскольку HTTP-сервисер или потребитель очереди находятся в своем собственном процессе, они работают со своими собственными потоками.

В результате получается следующая схема внедрения микросервиса:

  • Один объект, предоставленный клиентом
  • Остальные объекты вводятся зависимостью
  • Используемый поток основан на реализации сервиса / потребителя
  • Взаимодействие с другими микросервисами осуществляется через единый параметр ClientCall

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

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

Итак, можем ли мы вызывать и выполнять микросервис одним и тем же потоком и при этом продолжать предоставлять микросервисные преимущества ограниченного контекста? (другими словами, меньшие лобзики)

Локальный ограниченный контекст

Чтобы увидеть, как могут быть достигнуты локальные (вызывающие / выполняющие один и тот же поток) микросервисы, нам нужно немного изменить вышеприведенные реализации.

Вместо того, чтобы вводить поле / сеттер, давайте посмотрим на использование конструктора. Мы могли бы превратить вышеуказанную реализацию в следующее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public void SomeMicroserviceImpl {
  private final SomeRepository someRepository;
  private final AnotherRepository anotherRepository;
  private final ClientCall<SomeArgument> anotherMicroservice;
 
  @Inject
  public SomeMicroserviceImpl(
            SomeRepository someRepository,
            AnotherRepository anotherRepository,
            ClientCall<SomeArgument> anotherMicroservice) {
    this.someRepository = someRepository;
    this.anotherRepository = anotherRepository;
    this.anotherMicroservice = anotherMicroservice;
  }
 
  public void service(SomeObject httpRequestEntity) {
    // service HTTP request with injected dependencies
  }
}

Тем не менее, это много кода!

Скорее, почему бы просто не вставить зависимости непосредственно в метод:

1
2
3
4
5
6
7
public static void service(
            SomeObject httpRequestEntity,
            SomeRepository someRepository,
            AnotherRepository anotherRepository,
            ClientCall<SomeArgument> anotherMicroservice) {
    // service HTTP request with injected dependencies
  }

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

Это исполнение сейчас:

  1. ClientCall используется для вызова процедуры
  2. Процедура тянет в соответствующие зависимости
  3. Затем процедура вызывает другие процедуры через интерфейс ClientCall.

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

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

Теперь возникает вопрос, как мы можем реализовать это, чтобы процедуры выполнялись в одном и том же пространстве процессов?

Первоклассная процедура

Ну, эта процедура удивительно похожа на процедуру первого класса. Увидеть:

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

Два ограниченных контекстных подхода имеют сходные характеристики:

  • HTTP / Queue Communication можно рассматривать как один аргумент. Продолжение
  • Модели потоков могут быть разными в каждой первоклассной процедуре / микросервисном процессе
  • Внедрение зависимостей обоих позволяет получить доступ только к необходимому графу объектов, что позволяет создавать небольшие головоломки объектов (без монолитов). Другими словами, ограниченные контексты.

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

Удаленный против Локального

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

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

Для меня это относится к проблеме
рассуждать слишком рано . Чтобы получить правильное сочетание микросервисов, требуется значительное количество сборов требований и архитектуры. Почему? Потому что рефакторинг микросервисных архитектур может быть дорогим. Микросервисы связаны с большим количеством накладных расходов, как правило, в разных репозиториях кода, сборке конвейеров, обработке сетевых сбоев и т. Д. Обнаружение неправильного набора микросервисов требует больших усилий для изменения.

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

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

Затем, когда ваша нагрузка увеличивается, вы можете разделить первоклассные процедуры на отдельные узлы. Просто измените связь между ними на HTTP-вызов или очередь. Кроме того, это разделение может быть по разным причинам, которые вы можете обнаружить по пути:

  • различные циклы изменения функциональности
  • различные командные обязанности (например, закон Конвея)
  • управление данными может означать географические ограничения
  • безопасность может потребовать, чтобы некоторые работали в помещении
  • на предельных мощностях помещения может означать подталкивание некоторых к публичным облакам

Вышеуказанный список не является исчерпывающим.

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

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

Резюме

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

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

Смотрите оригинальную статью здесь: Локальные микросервисы: первоклассные процедуры

Мнения, высказанные участниками Java Code Geeks, являются их собственными.