Рубрики
Без рубрики

Рефактор микросервисов с GRPC

Доказательство концепции для разделения монолита в микросервисах с GRPC. Tagged с Python, Grpc, узлом.

Введение

Работа для разработчика тяжелая, часто мы буквально подвержены адскими условиям (у меня есть склонность к драматизации:-)). Некоторое время назад это было DLL Ад , совсем недавно обратный вызов ад , но тот, которого я боюсь больше всего, это Устаревший кодекс АД .

Кредиты toggl.com

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

Расщепляя ад

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

Предметом статьи является монолитное приложение Python 2.7. Подход состоит в том, чтобы создать доказательство концепции для проверки прогрессивного портирования монолитной кодовой базы в архитектуру микросервисов. MicroService – это злоупотребленный термином, модное слово, если хотите, но это интересный архитектурный шаблон с большим количеством преимуществ, если он принят с прагматизмом. Например, миграция базы кода «монолит» от Python 2.7 на Python 3.x может быть болью. Вместо этого, разделение проекта на небольшие компоненты (или услуги), и пусть они общаются друг с другом, может быть намного проще, разделите и императу! Фондом для разделения проекта таким образом является определение эффективного способа управления обслуживанием для общения. Это должно быть простое, быстрое, масштабируемые и проверенные боевыми действиями, название этой вещи – система RPC (удаленный вызов процедуры).

RPC

Удаленный вызов процедуры – это довольно старая идея, поскольку самые первые сети компьютера начали распространяться, была реализована некоторая система RPC. RPC обычно основан на шаблоне запроса/ответа, существует множество систем RPC, которые часто реализуются по -разному. Несмотря на то, что идея всегда одинакова: процесс A делает запрос на процесс B который может что -то ответить A . Эти процессы могут работать на одном и том же хосте или в разных, предполагают, что они способны общаться друг с другом через сеть. Это упрощенный взгляд, но с логической точки зрения он решает наше требование. Из, конечно, есть гораздо больше, чтобы выбрать правильный RPC, в частности, это должно быть:

  • Устойчивый
  • Исполнитель
  • Безопасный
  • Языковая агностик

Последний момент в настоящее время важен, я отличный противник подхода «Серебряной пули», который часто «если у вас есть все, это молоток, все выглядит как гвоздь». Имея выбор из широкого спектра языков, вы можете обнаружить, что некоторые компоненты лучше, если они разработаны с помощью JavaScript, другого в Python, а некоторые другие в Go, это мощно! (и в то же время опасно, если он злоупотребляет).

Проверить архитектурные изменения

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

  • Должен иметь возможность звонить в службы, реализованные в разных версиях Python (2.x и 3.x)
  • Должен иметь возможность звонить на службы, реализованные на разных языках, скажем, JavaScript
  • Должен работать в контейнерах

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

GRPC в качестве системы общения обслуживания

GRPC – это современная рамка удаленной процедуры с открытым исходным кодом (RPC), которая может работать где угодно , это то, что вы можете прочитать из Официальный сайт FAQ . Это выглядит именно то, что мы ищем, тогда стоит попробовать.

GRPC использует Протокол буфер как механизм для сериализации данных и определения сервисных интерфейсов. Используя конкретный язык для создания интерфейса, это довольно распространенный подход, в терминах RPC он называется IDL . Как правило, LDL является пользовательским языком описания, специально предназначенным для разработки интерфейса, используемого в Services Communications. Сосредоточив внимание на структуре проектов, если вы используете IDL, вам нужно хотя бы две вещи:

  • Один или несколько источников IDL, для интерфейсов услуг
  • Способ использовать (компилировать или динамически загружать) определения IDL в вашем коде

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

Определение интерфейса

Давайте начнем с примера интерфейса IDL, который мы собираемся использовать в POC.

syntax = "proto3";

import "common.proto";

package notificator;

service NotificatorService {
    rpc SendNotification(NotificationPayload) returns (Result) {}
}

message NotificationPayload {
    string destination  = 1;
    string message = 2;

}

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

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

Реализация

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

├── client-2.x
├── protos
│   ├── common.proto
│   └── notification.proto
└── server-3.x

Ничего особенного здесь, две папки Клиент-2.x и Сервер-3.x содержать код гипотетического сервиса и его потребителя, я назвал их клиентом и сервером, чтобы понять роли, но имейте в виду, что в GRPC нет ролевой концепции, она определяет интерфейсы и то, как обслуживание взаимодействует друг друга, не является чем -то это должно знать. Папка «Межстинг» – это прото Я поместил сюда источники IDL с определениями интерфейса. Проект состоит из услуги по отправке уведомлений (какой Hewer это Push -уведомление, SMS или что -то еще). Затем определение службы определяет метод отправки уведомления, полезной нагрузки с корпусом сообщений и адресом назначения. Trasled в протоколевом буфере IDL Это эквивалентно коду для интерфейса IDL в предыдущем абзаце.

