You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
HTTP server на Golang для новичков. На примере REST API к IBM MQ
Предистория
В ходе внедрения 1С:ERP в одном из крупных заказчков появилась интересная задача - интеграция 1С с корпоративной шиной IBM MQ.
Ключевыми требованиями в части взаимодействтия с IBM MQ были:
управление пулом подключений к IBM MQ
использование предустановленных подключений к IBM MQ
автоматическое переключение на резервный узел IBM MQ при сбое основного узла
использование транзакционного режима при работе с IBM MQ (SYNCPOINT)
Дополнительно были выдвинуты требования к обработке XML сообщений:
нормализация (канонизация) сообщений по стандрту RFC 3076
использование меток целостности для верификации целостности сообщений но кастомному алгоритму HMAC с хэш-функцией ГОСТ-34.11.94
управление оперативным кэшем серкретных ключей для вычисления HMAC
Стандартного адаптера в 1C к IBM MQ не было. Существующий REST API к IBM MQ не подходил под требования заказчика.
Решили делать собственный REST адаптер между 1С и IBM MQ. Точнее не полноценный REST, так как тело сообщения передавалось в XML формате "транспортного пакета" заказчика.
Оказалось, что IBM выпустила официальную библиотеку к IBM MQ на Go. Библиотека отличается неплохой стабильностью, что и не удивительно, так как она написана в виде "обертки" над официальной C библиотекой.
За полгода работы с этой библиотекой я зафиксировал всего 2 запроса. Оба были связаны с обработкой слайсов [].
Архитектура адаптера укрупненно показана на следующем рисунке.
В настоящей статье речь пойдет только об одной компоненте - HTTP сервере для обработки REST API.
Зачем еще раз писать про HTTP сервер на Go?
Действительно зачем? Столько написано статей, казалось бы, уже разжевано все, что только можно. В том то и проблема, написано много, но все статьи закрывают отдельные задчи или мелкие требования. Полноценного примера HTTP сервера для построения REST API я не нашел (возможно, плохо искал).
Функциональность HTTP сервер
Программный код в моем репозитории реализует следующий функционал:
Передача параметров для запуска через командную строку
Настройка параллельного логирования в Stderr и файл
Настройка HTTP сервера через конфигурационный файл
Корректный запуск и
Корректная остановка HTTP сервера с закрытием контекста
Кастомная структура для хранения ошибок
внедряется номер версии программы, дата сбори и номер commit
Basic аутентификация
JWT
MS AD аутентификация
Структура сервера. Запуск и останов
Основные компоненты / пакеты сервера:
main - содержит миминум кода. Его задача обработать входные флаги, установить уровень логирования, создать демон и запустить его. После запуска демона ожидать его завершения.
daemon - создает и связывает все корневые и обработчики. Например, в моем адаптере IBM MQ, daemon содержит сервисы: HTTP сервер, пул подключений к IBM MQ, обработчик очeреди IBM MQ, обработчик XML, обработчик кэша секретных ключей. Также daemon создает корневой контекст, который используется для корректной остановки всех сервисов. В задачи daemon входят считывание конфигурационного файла и создание конфигураций для всех сервисов.
httpserver - собственно HTTP сервер. Включает в себя: листенер, роутер, HTTP сервер, набор обработчиков.
handler - набор обработчиков HTTP запросов. Ключавая задача состояла в максимальном упрощении написания обработчиков. Ниже я покажу, что из этого получилось.
httplog - логирование входящих и исходящих запросов и ответов, включая заголовок и тело. Влючение и уровень логирования управляется через конфигурационный файл.
На следующем рисунке показана упрощенная UML диаграмма последовательности запуска и остановки сервера.
Для организации библиотек перешел с golang/dep/cmd/dep на go mod, но для хранения библиотек по прежнему используется папка vendor в корне проекта. Это позволяет хранить "правильные" версии библиотек в своем репозитории проекта. Обновление библиотек выполняется командой
go get -u ./../...
go mod vendor
При переходе на go mod столкнулся с проблемой, что часть сторонних библиотек не поддерживают корректно работу с модулями и скачивается неправильная версия. Для таких библиотек нужно указывать в конце номер нужного commit, например:
go get github.com/ibm-messaging/mq-golang/ibmmq@19b946c
Для того, чтобы компилятор брал библиотеки из каталога vendor, нужно указать опцию
go build -v -mod vendor
Сборка
Для сборки используется простой make файл с неколькими режимами (взят где-то на посторах интернета):
rebuild - полная пересборка
build - инкрементальная сборка
check - проверка кода с использованием github.com/golangci/golangci-lint
При сборе в исполняемый файл внедряется номер версии программы, дата сбори и номер commit. Эта инфорация выводится в лог файл - весьма полезно для разбора ошибок. Для реализации такого внедрения, в main добавляем переменные
var (
version="0.0.5"// номер версии, задается рукамиcommit="unset"buildTime="unset"
)
При сборе в make файле, запрашиваем git о номере commit
COMMIT?=$(shell git rev-parse --short HEAD)
BUILD_TIME?=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
При сборке привязываем эти значения к ранее объявленным переменным (опция -ldflags "-X"), заодно указываем операционку, под которую собираем и место хранения бинарника
GOOS=${GOOS} go build -v -a -mod vendor \
-ldflags "-X main.commit=${COMMIT} -X main.buildTime=${BUILD_TIME}" \
-o bin/${GOOS}/${APP}
В mainmain.main в переменную записываю итоговую информацию о версии, commit и дате сборки
app.Version=fmt.Sprintf("%s, commit '%s', build time '%s'", version, commit, buildTime)
Обработка ошибок
Создание ошибок Логирование ошибки в месте возникновения Передача ошибки на уровень вверх Обработка кастомных ошибок
0 commit comments