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

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

Этот пост посвящен 3 шагам в процессе настройки нового блестящего приложения Google Cloud. Здесь я расскажу, как использовать инструменты платформы в GCP, чтобы легко запускать задания, связанные с действием пользователя, но не должно произойти до тех пор, пока пользователь не закончит свой веб-запрос. Вы можете запускать любое количество заданий и поддерживать чистоту, разобщенность и производительность ваших сервисов. В шаблоне реализации мы запускаем событие из нашей кодовой базы и используем другие компоненты GCP, чтобы разложить эти события на небольшие автономные функции.

Читайте дальше, и вы также узнаете, как реализовать основанные на событиях задания на Google Cloud Platform с 1 строкой кода и 1 шагом конфигурации для начала отправки событий, а также 1 шагом конфигурации и 1 функцией Javascript для каждой задачи, которую вы хотите выполнять асинхронно. . Вы также познакомитесь с приятным и функциональным пользовательским интерфейсом GCP, и я объясню, как стандартная среда Google App Engine ломает Go. Есть еще какой-то код.

Цель: избавиться от дофамина

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

Мы вместе (конечно) находимся в Slack, поэтому самый простой и пассивный способ для нас отслеживать активность - публиковать сообщения в канале Slack. Поговорить со Slack - довольно простая вещь, и мы получаем удивительную информацию, просто глядя на время действий людей и их местоположение, что также передается в сообщение Slack.

Архитектура: микросервисы на Google Cloud Platform

Архитектура без совместного использования ресурсов, основанная на микросервисах, соответствует нашим общим техническим целям, а именно:

  • Низкие эксплуатационные расходы
  • Возможность легко привлекать краткосрочных сотрудников к конкретным проектам и отказываться от них
  • Представление
  • Устойчивость
  • API-интерфейс / мобильное приложение готово

Еще несколько дней назад у нас было всего 2 услуги. Один для обслуживания веб-ресурсов, а другой для предоставления REST API и сохранения данных в MySQL. Это оба приложения Google App Engine, работающие в стандартной среде, написанные на Go. В API пока немногое, поэтому не было никаких причин для дальнейшего разложения.

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

Pub / Sub

Pub / Sub - это компонент диспетчера сообщений Google Cloud Platform. Когда кто-то регистрируется в Taxat или обновляет свою регистрационную информацию, служба api отправляет сообщение в Pub / Sub, а затем продолжает заниматься своими делами так быстро, как только может. Заинтересованные стороны подписываются на сообщения из Pub / Sub, и он транслирует сообщения всем подписанным слушателям.

Настройка мероприятия

Тема - ключевая метафора в Google PubSub. Тема - это канал, по которому отправляются объекты данных. Сами объекты данных содержат строку, определяющую сообщение о событии, и необязательный набор пар атрибутов строки "ключ-значение". Например, в этом случае есть канал с именем подписчик, который используется для передачи сообщений о подписчиках. Сообщение содержит глагол, описывающий, что на самом деле произошло, а атрибуты несут информацию о пользователе. Когда издатель отправляет сообщение, он отправляет его в тему, и там слушатели его слушают.

Темами можно управлять программно, но это дорого и обычно не требуется в оперативном режиме. Неудивительно, что настройка тем занимает примерно половину панели управления Pub / Sub на панели инструментов GCP.

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

Снимки экрана должны давать хорошее представление об интерфейсе GCP, если вы им не пользовались. В целом виды ясные и легкие, и в них используется материальный дизайн, как и следовало ожидать. Самым большим фактором в моем выборе GCP вместо AWS была относительная ясность пользовательского интерфейса и документации. AWS был настоящим беспорядком, особенно для нового пользователя.

К сожалению, на стороне публикации Pub / Sub нет журнала сообщений, поэтому вы не можете проверить, действительно ли отправляется то, что, по вашему мнению, отправляется. Существует эмулятор Pubsub, который якобы работает на вашем локальном компьютере, но я потерял интерес к его настройке через пару часов, которые вас разочаровали. Без работающего эмулятора вам понадобится хотя бы тривиальный подписчик, работающий, чтобы видеть, что происходит; в Google Cloud Functions есть программа просмотра журналов в реальном времени (включена в GAE или Compute Engine, если вы используете одну из них для обработки событий). Если бы я начинал заново, я бы сначала написал подписчика, что избавило бы меня от некоторых проблем при тестировании.

Публикация событий с Go

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

Я поместил сантехнику в пакет под названием publish, который экспортирует один метод, который не принимает в качестве аргументов какие-либо типы Pubsub и ничего не возвращает. В момент, когда происходит вещание, это одна строка кода.

Реализация event () в публикации выполняет 5 функций:

  1. Получает клиента для экземпляра Pubsub
  2. Получает дескриптор темы, на которую вы хотите транслировать
  3. Отправляет сообщение
  4. Журналы при сбое (возможно, см. Ниже)
  5. Очищает

r.Get () получает статус t.Publish (). Он блокируется, поэтому его вызов заключен в горутину, чтобы мы могли попытаться войти в журнал, если вызов завершился неудачно, без блокировки отправителя. Обычный шаблон Go заключался бы в том, чтобы обернуть весь этот метод в горутину и полностью исключить медленные части для вызывающего, и это то, что хотел сделать общедоступный интерфейс Event ():