В параметрах метода буфера протокола и типах возврата должны всегда определяться как пользовательские типы, в других терминах вы не можете использовать примитивные типы, такие как строка или Bool Как они есть, обязательно определить пользовательский тип. В нашем случае Уведомление о выгрузке определение показано внизу, в то время как Результат импортируется из Common.proto . Определение типа Proto Files в прото -файлах касается чисел, назначенных каждому свойству (например, назначение или Эти цифры связаны с тем, как Протокол буферной кодировки работает. Важно знать, что они должны быть уникальными в определении сообщений и, что наиболее важно, если изменены кодированные данные несовместимы с клиентом, использующим старую нумерацию.

Есть много других подробностей о буфере протокола, они хорошо документированы в Официальная документация буфера протокола Анкет

Установка зависимостей

Оба проекта, Клиент-2.x и сервер-3-х , иди с Требования.txt файл. В качестве фактического стандарта, наличие этого файла, это тривиально установить все зависимости проекта с PIP установка -r retement.txt Анкет Заглянуть внутрь файла требований может быть интересно посмотреть, что нужно проекту, в частности, два основных пакета grpcio и grpcio-tools Это реализация GRPC и пакеты инструментов, основные пакеты для использования GRPC.

Примечание о Makefile (ы)

В проекте вы заметите некоторые Makefiles, это не потому, что я ностальгический разработчик C/C ++:-). Это потому, что у Python не хватает стандартного способа определения сценариев, как Node.js, с Сценарии в package.json . Я нахожу Makefile Хороший компромисс, вместо создания пользовательского сценария оболочки, поэтому зависимости проекта могут быть установлены с помощью сделать установку , печатать просто сделать перечислены все предоставленные команды. Конечно, сделать Должен присутствовать на системе, как установить ее, не зависит от осадки, и ОС зависит, но об этом существует огромное количество документации.

Звонить в службу

Хорошо здесь, но как мы используем IDL, чтобы позвонить в службу через GRPC? Как я писал до того, как есть два способа использования Прото Файлы, в этом проекте мы генерируем код из IDL. До этого мы заметили, что помимо пакета Python GRPC есть еще один под названием grpc_tools Анкет Трудно догадаться, но оказывается, что это пакет, предоставляющий инструменты для GRPC. Одной из предоставленных функций является генерация кода, начиная с Прото Файл, это то, что мы собираемся использовать. Начнем с Клиент-2.x Проект, это точно то же самое для сервер-3.x , используя файл MAKE, предоставленный в проекте, это вопрос запуска делать сборка Анкет На самом деле Makefile запускает инструменты Python GRPC, заглядывая в один из MakeFile, предоставленных внутри клиента или сервера, мы можем увидеть, как.

python -m grpc_tools.protoc -I../protos --python_out=. --grpc_python_out=. ../protos/common.proto
python -m grpc_tools.protoc -I../protos --python_out=. --grpc_python_out=. ../protos/notification.proto

Запуск вышеупомянутых команд даст несколько новых исходных файлов Python. Эти файлы представляют собой перевод услуги Python и полезные нагрузки, определенные в Прото файл. Заметить, что для каждого прото Файл создается два файла. Согласно эти файлы имеют одинаковое прото Имя и Postfix, один – _pb2.py а другой – _pb2_grpc.py Анкет Проще говоря, первое – это то, где определяются структуры данных, как Уведомление о выгрузке , последнее, где находятся заглушки по обслуживанию. Давайте начнем с клиента, позвонив в Уведомления о счете так же просто, как следующий код.

    with grpc.insecure_channel('{0}:{1}'.format(GRPC_HOST, GRPC_PORT)) as channel:
        stub = notification_pb2_grpc.NotificatorServiceStub(channel)
        stub.SendNotification(
                   notification_pb2.NotificationPayload(destination="Fabrizio", message="Hello!!!")
                   )

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

Одно важное примечание об окружающей среде: я написал, что одним из требований для POC является проверка сервисной связи между различными версиями Python. Если вы хотите проверить проект без Docker (ниже дополнительной информации о нем) вам необходимо использовать Python 2.7 для клиента и Pythion 3.6 для сервера на той же машине. Это можно сделать с Virtualenv , быстрое введение к нему можно найти здесь , В любом случае, если вы предпочитаете подход «Дайте мне посмотреть, как он работает как можно скорее», прочитайте параграф «Запуск в докере» ниже.

Создание сервиса

На данный момент у нас почти все, мы определили IDL, разработали клиента, но мы скучаем по основному блюду: услуга! Я оставил реализацию службы после намерения клиента, уже определив IDL и клиента, это должно быть ясно, что нам нужно от него. Важным моментом, на котором следует сосредоточиться, является то, что нам нужно где-то в коде, реализация сервиса, которую мы хотим сделать через GRPC, ниже нашего супер-мега-крутого Уведомления о счете Анкет

class NotificatorServiceServicer(notification_pb2_grpc.NotificatorServiceServicer):
    def SendNotification(self,  request, context):
        logging.debug(f"handling notification message '{request.message}' to {request.destination})  ")
        return common_pb2.Result(status=True)

Сразу ясно, что мы реализуем здесь: интерфейс, определенный в нашем IDL. Базовый класс уведомление_pb2_grpc. Natificatorserservicer Полезная нагрузка и результаты – те, которые находятся в IDL. Реализация тривиальна: мы используем Сообщение и пункт назначения исходя из запроса, который Уведомление о выгрузке , чтобы зарегистрировать сообщение, отвечая с Результат Обертывание статуса успеха статус = true Анкет

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

server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
notification_pb2_grpc.add_NotificatorServiceServicer_to_server(
    NotificatorServiceServicer(), server)
server.add_insecure_port(f"0.0.0.0:5001")
server.start()

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

На этом этапе запуска сервера в Virtualenv С Python 3.6 и клиентом в другом с Python 2.7 они должны начать звонить друг другу, доступен полный исходный код здесь

Как насчет использования других языков?

Я не забыл один из самых важных моментов, чтобы проверить с POC, проверяя взаимодействие с другими языками. Теперь, что мы немного уверены в GRPC и как это работает, пришло время представить нового клиента. Этот использует JavaScript, работающий точно таким же образом Python 2.x One. Конечно, есть привязки GRPC практически для любого языка (C, C ++, Java, C#, …) Но я предпочитаю использовать JavaScript, потому что теперь это один из самых распространенных лагей. В предыдущем проекте, которую я лгал, я пропустил клиента JavaScript, реальная структура проекта – такая ниже.


├── client-2.x
├── js-client     <<<=== You are here!!!
├── protos
│   ├── common.proto
│   └── notification.proto
└── server-3.x

Очевидно, что клиент JavaScript предназначен для того же поведения, что и Python One, если вы уверены в среде Node.js, вы знаете, что первый шаг – установить зависимости (также известные как модули узлов).

npm intall

Со всеми модулями нам нужно генерировать прокси -код GRPC из прото -файлов, как мы делали в версии Python. Как обычно в среде node.js есть сценарий, определенный в Package.json для этого

npm run build

Это ярлык, но «под капотом» команда очень похожа на то, что используется для клиента Python.

grpc_tools_node_protoc --js_out=import_style=commonjs,binary:. --grpc_out=. --plugin=protoc-gen-grpc=node_modules/grpc-tools/bin/grpc_node_plugin -I ../protos/ common.proto && grpc_tools_node_protoc --js_out=import_style=commonjs,binary:. --grpc_out=. --plugin=protoc-gen-grpc=node_modules/grpc-tools/bin/grpc_node_plugin -I ../protos/ notification.proto

Короче говоря, я использовал проток (AKA Protobuf IDL Compiler), специфичный для Node.js, эта команда создает четыре файла, так же, как я с проток призван с питоном выше

Бег в Docker

Контейнеры

Если вы следовали всем инструкциям по статье на данный момент, вы можете запустить все локально, но, поскольку одним из моих требований было протестировать проект в среде контейнеров, проект содержит Dockerfile (ы) и определение Docker-Compose. Опять же, установка Docker вышла из области (я чувствую, как шутка из изобретателя Skydivers Hook (*))

Работать локально с Docker Compose

Предполагая, что среда Docker настроена на машине, запуск проекта является вопросом запуска Docker-Compose Up в корневой папке. Через некоторое время консоль будет затоплена сообщениями от сервера и клиента.

Каждая итерация три сообщения напечатаны на стандартном выводе.

client_1  | DEBUG:root:Client: sending notification calling gRPC server
server_1  | DEBUG:root:handling notification message 'Hello!!!' to Fabrizio)  
client_1  | DEBUG:root:Client: notification sent

Вывод

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

(*) Шутка из изобретателя крючка с парашютом

Изобретатель пошел в патентное управление, сказав: «Я изобрел крючок, чтобы спасти жизнь парашютистов, и я хочу на него патентировать».

Сотрудник сказал: «Ну, скажи мне, как это работает»

Изобретатель : «Просто, если парашют не открывает, парашютист может использовать крюк, чтобы спасти его жизнь»

Сотрудник : «Хорошо, хорошо, но где прыжок с парашютом должен зацепить?»

Изобретатель : «Эй, я не могу просто придумать все вещи сам!»

Оригинал: “https://dev.to/guglielmino/refactor-to-microservices-with-grpc-33id”