Разбитый контекст, разбитые мечты

Здесь горутина дает сбой и вызывает панику из-за того, как Google реализует и калечит context.Context в стандартной среде, а также делает невозможным выполнение реальных фоновых задач и, как правило, делает мир немного более дерьмовым, чем он есть сейчас.

Все функции Google API принимают Контекст в качестве первого аргумента. Интерфейс Context является частью библиотеки Go верхнего уровня и сочетает в себе 2 намерения:

  1. Чтобы предоставить механизм для переноса данных и состояния через границы, которые не соответствуют изменяемым областям действия. В частности, он используется для хранения данных, относящихся к запросу, так что любой субъект в цепочке вызовов может иметь доступ к информации, относящейся к запросу. Запросы обрабатываются одновременно, поэтому нет области, в которой можно было бы изолировать конкретный запрос.
  2. Чтобы предоставить механизм для завершения разветвленных функций, когда их родительский элемент завершается или когда истекает определенное количество времени. В случае контекста запроса контекст отменяется, как только завершается основная линия (ServeHTTP ()), поэтому все, что использует контекст запроса, будет иметь очень жесткую остановку в этот момент в время.

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

Авторы Go рассмотрели этот сценарий, и стандартные библиотеки предоставляют метод context.Background (), который возвращает Context, который позволяет выполнять более длительный код в фоновом режиме, без нужно беспокоиться об отмене мероприятия.

Фоновый Контекст логически целесообразно передать в Pubsub API, поскольку мы хотим, чтобы приоритет публикации был ниже, чем у запроса. К сожалению, Google ограничил API GCP так, чтобы они запаниковали, если вы передадите им фоновый контекст. Они не работают ни с чем, кроме текущего активного контекста запроса, и они останавливают каждую горутину, зависшую от контекста, как только основная линия завершает свой HTTP-ответ. Другими словами, любой код, использующий API-интерфейсы GCP, может выполняться одновременно, но он должен быть жестко запрограммирован для завершения до того, как запрос будет выполнен.

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

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

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

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

Примеры кода Go

Вот пара фрагментов кода Go, которые могут быть полезны. Опубликовать пакет содержит вспомогательные методы для заполнения Context сабсуба pubsub Client в, чтобы получить его для повторного использования в функциях. Модуль mapify может преобразовать почти любую структуру в строку map [string] для использования в качестве набора атрибутов сообщения с использованием отражения.

Перенос событий с помощью функций Google Cloud

Если обработка контекста в Go - мой наименее любимый вариант дизайна в GCP, то GCF, вероятно, мой самый любимый.

Облачная функция Google - это фрагмент кода без отслеживания состояния и нулевой инфраструктуры, написанный на Node / JS, который отвечает на события Pubsub или HTTP-запросы. Это то, к чему вы можете обратиться, когда просто хотите создать что-то в Интернете без лишней ерунды.

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

Номинально существует эмулятор для локального запуска функций GCF. Для его запуска требуется больше понимания управления пакетами Node, чем я планирую достичь в этой жизни.

Node / JS - это единственный язык программирования, поддерживаемый GCF. Давным-давно в компании, название которой начинается с Y, я был на самом деле профессиональным фронтенд-разработчиком и регулярно писал, если можно так назвать, Javascript. В настоящее время я стараюсь избегать этого, насколько могу, но каждые несколько лет мне нужно что-то делать с Javascript. При погружении всегда чувствуется, что шериф Хоппер из Очень странных дел копает землю под тыквенным грядкой и находит злые внепространственные формы жизни, наводняющие землю под его ничем не примечательным городом, и вы знаете, что все, о чем он, возможно, думает, это что, черт возьми, здесь происходит?

Облачная функция post-signups-to-slack в основном должна выполнить пару sprintfs с данными события и POST json на URL-адрес Slack. Я поискал в Интернете, как правильно выполнить HTTP-запрос от Node, и начал наталкиваться на такой код:

Я обычно поддерживаю трансгрессивные импульсы, но человеку, который ввел эту const в стандарты ECMA, действительно нужно хобби. Или, может быть, им следует стать президентом.

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

Slack

По мере того, как мы приближаемся к финишу, на самом деле мало что можно сказать о Slack, и это нормально. Здесь он в основном служит примером того, что вы делаете с событием в конце цепочки GAE- ›Pub / Sub-› GCF. Кроме того, место для создания приложений Slack совершенно гладкое и простое, и, к сожалению, не на что жаловаться.

Отправка сообщений в канал Slack - это просто отправка небольшого объекта JSON в конечную точку с очень длинным URL-адресом. Шаги:

  1. Создать приложение Slack
  2. Выберите «Входящие веб-перехватчики».
  3. Включите переключатель
  4. Добавить новый веб-перехватчик в рабочую область
  5. Укажите канал для публикации
  6. Скопируйте URL-адрес и вставьте в свой код

Сложите все вместе, и вы получите:

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

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

Благодарности: Кейур Гованде за то, что выслушал меня, сука о контекстах, и помог мне разобраться в этом. Авлин Виг за то, что побудила меня написать это. Это его вина.