Руководство прикладного разработчика#
Термины и определения#
Термин/Аббревиатура |
Определение |
|---|---|
АС |
Автоматизированная система |
Heartbeat |
Квитанция, передаваемая от получателя к отправителю в ответ на gRPC-вызов, подтверждающая получение сообщения. Не содержит бизнес-информации |
CPU |
Central Processing Unit, центральный процессор |
gRPC |
Высокопроизводительный фреймворк, разработанный компанией Google для вызова удаленных процедур (RPC) |
Liveness-проба |
Программный зонд для определения живучести контейнера. Проба может иметь статус UP - поднята или DOWN - опущена |
Readiness-проба |
Программный зонд для определения готовности контейнера. Проба может иметь статус UP - поднята или DOWN - опущена |
Audit OPM |
Функциональная подсистема продукта Platform V Monitor, предназначенная для аудирования событий |
Системные требования#
Требования к системному программному обеспечению приведены в разделе Системные требования документа «Руководство по установке» компонента MQ Gateway (MQGT) продукта Platform V Synapse Enterprise Integration.
Подключение и конфигурирование#
Для установки экземпляра Шлюза MQ на стенд в проект загрузите конфигурационные артефакты:
Артефакт |
Содержание |
Описание |
|---|---|---|
Deployment |
Параметры запуска контейнера приложения в Kubernetes (OpenShift) |
Наименование экземпляра приложения, ссылка на образ контейнера приложения, запрашиваемые ресурсы, публикуемые ports, параметры Liveness- и Readiness-проб, необходимость и параметры подключения sidecar-контейнеров, точки монтирования конфигурационных артефактов в файловую систему контейнера |
Config Map |
|
Файл, содержащий параметры конфигурации приложения |
Secret |
|
Файл, содержащий параметры настройки SSL. Содержит конфиденциальные данные (пароли к приватным ключам), поэтому загружается в виде секрета |
Secret |
Ключи и сертификаты |
Файлы |
Service |
Артефакт для регистрации приложения в service discovery Kubernetes (OpenShift) |
Селекторы и ports для подключения приложения к механизмам распределения трафика Kubernetes (OpenShift) |
Virtual Service |
Артефакт для настройки политик Istio |
Параметры маршрутизации трафика между сервисами в Kubernetes (OpenShift) |
Destination Rule |
Артефакт для настройки политик Istio |
Параметры балансировки трафика между Pods приложения |
Service Entry |
Артефакт для регистрации внешнего сервиса в Kubernetes (OpenShift) |
Содержит адреса hosts и номера портов для подключения к менеджерам MQ. Если он уже установлен в проекте для необходимых менеджеров Service Entry, загружать его повторно не требуется |
Последовательность загрузки артефактов:
Config Map c
application.yml.Secret c ключами.
Service Entry.
Deployment.
Service.
Virtual Service.
Destination Rule.
Порядок действий при загрузке артефактов в проект см. в разделе «Установка» документа «Руководство по установке».
Настройка Шлюза MQ согласно вариантам использования подробно описана в разделе «Настройка конфигурации» документа «Порядок настройки Шлюза MQ». Список доступных для настройки параметров приведен в разделе «Верхнеуровневые блоки настроек» документа «Полное описание настроек».
Миграция на текущую версию#
Для миграции на текущую версию выполните следующие действия:
Подготовьте Config-map с новой конфигурацией.
Подготовьте артефакт Deployment, в котором замените ссылку на Docker-образ Шлюза MQ в репозитории ссылкой на Docker-образ с текущей версией.
Остановите Шлюз MQ.
Загрузите новую конфигурацию Шлюза MQ.
Загрузите новый Deployment.
Запустите Шлюз MQ (если количество реплик (значение параметра
replicas) в Deployment больше 0, Шлюз MQ запустится автоматически).
Дополнительно:
порядок действий при обновлении описан в разделе «Обновление» документа «Руководство по установке»;
остановка/запуск Шлюза MQ описаны в разделе «Сценарии администрирования» документа «Руководство по системному администрированию»;
подготовка конфигурационных артефактов описана в документе «Настройка конфигурации» документа «Порядок настройки Шлюза MQ».
Быстрый старт#
Разработка первого приложения с использованием программного продукта:
Для менеджера MQ Dev-стенда создайте 2 тестовые очереди для входящих и исходящих сообщений (например,
TEST.GW.INиTEST.GW.OUT).Проверьте наличие и при необходимости загрузите артефакт ServiceEntry, задающий точку подключения.
В артефакте должны быть указаны параметры менеджера MQ:
spec.hosts[].<hostname>;spec.ports[].protocol: tcp;spec.ports[].name: tcp;spec.ports[].number:<port>.
Service Entry: ServiceEntry.yml
Подготовьте и загрузите артефакт Config Map c файлом
application.yml, устанавливающий настройки Шлюза MQ.В артефакте должны быть указаны следующие параметры:
режим работы Шлюза MQ:
mq.workMode: async;mq.systemType: sp;
очереди MQ:
mq.connection.receiveQueue: TEST.GW.IN;mq.connection.sendQueue: TEST.GW.OUT;
подключения к MQ:
mq.connection.connections[].hostname: <hostname>;mq.connection.connections[].port: <port>;mq.connection.connections[].channel: <channel>*;mq.connection.connections[].queueManager: <manager>*;
порта Healthcheck:
server.port: 8787;
сервера и клиента gRPC;
grpc.server.serverPort: 5454;grpc.client.settings.default.port: 5454;
маршрутизации:
routing.routeList[].destinationExpression: '"<имя сервиса клиентского приложения>"'.
* следующие параметры используются только при работе с провайдером IBM MQ:
mq.connection.connections[].channel: <channel>;mq.connection.connections[].queueManager: <manager>;
Config Map: ConfigMap.yml
Подготовьте и загрузите артефакт Deployment, устанавливающий параметры запуска контейнера Шлюза MQ.
В артефакте должны быть заданы параметры:
spec.template.spec.volumes[0].configMap.name: <Имя артефакта Config Name>;spec.template.spec.containers[].ports[].containerPort: 5454;spec.template.spec.containers[].ports[].containerPort: 8787;spec.template.spec.containers[].image: <ссылка на Docker-образ Шлюза MQ в целевом репозитории>.
Deployment: Deployment.yml
Подготовьте и загрузите артефакт Service, регистрирующий сервис в Kubernetes (OpenShift).
В артефакте должны быть заданы параметры:
spec.ports[].name: grpc;spec.ports[].protocol: TCP;spec.ports[].port: 5454;spec.ports[].targetPort: 5454.
Service: Service.yml
Проверьте, что Шлюз MQ запущен и готов к работе:
В консоли Kubernetes (OpenShift) откройте меню Workloads → Pods, найдите Pod Шлюза MQ. Перейдите по ссылке в наименовании Pod и проверьте, что он имеет статус Running.
Зайдите в терминал Pod и выполните команду:
sh-4.2$ curl localhost:8799/actuator/health/ping {"status":"UP"}Ответ
{"status":"UP"}показывает, что Шлюз MQ запущен и готов к работе.
Подготовьте Proto-файлы с описанием универсального API Шлюза MQ.
ProtoMessage.proto:syntax = "proto3"; import "google/protobuf/any.proto"; package com.sbt.synapse.gateway; message ProtoMessage { string messageId = 1; string correlationId = 2; string body = 14; map<string, string> systemHeaders = 3; map<string, string> userHeaders = 4; google.protobuf.Any extension = 15; }Heartbeat.proto:syntax = "proto3"; package com.sbt.synapse.gateway; message Heartbeat { int64 timestamp = 1; string messageId = 2; }MessageService.proto:syntax = "proto3"; import "Message.proto"; import "Heartbeat.proto"; package com.sbt.synapse.gateway; option java_multiple_files = true; option java_package = "com.sbt.synapse.gateway.protobuf"; option java_outer_classname = "MessageService"; option objc_class_prefix = "HLW"; service MessageAsyncChannel { rpc processMessage (ProtoMessage) returns (Heartbeat); } service MessageSyncChannel { rpc processMessage (ProtoMessage) returns (ProtoMessage); }MqMessageInformation.proto:syntax = "proto3"; package com.sbt.synapse.gateway.mq; message MqMessageInformation { enum DeliveryMode { UNKNOWN_MODE = 0; NON_PERSISTENT = 1; PERSISTENT = 2; } enum MessageType { UNKNOWN_TYPE = 0; REQUEST = 1; REPLY = 2; REPORT = 4; DATAGRAM = 8; } MessageType messageType = 1; int32 priority = 2; DeliveryMode deliveryMode = 3; int64 expiry = 4; string destinationQueue = 5; string destinationManager = 6; string sourceQueue = 7; string sourceManager = 8; string jmsType = 9; }Создайте проект SpringBoot-приложения.
По proto-описанию сгенерируйте Java-классы, используя proto-компилятор (protoc) или плагин для используемой системы сборки (gradle, maven). Для генерации с помощью protoc:
Установить proto-компилятор (инструкция: https://grpc.io/docs/protoc-installation/)
Скачать gRPC-плагин для proto-компилятора для java (в примере использована версия 1.25.0, можно получить из maven-репозитория: https://mvnrepository.com/artifact/io.grpc/protoc-gen-grpc-java/1.25.0 пункт Files: view all )
Положить файлы .proto в общий каталог
Создать в нем подкаталог java (пустой)
Из каталога с proto-файлами выполнить команду:
В Windows:
protoc --plugin=protoc-gen-grpc-java="C:\Program Files\proto_plugin\protoc-gen-grpc-java-1.25.0-windows-x86_64.exe" -I=. --java_out=java --grpc-java_out=java .\*.protoВ Linux:
protoc --plugin=protoc-gen-grpc-java=~/proto_plugin/protoc-gen-grpc-java-1.25.0-linux-x86_64.exe" -I=. --java_out=java --grpc-java_out=java ./*.proto
В подкаталоге java появится разложенные по папкам в соответствии с именами пакетов java-файлы с кодом сервисов и сообщений.
Добавьте полученные файлы к проекту.
Package
Classes
com.sbt.synapse.gatewayMessage
HeartbeatOuterClasscom.sbt.synapse.gateway.protobufMessageAsyncChannelGrpc
MessageSyncChannelGrpccom.sbt.synapse.gateway.mqMqMessageInformationOuterClassДополнительно укажите зависимости:
<dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.20.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.20.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-core</artifactId> <version>1.20.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty</artifactId> <version>1.20.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-services</artifactId> <version>1.20.0</version> </dependency> <dependency> <groupId>io.github.lognet</groupId> <artifactId>grpc-spring-boot-starter</artifactId> <version>2.4.4</version> </dependency> <dependency> <groupId>io.github.lognet</groupId> <artifactId>grpc-spring-boot-starter</artifactId> <version>2.4.4</version> </dependency>Добавьте импорт полученных классов, а также классов поддержки
protobufиgrpc.Импорт классов:
import com.google.protobuf.Any; import com.sbt.synapse.gateway.HeartbeatOuterClass.Heartbeat; import com.sbt.synapse.gateway.protobuf.MessageAsyncChannelGrpc; import com.sbt.synapse.gateway.Message.ProtoMessage; import com.sbt.synapse.gateway.mq.MqMessageInformationOuterClass.MqMessageInformation; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import org.lognet.springboot.grpc.GRpcService;Добавьте в приложение сервис для приема асинхронных входящих вызовов и отправки подтверждений.
Пример сервиса:
@GRpcService public class GrpcService extends MessageAsyncChannelGrpc.MessageAsyncChannelImplBase { @Override public void processMessage(ProtoMessage message, StreamObserver<Heartbeat> responseObserver) { Logger logger = LoggerFactory.getLogger(GrpcService.class); logger.info("Request: " + message.getBody()); responseObserver.onNext(Heartbeat.newBuilder().setMessageId(message.getMessageId()).setTimestamp(System.currentTimeMillis()).build()); responseObserver.onCompleted(); } }Добавьте в приложение клиента, формирующего простой XML-запрос с установленными
messageIdиExpiry;Пример клиента:
@Component public class GrpcClientSender { @Value("${service.host}") private String serviceHost; @Value("${service.port}") private int servicePort; public void makeRequest() { Logger log = LoggerFactory.getLogger(GrpcClientSender.class); ManagedChannel channel = ManagedChannelBuilder.forAddress(serviceHost, servicePort).usePlaintext().build(); MqMessageInformation mqMessageInformation = MqMessageInformation.newBuilder().setExpiry(30000).build(); ProtoMessage message = ProtoMessage.newBuilder() .setBody("<Rq><RqUID>12345678901234567890123456789012</RqUID></Rq>").setExtension(Any.pack(mqMessageInformation)).setMessageId("123456789012345678901234567890").build(); MessageAsyncChannelGrpc.MessageAsyncChannelBlockingStub messageAsyncChannelBlockingStub = MessageAsyncChannelGrpc.newBlockingStub(channel); try { Heartbeat pongMessage = messageAsyncChannelBlockingStub.processMessage(message); log.info("Response: " + pongMessage.getMessageId()); } catch (Exception e) { System.out.println("catch error " + e.getMessage()); } finally { channel.shutdown(); } } }В
application.propertiesустановите следующие параметры:service.host=localhost service.port=5454 server.port=8787 grpc.port=5454Для публикации образа приложения в docker-registry создайте Dockerfile. В нем укажите, на основе какого базового образа собирать контейнер с приложением, имя jar-файла приложения для установки в контейнер и команду для его запуска.
Dockerfile:
#Ссылка на базовый Docker-образ, нужно указать свой FROM <базовый образ для сборки> ADD <jarName>.jar /app/<jarName>.jar CMD touch /app/<jarName>.jar ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-Dspring.config.location=file:/deployments/config/application.properties","-Djava.security.egd=file:/dev/./urandom","-jar","/app/<jarName>.jar"]Соберите Docker-образ и опубликуйте его в Dev-репозитории.
Настройте артефакты для приложения.
Config Map:
kind: ConfigMap apiVersion: v1 metadata: name: test-app-config data: application.properties: server.port=8787 grpc.port=5454 service.host=<имя сервиса шлюза> service.port=5454Deployment:
kind: Deployment apiVersion: apps/v1 metadata: name: test-app spec: replicas: 1 selector: matchLabels: app: test-app template: metadata: labels: app: test-app annotations: sidecar.istio.io/inject: 'true' spec: volumes: - name: application-config configMap: name: test-app-config items: - key: application.properties path: application.properties defaultMode: 400 containers: - resources: limits: cpu: 200m memory: 400Mi requests: cpu: 100m memory: 330Mi name: test-app env: - name: PROJECT_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.namespace ports: - containerPort: 5454 protocol: TCP - containerPort: 8787 protocol: TCP imagePullPolicy: Always volumeMounts: - name: application-config readOnly: true mountPath: /deployments/config terminationMessagePolicy: File image: >- <ссылка на Docker-образ приложения в dev - репозитории> terminationGracePeriodSeconds: 80Service:
apiVersion: v1 kind: Service metadata: name: test-app spec: selector: name: test-app ports: - name: grpc port: 5454 targetPort: 5454Загрузите артефакты в Kubernetes (OpenShift).
Откройте терминал Pod приложения и проверьте, что в журналах появились записи об успешной отправке сообщений.
Пример журнала клиента:
2021-02-07 10:54:27.740 INFO 14592 --- [ main] c.s.s.workshop.grpc.GrpcClientSender : Response: 123456789012345678901234567890 2021-02-07 10:54:27.743 INFO 14592 --- [ main] c.s.s.workshop.grpc.GrpcClientSender : Response: 123456789012345678901234567890 2021-02-07 10:54:27.746 INFO 14592 --- [ main] c.s.s.workshop.grpc.GrpcClientSender : Response: 123456789012345678901234567890Проверьте прием входящих сообщений в асинхронном режиме. Для этого поместите сообщение в тестовую очередь входящих сообщений. В журнале приложения должна появиться запись о приеме сообщения.
Пример журнала сервера:
2021-02-07 10:54:27.743 INFO 3876 --- [ault-executor-0] c.sbt.synapse.workshop.grpc.GrpcService : Request: <Rq><RqUID>12345678901234567890123456789012</RqUID></Rq> 2021-02-07 10:54:27.746 INFO 3876 --- [ault-executor-0] c.sbt.synapse.workshop.grpc.GrpcService : Request: <Rq><RqUID>12345678901234567890123456789012</RqUID></Rq> 2021-02-07 10:54:27.748 INFO 3876 --- [ault-executor-0] c.sbt.synapse.workshop.grpc.GrpcService : Request: <Rq><RqUID>12345678901234567890123456789012</RqUID></Rq>
Использование программного компонента#
Типовые схемы включения#
Компонент Шлюз MQ предназначен для организации взаимодействия микросервисов внутри кластера Kubernetes (OpenShift) с внешними АС с помощью систем обмена сообщениями (далее - MQ). Для внутри кластера используется протокол gRPC. Для сериализации передаваемых по протоколу gRPC сообщений используется ProtoBuf.
Использование компонента Шлюз MQ позволяет исключить специфичную логику вызова провайдера MQ из логики микросервисов.
Шлюз MQ реализован в виде контейнеризированного Java-приложения, запускаемого в среде оркестрации контейнеров Kubernetes (OpenShift). Для доступа к очередям MQ используется интерфейс JMS.
Шлюз MQ является компонентом Platform V и для полноценной работы требует наличия технологических сервисов платформы, таких как прикладное журналирование, интеграционное журналирование, а также включенного механизма оркестрации сервисов (Service Mesh) Istio.
Шлюз MQ поставляется в виде собранного Docker-образа. Загрузка образа в среду исполнения и настройка его параметров для конкретной прикладной задачи выполняется в файлах конфигурации для среды Kubernetes (OpenShift). Набор этих файлов и составляет прикладной дистрибутив Шлюза MQ.
Один прикладной дистрибутив (Deployment) Шлюза MQ может обеспечивать работу нескольких независимых интеграционных цепочек при условии, что они не требуют установки взаимоисключающих значений конфигурационных параметров. В противном случае для каждой интеграционной цепочки требуется использовать отдельный Deployment Шлюза MQ с собственным набором параметров.
Типовая упрощенная схема показывает место Шлюза MQ в интеграционной цепочке:
Схема. Место Шлюза MQ в интеграционной цепочке
Шлюз MQ может обеспечивать взаимодействие типа «запрос-ответ» как по инициативе внешней системы, так и по инициативе внутренних микросервисов.
Шлюз MQ может подключаться к нескольким менеджерам MQ (при условии, что названия очередей, с которыми работает Шлюза MQ на всех менеджерах, совпадают).
Схема. Подключение к нескольким MQ
Шлюз MQ поддерживает протокол HTTP и позволяет реализовывать взаимодействие MQ-HTTP и HTTP-MQ. Сервис, вызываемый Шлюзом MQ или вызывающий Шлюз MQ по HTTP, может находиться как снаружи, так и внутри Kubernetes (OpenShift).
Схема. Варианты подключения Шлюза MQ
Следует иметь в виду, что для корректной работы Шлюза MQ должны быть правильно сформированы все артефакты Kubernetes (OpenShift)/Istio, обеспечивающие среду его функционирования.
Описание работы Шлюза MQ#
Версия Шлюза MQ определяет собранный и опубликованный образ контейнера приложения. Например, версии 0.2.0.24 и 0.2.0.20 - это разные образы в репозитории. Другие параметры (наименование, тип шлюза и пр.) являются конфигурируемыми и определяются в артефактах Deployment и Config Map при установке в проект Kubernetes (OpenShift).
Настроив нужным образом конфигурацию одного и того же образа контейнера Шлюза MQ (например, mq-gateway-0.2.0.24), можно получить прикладные дистрибутивы шлюзов as1-mq-gateway-sc (шлюз-потребитель для АС as1) и as2-mq-gateway-sp (шлюз-поставщик для АС as2).
Шлюзы потребителя и поставщика разделены, так как:
имеют принципиальные различия при работе в синхронном режиме;
имеют различия в обработке заголовков при работе в асинхронном режиме;
имеют различные конфигурации при работе с АС, которая является одновременно потребителем по одним сервисам и поставщиком по другим.
Настройки Шлюза MQ подробно описаны в разделе «Настройка конфигурации» документа «Порядок настройки Шлюза MQ» и «Верхнеуровневые блоки настроек» документа «Полное описание настроек».
Запуск#
При старте Шлюз MQ устанавливает соединение с менеджером очередей согласно параметрам, указанным в настройках, и начинает прослушивать очереди из списка входящих очередей (очереди запросов для шлюза-потребителя или очереди ответа для шлюза-поставщика).
Параметры подключения к MQ-менеджерам устанавливаются настройками в блоке mq.
Если менеджер очередей недоступен, то Readiness проба Шлюза MQ возвращает значение failed. Если Readiness и Liveness-пробы настроены на общий endpoint (/actuator/health), то в артефакте Deployment для Liveness-пробы должно быть установлено такое значение InitialDelay, которое совместно с periodSeconds и failureThreshold позволят Шлюзу MQ загрузиться и установить соединение с менеджером очередей раньше, чем отсутствие соединения заставит Kubernetes (OpenShift) перезапустить Шлюз MQ.
Чтобы отключение менеджера не приводило к рестарту Pod Шлюза MQ рекомендуется настраивать Readiness и Liveness-пробы на отдельные endpoints (/actuator/health/readiness и actuator/health/liveness соотвественно). В этом случае liveness-проба не проверяет подключение к менеджерам, и их недоступность не приводит к перезапуску пода.
При проверке доступности менеджеров (в реализации Шлюза MQ на Java, для провайдера IBM MQ) возможно подвисание соединений к MQ, что приводит к задержкам срабатывания reаdiness-пробы (и как следствие — к задержке возврата шлюза в работу при восстановлении доступности менеджера). Для снижения влияния таких подвисаний, требуется установить таймауты на соединение c использованием опций JVM:
com.ibm.mq.cfg.MQRCVBLKTO- таймаут ожидания ответа от сервера;com.ibm.mq.cfg.TCP.Connect_Timeout- таймаут на установку соединения.
Их можно задать через переменные окружения в Deployment Шлюза.
Пример:
env:
- name: JAVA_TOOL_OPTIONS
value: >-
-Dcom.ibm.mq.cfg.MQRCVBLKTO=5 -Dcom.ibm.mq.cfg.TCP.Connect_Timeout=5
Пока Readiness-проба не поднята, механизмы Kubernetes (OpenShift) не включают этот Pod Шлюза MQ в балансировку, и трафик на него не поступает. Для Readiness-пробы значения InitialDelay, periodSeconds и failureThreshold должны быть существенно меньше, чтобы не создавать излишних задержек при включении экземпляра шлюза под нагрузку.
Конфигурирование артефакта Deployment описано в разделе «Подготовка общих артефактов Kubernetes (OpenShift)» документа «Порядок настройки Шлюза MQ».
Передача сообщений от Внешней АС Микросервису (Тип шлюза: шлюз потребителя)#
Задачами шлюза-потребителя являются:
получение запроса Внешней АС через систему обмена сообщениями;
трансформация запроса в формат protoBuf;
отправка Proto-сообщения внутреннему микросервису с помощью вызова по gRPC, поднятого им универсального API;
прием ответа от внутреннего микросервиса;
трансформация ответа в формат системы обмена сообщениями;
отправка ответа через систему обмена сообщениями Внешней АС.
Режим потребителя устанавливается настройкой mq.systemType = sc.
Асинхронный режим работы#
В асинхронном режиме Шлюз MQ не связывает ответ, полученный от микросервиса, с отправленным ему запросом. Передача запроса и ответа происходит независимо друг от друга.
Асинхронный режим устанавливается настройкой mq.workMode = async.
Прием запроса#
При появлении в очереди запросов входящего сообщения Шлюз MQ считывает сообщение из очереди.
Входящая очередь для асинхронных запросов устанавливается настройкой mq.connection.receiveQueue.
Из полученного сообщения Шлюз MQ извлекает значения параметров, определяющих дальнейшую маршрутизацию вызова. Эти параметры могут содержаться как в заголовках, так и в теле сообщения либо задаваться константой. Процесс маршрутизации описан в разделе «Маршрутизация gRPC-вызовов в Шлюзе MQ» текущего документа.
Параметры маршрутизации устанавливаются в секции routing.
На основании данных полученного запроса Шлюз MQ формирует Proto-сообщение и вызывает по gRPC микросервис в Kubernetes (OpenShift), определенный по значениям параметров маршрутизации, передавая ему сформированное сообщение. Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса JMS в gRPC» документа «Преобразование интерфейсов».
Необходимо иметь в виду, что вызов по gRPC всегда выполняется синхронно. Если в конфигурации Шлюза MQ указан асинхронный режим работы, то это означает, что Шлюз MQ, выполнив вызов по gRPC, получает не результат обработки запроса на стороне поставщика сервиса, а подтверждение того, что запрос успешно получен поставщиком сервиса. Это специальное сообщение - HeartBeat, которое содержит идентификатор исходного сообщения и время его получения.
Отправка ответа#
Чтобы передать информационный ответ через Шлюз MQ, работающий в асинхронном режиме, поставщик сервиса должен самостоятельно инициировать его вызов по gRPC.
Схема. gRPC-вызов Шлюза MQ
При работе в асинхронном режиме ответ может быть получен любым Шлюзом MQ, а не только тем, который обработал запрос (на схеме Шлюзы MQ отмечены голубым цветом).
Чтобы обеспечить возможность работы в асинхронном режиме, вызываемый микросервис должен поднять асинхронный API. Более подробно API описан в разделе «Интерфейс gRPC» документа «Спецификация интерфейса gRPC».
Для приема ответов от поставщика сервиса Шлюз MQ поднимает асинхронный API gRPC.
При вызове этого API поставщиком Шлюз MQ принимает Proto-сообщение с ответом.
Шлюз MQ преобразует полученное сообщение в MQ-сообщение. Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса gRPC в JMS» документа «Преобразование интерфейсов»
В зависимости от значения параметра sendToCustomDestination в разделе mq-конфигурации Шлюза MQ и информации, полученной во входящем сообщении, Шлюз MQ может передать сообщение:
в очередь и менеджер, указанные в настройках шлюза;
в очередь и менеджер, указанные в блоке Destination Proto-сообщения;
в очередь и менеджер, указанные в gRPC-заголовках входящего вызова.
Порядок определения очереди и менеджера, в которые Шлюз MQ отправляет ответ, описан в разделе «Правила определения очереди и менеджера сообщения при отправке сообщения в MQ» текущего документа.
Если отправка завершилась успешно, Шлюз MQ возвращает Heartbeat в ответе.
Если в процессе отправки возникает ошибка, Шлюз MQ завершает gRPC-вызов ошибкой с кодом 603 (ошибка взаимодействия с MQ).
Синхронный режим работы#
При синхронном режиме работы ответ связан с запросом сессией gRPC-вызова.
Синхронный режим устанавливается настройкой mq.workMode = sync.
Прием запроса#
При появлении в очереди запросов входящего сообщения Шлюз MQ считывает сообщение из очереди.
Входящая очередь для синхронных запросов (mq.workMode=sync) устанавливается настройкой mq.connection.receiveQueue.
Из полученного сообщения Шлюз MQ извлекает значения параметров, определяющих дальнейшую маршрутизацию вызова. Процесс маршрутизации описан в разделе «Маршрутизация gRPC-вызовов в Шлюзе MQ» текущего документа.
На основании данных полученного запроса Шлюз MQ формирует Proto-сообщение. Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса JMS в gRPC» документа «Преобразование интерфейсов».
При работе в синхронном режиме Шлюз MQ получает JMS-сообщение, преобразует его в Proto-сообщение и вызывает синхронный API, который должен быть поднят вызываемым сервисом. API подробно описан в разделе «Интерфейс gRPC» документа «Спецификация интерфейса gRPC».
Отправка ответа#
Шлюз MQ ожидает ответ в течение времени тайм-аута, заданного в конфигурации в секции grpc. Для исключения зависания gRPC-соединений при синхронных вызовах необходимо обязательно задавать тайм-аут.
Тайм-аут устанавливается настройкой grpc.client.settings.timeout.
Если в течении этого времени ответ не получен, Шлюз MQ прерывает вызов и записывает тайм-аут в журнал.
В случае успешного завершения вызова, Шлюз MQ получает Proto-сообщение с ответом от поставщика сервиса. В противном случае в журнал записывается ошибка.
Далее, аналогично работе в асинхронном режиме, Шлюз MQ преобразует полученное сообщение в JMS-сообщение и отправляет его менеджеру в очередь, указанную в настройках Шлюза MQ и согласно информации, содержащейся в ответе.
Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса gRPC в JMS» документа «Преобразование интерфейсов».
Порядок определения очереди и менеджера, в которые Шлюз MQ отправляет ответ, описан в разделе «Правила определения очереди и менеджера сообщения при отправке сообщения в MQ» текущего документа.
Если отправка завершилась неудачей, Шлюз MQ записывает в журнал ошибку.
Схема. Синхронный режим работы шлюза-потребителя
Передача сообщений от Микросервиса Внешней АС (Тип шлюза: шлюз поставщика)#
Задачами шлюза поставщика являются:
прием по gRPC запроса внутреннего микросервиса;
трансформация запроса в формат системы обмена сообщениями;
отправка запроса через систему обмена сообщениями внешней АС;
прием ответа от внешней АС через систему обмена сообщениями;
трансформация ответа в формат protoBuf;
отправка Proto-сообщения внутреннему микросервису;
мониторинг и очистка очереди ответов от просроченных сообщений, которые не были считаны.
Режим поставщика устанавливается настройкой mq.systemType = sp.
Асинхронный режим работы#
В асинхронном режиме Шлюз MQ не связывает запрос, отправленный внешней АС, с полученным от нее ответом. Передача запроса и ответа происходит независимо друг от друга.
Асинхронный режим устанавливается настройкой mq.workMode = async.
Отправка запроса#
При старте Шлюз MQ поднимает на своей стороне асинхронный API gRPC.
Схема. Асинхронный режим работы шлюза-поставщика
Чтобы отправить запрос во внешнюю систему, микросервис внутри Kubernetes (OpenShift) вызывает gRPC API Шлюза MQ.
Шлюз MQ преобразует полученное сообщение в JMS-сообщение. Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса gRPC в JMS» документа «Преобразование интерфейсов».
В зависимости от настройки sendToCustomDestination в разделе mq-конфигурации Шлюза MQ и информации, полученной во входящем сообщении, Шлюз MQ может отправить сообщение:
в очередь и менеджер, указанные в настройках Шлюза MQ;
в очередь и менеджер, указанные в блоке Destination Proto-сообщения;
в очередь и менеджер, указанные в gRPC-заголовках входящего вызова.
Порядок определения очереди и менеджера, в которые Шлюз MQ отправляет сообщение, подробно описан в разделе «Правила определения очереди и менеджера сообщения при отправке сообщения в MQ» текущего документа.
Если отправка завершилась успешно, то Шлюз MQ возвращает Heartbeat в ответе.
Если в процессе отправки возникает ошибка, то Шлюз MQ завершает gRPC-вызов ошибкой с кодом 603 (ошибка взаимодействия с MQ).
Внешняя АС считывает сообщение из очереди запросов, выполняет свою бизнес-функцию и помещает ответ в очередь ответов.
Прием ответа#
В асинхронном режиме запущенные экземпляры Шлюза MQ выполняют конкурентное считывание из очереди ответов. Ответ на запрос может быть считан не тем экземпляром Шлюза MQ, который обрабатывал исходный запрос (голубая стрелка на схеме).
Не имея информации исходного запроса или заранее установленного соединения, Шлюз MQ извлекает из полученного сообщения значения параметров, определяющих дальнейшую маршрутизацию вызова. Эти параметры могут содержаться как в заголовках, так и в теле сообщения, либо задаваться константой.
Процесс маршрутизации описан в разделе «Маршрутизация gRPC-вызовов в Шлюзе MQ» текущего документа.
Параметры маршрутизации устанавливаются в секции настроек routing.
Шлюз MQ преобразует JMS-сообщение в Proto-сообщение и отправляет его в определенный по параметрам маршрутизации микросервис, вызывая его асинхронный gRPC API.
Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса JMS в gRPC» документа «Преобразование интерфейсов».
Если в процессе отправки возникает ошибка, то Шлюз MQ завершает gRPC-вызов ошибкой с кодом 603 (ошибка взаимодействия с MQ).
Синхронный режим работы#
В синхронном режиме Шлюз MQ связывает ответ внешней АС с отправленным ей запросом по CorrelId, который должен быть равен MsgId.
Синхронный режим устанавливается настройкой mq.workMode = sync.
Отправка запроса#
В синхронном режиме работы Шлюз MQ при старте поднимает синхронный API gRPC.
Для отправки запроса во внешнюю систему микросервис внутри Kubernetes (OpenShift) вызывает gRPC API Шлюза MQ. В случае синхронного вызова gRPS-сессия остается активной до получения ответа от поставщика или истечения тайм-аута.
Таймаут устанавливается настройкой mq.sync-receiver.maxProcessingTime.
В режиме синхронного поставщика при отправке сообщения в очередь Шлюз MQ проверяет время жизни (Time To Live, Expiry) отправляемого сообщения, и если оно превышает таймаут установленный на шлюзе, или не ограничено, принудительно устанавливает его равным таймауту.
Шлюз MQ преобразует полученное Proto-сообщение в JMS-сообщение. Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса gRPC в JMS» документа «Преобразование интерфейсов».
В зависимости от значения параметра sendToCustomDestination в разделе конфигурации Шлюза MQ и информации, полученной во входящем сообщении, Шлюз MQ может отправить сообщение:
в очередь и менеджер, указанные в настройках шлюза;
в очередь и менеджер, указанные в блоке Destination Proto-сообщения;
в очередь и менеджер, указанные в gRPC-заголовках входящего вызова.
Порядок определения очереди и менеджера, в которые Шлюз MQ отправляет ответное сообщение, подробно описан в разделе «Правила определения очереди и менеджера сообщения при отправке сообщения в MQ» текущего документа.
Для синхронного ответа внешняя АС-поставщик должна записать значение MsgId запроса в CorrelId ответа. Кроме того, ответное сообщение должно быть считано тем же экземпляром Шлюз MQ, который отправил запрос, поэтому внешняя система должна отвечать по ReplyToQ/ReplyToQMgr.
Очередь для ответа (ReplyToQ) в Proto-сообщении устанавливает микросервис, инициировавший запрос. ReplyToQMgr обычно не заполняется и подставляется клиентской библиотекой/MQ-менеджером.
Внешняя АС-поставщик считывает сообщение из очереди запросов, выполняет бизнес-функцию и помещает ответ в очередь ответов в ReplyToQ/ReplyToQMgr.
В рамках доработки Шлюза MQ были реализованы дополнительные режимы синхронного чтения ответов со всех подключенных менеджеров. Подробнее описано в разделе Расширенные режимы работы синхронного поставщика текущего документа.
Прием ответов#
Шлюз MQ проверяет очередь ответов по списку отправленных запросов и при появлении сообщения с нужным CorrelId считывает его из очереди.
Если тайм-аут gRPC-соединения еще не истек, Шлюз MQ преобразует полученный ответ в Proto-сообщение, возвращает его в качестве ответа микросервису-инициатору и завершает сессию.
Если в полученном ответе отсутствует (пустое) тело сообщения, то Шлюз записывает в системный лог предупреждение: «Отсутствует тело в ответе на сообщение <ID сообщения>».
Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса JMS в gRPC» документа «Преобразование интерфейсов».
Если истек таймаут на Шлюзе MQ или на вызывающем Микросервисе (смотря что произошло раньше), Шлюз MQ закрывает входящее соединение и прекращает ожидание и обработку ответа.
Схема. Синхронный режим работы шлюза-потребителя
Мониторинг и очистка очереди ответов#
Возможна ситуация, когда при синхронном вызове АС-поставщик в ответе не устанавливает значение Expiry (Expiry=-1). Если у АС-поставщика возникают задержки с ответом, превышающие тайм-аут, установленный в настройках шлюза, то эти сообщения не считываются и начинают накапливаться в очереди ответов. Для предотвращения переполнения очередей в Шлюз MQ встроен механизм, который отслеживает наличие просроченных сообщений в очередях: Шлюз MQ проверяет время нахождения сообщения в очереди, и если оно превышает установленный порог, удаляет его.
Параметры мониторинга просроченных сообщений устанавливаются в настройках
mq.monitoring.messages.expiry-listener.
Важно! В текущей реализации Шлюз MQ для работы мониторинга просроченных сообщений в настройках шлюза обязательно должен быть сконфигурирован порт сервера gRPC.
Порт сервера gRPC устанавливается настройкой grpc.server.serverPort.
Остановка Шлюза MQ#
При штатной остановке Шлюза MQ:
Прекращается прием новых запросов по каналам gRPC и MQ.
Завершается обработка ранее поступивших сообщений.
Завершается отправка обработанных сообщений.
Приложение Шлюза MQ завершает работу.
Шлюз MQ ожидает остановки listeners MQ-очередей в течение времени, заданного в его настройках.
Время остановки listeners MQ устанавливается настройкой terminationGracePeriodSeconds.
Если приложение не завершает работу за время, указанное в артефакте Deployment, то оно принудительно останавливается сигналом kill механизмами Kubernetes (OpenShift).
Время ожидания Kubernetes (OpenShift) до принудительной остановки приложения устанавливается в Deployment параметром spec.template.spec.terminationGracePeriodSeconds.
Журналирование#
В процессе работы Шлюз MQ фиксирует события в журналах.
Системное журналирование#
В системном журнале фиксируются события, возникающие в приложении в процессе его работы. По умолчанию системный журнал выводится в консоль контейнера приложения.
Вы можете получить доступ к системному журналу с помощью веб-интерфейса Kubernetes (OpenShift): Workloads → Pods → имя Pod → вкладка Logs.
Чтобы выгрузить системный журнал через консоль клиента Kubernetes (OpenShift), выполните команду:
oc logs -c <имя контейнера> <имя Pod> > <имя файла>.txt
Канал вывода журнала, уровень журналирования и шаблон записи события задается в файле logback.xml. Для изменения настроек по умолчанию в конфигурации шлюза вы можете задать расположение собственного файла logback.xml.
Расположение собственного файла logback.xml устанавливается настройкой logging.config.
Прикладное журналирование#
В прикладном журнале фиксируются следующие шаги прохождения интеграционной цепочки:
Шлюз MQ считал сообщение JMS/MQ из очереди внешней системы;
Шлюз MQ вызвал микросервис по gRPC и отправил ему сообщение;
Шлюз MQ получил сообщение по gRPC от микросервиса;
Шлюз MQ отправил JMS/MQ-сообщение в очередь внешней системы;
Ошибка на Шлюзе MQ.
Прикладной журнал записывается на смонтированный в контейнер приложения ресурс, который задается в конфигурации шлюза. По умолчанию прикладной журнал записывается в файл /var/log/synapse/messages1.log.
Прикладные записи журнала могут быть извлечены из контейнера приложения и отправлены в подсистему журналирования платформы. Для этого в Deployment Шлюза MQ должен быть сконфигурирован Sidecar-контейнер с компонентом платформы - Агентом журналирования. Порядок подключения и использования Агента журналирования приведено в документе Руководство прикладного разработчика на этот компонент.
Интеграционное журналирование (трассировка)#
Интеграционное журналирование выполняется средствами Service Mesh.
Sidecar-контейнер istio-proxy обеспечивает передачу трассировочной информации из транспортных заголовков каждого исходящего gRPC-вызова в систему интеграционного журналирования.
Шлюз MQ заполняет заголовки трассировки в соответствии с заданными настройками.
Правила заполнения заголовков трассировки устанавливаются в секции настроек tracing.
Горизонтальное масштабирование#
Горизонтальное масштабирование Шлюза MQ реализуется и регулируется системными механизмами Kubernetes (OpenShift)/Istio. В любой момент, при необходимости, могут работать одновременно произвольное количество экземпляров Шлюза MQ с общей конфигурацией (произвольное количество Pods одного Deployment). Количество запущенных экземпляров определяется настройкой в Deployment и ограничивается только лимитами ресурсов проекта в Kubernetes (OpenShift).
Количество запускаемых экземпляров (Pods) Шлюза MQ задается в артефакте Deployment параметром spec.replicas.
Метрики#
Для дополнительного мониторинга параметров в Шлюзе MQ реализован механизм сбора метрик его работы. Метрики публикуются на HTTP-интерфейсе шлюза в формате prometheus и доступны по URI /actuator/prometheus (метод GET).
Порт, на котором запускается HTTP-сервис, задается настройкой server.port конфигурации шлюза.
Вы можете получить текущий актуальный список метрик, выполнив команду в терминале контейнера Шлюза MQ:
curl localhost:<номер http порта шлюза>/actuator/prometheus
Мониторинг сертификатов#
Шлюз MQ в процессе работы проверяет срок действия используемых сертификатов SSL, и публикует метрику certs выводящую эту информацию, вид метрики приведен ниже, Значения параметров указаны для примера и в действующей системе будут отличаться.
certs{certLabel="{Alias='gateway', Subject='CN=GATEWAY', Expiration date='01.03.2122', Issuer='CN=SYNAPSEDEV_ROOT_CA'}",} 364.0
certs{certLabel="{Alias='root-ca', Subject='CN=SYNAPSEDEV_ROOT_CA', Expiration date='26.07.3021', Issuer='CN=SYNAPSEDEV_ROOT_CA'}",} 3649.0
Мониторинг используемых ресурсов#
Для мониторинга утилизируемых Шлюзом MQ ресурсов используются прикладные метрики:
Метрика |
Комментарий |
|---|---|
|
использование CPU гостевой ОС контейнера в целом |
|
использование CPU процессом JVM |
|
Количество используемой памяти для heap |
|
Количество используемой памяти кроме heap |
|
Общее количество используемой памяти |
|
Количество памяти в байтах выданное JVM для heap |
|
Количество памяти в байтах выданное JVM кроме heap |
|
Общее количество памяти в байтах выданное JVM |
|
Максимальное количество памяти для heap в байтах, которое может быть использовано при управлении памятью |
|
Максимальное количество памяти кроме heap в байтах, которое может быть использовано при управлении памятью |
|
Общее максимальное количество памяти в байтах, которое может быть использовано при управлении памятью |
|
Оценка объема памяти, используемой JVM под пул |
|
Количество активных потоков (daemon и non-daemon) |
Для расчета производных показателей используются также платформенные метрики публикуемые Kubernetes:
Метрика |
Комментарий |
|---|---|
|
рабочий набор контейнера = RSS+Cache , кеш - уровня ОС и не регулируется приложением |
|
лимит памяти контейнера |
|
RSS контейнера |
Метрики Kubernetes публикуются для всех контейнеров в Pod, поэтому при расчете показателей их нужно фильтровать по контейнеру приложения, а не только по Pod.
Для мониторинга ресурсов в системе визуализации (Grafana) можно настроить следующие показатели:
heap %
=sum(jvm_memory_used_bytes{instance="$instance",area="heap"})/sum(jvm_memory_max_bytes{instance="$instance",area="heap"})*100
Важный показатель для контроля заполнения heap и ошибок OOM heap space.
jvm memory
~=sum(jvm_memory_used_bytes{instance="$instance", area="nonheap",id!="miscellaneous non-heap storage"})+sum(jvm_memory_committed_bytes{instance="$instance",area="heap"})+sum(jvm_buffer_memory_used_bytes{application="$app", instance="$instance", id="direct"})+sum(jvm_threads_live_threads{instance="$instance"})*1024*(256*2)
Это примерная память процесса JVM (точно подсчитать нельзя), причем при простое ее сбрасывает ОС или JVM и разрыв между RSS и JVM вырастает.
Container working set %
=max(container_memory_working_set_bytes{pod="$instance", container=~".*gate.*"})/max(container_spec_memory_limit_bytes{pod="$instance", container=~".*gate.*"})*100
Это процент использования памяти контейнера - то, что отображается в стандартном мониторинге.
Container working set
=max(container_memory_working_set_bytes{pod="$instance", container=~".*gate.*"})
Это рабочий набор контейнера
Container rss %
=max(container_memory_rss{pod="$instance", container=~".*gate.*"})/max(container_spec_memory_limit_bytes{pod="$instance", container=~".*gate.*"})*100
Это % RSS от лимита памяти контейнера
Container rss
=max(container_memory_rss{pod="$instance", container=~".*gate.*"})
Это RSS контейнера
В примерах выше переменные содержащие $app - имя Deployment, а $instance - имя Pod шлюза.
Многоточечное подключение#
При многоточечном подключении один Deployment Шлюза MQ может быть подключен к нескольким MQ-менеджерам. При отправке сообщений в исходящие очереди выполняется балансировка по точкам подключения (хост: порт, менеджер, канал).
Важно! Это внутренняя функция Шлюза MQ, которая никак не связана с балансировкой сообщений по менеджерам в кластере MQ.
При этом Шлюз MQ периодически проверяет доступность менеджеров. Если проверка завершается неудачей, менеджер исключается из балансировки до следующей успешной проверки. Из балансировки также исключаются менеджеры отключенные настройкой disabledManagerIds, причем по менеджерам отключенным настройкой, проверка доступности не выполняется. Если доступные менеджеры отсутствуют, то Шлюз MQ опускает Readiness-пробу, и трафик на этот экземпляр (Под) Шлюза MQ блокируется механизмами Kubernetes (OpenShift)/Istio.
Если менеджер доступен, но при отправке сообщения в MQ возникает ошибка, то Шлюз MQ выполняет попытку отправки через другой менеджер из числа доступных. Если сообщение не удается отправить ни через один менеджер, то Шлюз MQ возвращает ошибку вызывавшему микросервису, и опускает Readiness-пробу.
Шлюз MQ читает запросы/асинхронные ответы из входящих очередей всех доступных менеджеров.
В синхронном режиме работы Шлюз MQ читает ответ из того же менеджера, через который был отправлен запрос.
Настройки проверки доступности менеджеров устанавливается в секции mq.connection.health конфигурации шлюза. Эта секция имеет приоритет перед настройкой mq.connection.connectionCheckPeriod, которая теперь является устаревшей (deprecated).
В секции mq.connection.health можно задать период опроса, задержку, и, отдельно, количество попыток перед вводом менеджера в балансировку, и выводом из балансировки. Подробнее см. раздел «Верхнеуровневые блоки настроек» документа «Полное описание настроек».
Алгоритмы проверки доступности менеджеров#
Через настройку mq.connection.health.type можно выбрать алгоритм проверки доступности менеджеров, восстановления подключений и балансировки отправки сообщений в менеджеры.
Common - дефолтный алгоритм проверки. В этом алгоритме используется балансировщик RoundRobin. При каждой проверки доступности менеджера создается отдельное подключение.
Smart - оптимизированный алгоритм проверки. Уменьшает утилизацию на менеджерах. В этом алгоритме используется балансировщик LeastConn, который учитывает нагруженность на Менеджеры. В случае «зависания» подключения отправляет в более быстрый менеджер сообщение.
HTTP-интерфейс#
HTTP-интерфейс в Шлюзе MQ предназначен для организации взаимодействия между внешними АС с разными протоколами взаимодействия - MQ и HTTP.
Использование Шлюза MQ с HTTP-интерфейсом позволяет осуществлять вызов MQ/HTTP с возможностью трансформации транспортных заголовков HTTP→MQ/MQ→HTTP (без преобразования тела сообщения).
Не реализована возможность вызова HTTP→gRPC и gRPC→HTTP.
Определение параметров маршрутизации по телу сообщения работает только с форматами тела сообщения XML и JSON. Возможность работы с другими форматами не поддерживается.
Тип шлюза: шлюз потребителя (MQ→HTTP)#
При появлении в очереди запросов входящего сообщения Шлюз MQ считывает сообщение из очереди.
Из полученного сообщения Шлюз MQ извлекает значения параметров, определяющих дальнейшую маршрутизацию вызова. Эти параметры могут содержаться как в заголовках, так и в теле сообщения, либо задаваться константой. Правила определения параметров маршрутизации устанавливаются в секции настроек
routing.
Для вызова по HTTP в параметрах маршрута должен быть задан тип транспорта HTTP. Тип транспорта устанавливается настройкой routing.routeList[].transportType.
Шлюз MQ преобразует полученный запрос в httpMessage и вызывает по HTTP сервис, заданный в параметрах секции HTTP-конфигурации Шлюза MQ, передавая ему сформированное сообщение. Параметры по умолчанию вызываемого сервиса устанавливаются в блоке настроек http.
Параметры вызываемого сервиса для конкретного маршрута устанавливаются в секции настроек routing.routeList[].http.
Важно! В секцию
httpвходят настройки SSL для HTTP-вызова. Эти настройки должны храниться и монтироваться в артефакте типа secret. В отдельный конфигурационный файл можно вынести только настройки SSL по умолчанию из блокаhttp.
Правила преобразования MQ-заголовков в заголовки HTTP сообщения устанавливаются в настройке transform.mq-http.headers.
Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса JMS в HTTP» документа «Преобразование интерфейсов».
В асинхронном режиме после отправки сообщения Шлюз MQ завершает обработку.
В синхронном режиме Шлюз MQ ожидает ответ в течение времени тайм-аута, заданного в конфигурации в секции http. Если в течении этого времени ответ не получен, Шлюз MQ прерывает вызов и записывает в журнал тайм-аут. В случае успешного завершения вызова, Шлюз MQ получает ответ от поставщика сервиса. В противном случае в журнал записывается ошибка.
Шлюз MQ преобразует полученное сообщение в MQ-сообщение. Правила преобразования заголовков HTTP-сообщения в заголовки MQ-сообщения устанавливаются в настройке
transform.http-mq.
Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса HTTP в JMS» документа «Преобразование интерфейсов».
Шлюз MQ отправляет сообщение в менеджер и очередь, определяемые из настроек шлюза и информации, содержащейся в ответе. Порядок определения очереди и менеджера, в которые Шлюз MQ отправляет ответное сообщение, подробно описан в разделе «Правила определения очереди и менеджера сообщения при отправке сообщения в MQ» текущего документа.
Если отправка завершилась неудачей, Шлюз MQ записывает ошибку в журнал.
Тип шлюза: шлюз поставщика (HTTP→MQ)#
Шлюза MQ при запуске поднимает сервис согласно параметрам, указанным в настройках в секции server конфигурации шлюза. Параметры HTTP-сервиса устанавливаются в блоке настроек server. Когда сервис получает HTTP-запрос, Шлюза MQ преобразует его в MQ-сообщение. Правила преобразования заголовков HTTP-сообщения в заголовки MQ-сообщения устанавливаются в секции настроек transform.http-mq.
Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса JMS в HTTP» документа «Преобразование интерфейсов».
В зависимости от значения параметра sendToCustomDestination в разделе mq конфигурации Шлюза MQ и информации, полученной во входящем сообщении, Шлюз MQ может отправить сообщение:
в очередь и менеджер, указанные в настройках шлюза;
в очередь и менеджер, указанные в блоке Destination Proto-сообщения;
в очередь и менеджер, указанные в gRPC-заголовках входящего вызова.
Порядок определения очереди и менеджера, в которые Шлюз MQ отправляет ответ, подробно описан в разделе «Правила определения очереди и менеджера сообщения при отправке сообщения в MQ» текущего документа.
Для синхронного ответа АС-поставщик должна установить значение MsgId запроса в CorrelId ответа. Кроме того, ответ должен быть считан тем же экземпляром Шлюза MQ, который отправил запрос, поэтому внешняя система должна отвечать по ReplyToQ/ReplyToQMgr.
ReplyToQMgr обычно не заполняется и подставляется клиентской библиотекой/MQ-менеджером. Значение ReplyToQ должно быть передано клиентом в запросе.
В асинхронном режиме Шлюз MQ отправляет преобразованное сообщение и завершает обработку.
В синхронном режиме Шлюз MQ отправляет преобразованное сообщение и синхронно ожидает ответ с CorrelId, равным MsgId запроса. Таймаут устанавливается настройкой mq.sync-receiver.maxProcessingTime.
АС-поставщик считывает сообщение из очереди запросов, выполняет бизнес-функцию и помещает ответ в очередь ответов по ReplyToQ/ReplyToQMgr.
Шлюз MQ считывает ответ и преобразует его в HttpMessage. Правила преобразования MQ-заголовков в заголовки HTTP-сообщения задаются в настройке transform.mq-http.headers.
Порядок формирования сообщения подробно описан в разделе «Преобразование интерфейса JMS в HTTP» документа «Преобразование интерфейсов».
Шлюз MQ возвращает преобразованное сообщение инициатору запроса.
Прогрев#
Для снижения латентности (времени обработки сообщений) при запуске нового экземпляра (Pod) Шлюза MQ в нем реализована процедура прогрева.
Перед тем, как Шлюз MQ поднимает Readiness- и Liveness-пробы, он выполняет внутреннюю отправку служебных сообщений. Это позволяет Java-машине оптимизировать исполнение прикладного кода и повысить его быстродействие.
После выполнения процедуры прогрева Шлюз MQ поднимает Readiness- и Liveness-пробы. Шлюз MQ начинает получать рабочий трафик.
Прогрев на Шлюзе MQ включается в настройках секции warmup.
Прогрев не реализован для синхронного режима шлюза-потребителя.
В версии Шлюза MQ 0.2.0.25-AOT используется виртуальная Java-машина OpenJ9 JVM11. OpenJ9 - это виртуальная машина Java, разрабатываемая Eclipse Foundation. Компилятор AOT динамически компилируют методы Java в собственный код AOT во время выполнения и сохраняет их в кеше общих классов (AOT cache). Это позволяет виртуальной машине быстрее запускать приложение при следующем запуске, поскольку ей не нужно тратить время на интерпретацию методов Java.
Шлюз MQ на OpenJ9 - это Docker-образ Java-приложения Шлюза MQ, запущенного на виртуальной машине OpenJ9 с подготовленным AOT-кешем. AOT-кеш был подготовлен при запуске Шлюза MQ на OpenJ9 и прогоне нагрузки через механизм прогрева средствами DevOps при сборке образа перед публикацией в registry.
Расширенные режимы работы синхронного поставщика#
Шлюз MQ реализует три дополнительных варианта работы в синхронном режиме:
1. Режим динамического создания очередей для ответов для каждого Pod. (не работает c провайдером IBM в реализации Шлюза MQ на Java)
В этом режиме Шлюз MQ при старте каждого Pod создает отдельную очередь связанную с именем Pod.
При отправке запроса АС-поставщику конкретный Pod Шлюза MQ в заголовке ReplyToQ передает имя «своей» очереди для ответа.
АС поставщик адресует ответ в указанную очередь.
Ответ считывается тем же подом Шлюза, который отправлял запрос.
В этом варианте Pod Шлюза MQ должен проверять ответы на каждом менеджере MQ, к которому он подключен, но поскольку он работает только со своими очередями, чтение по селектору не требуется.
Динамические очереди именуются на основе имени Pod.
Для провайдера IBM:
Для создания динамических очередей на брокере должна быть создана Model Queue. Для целей многопоточной вычитки у этой очереди должен быть установлен режим Shared:
DEFINE QMODEL('DYN') DEFSOPT(SHARED) DEFTYPE(SHAREDYN) REPLACE
Добавлены настройки модели и разделителя для имени очереди:
mq:
syncReceiver:
modelName: DYN
dynamicQNameSeparator: .DQ.
В настройках шлюза нужно сослаться на на эту модель и указать разделитель, который будет использоваться для создания очереди.
Имя очереди формируется так:
mq.syncReceiver.defaultReceiveQueue + mq.syncReceiver.dynamicQNameSeparator + значимая_часть_имени_пода_без_дефисов_с_точкой.
Для пода «go-gateway-5dddcc6455-vd4j9» и конфигурации вида:
mq:
syncReceiver:
defaultReceiveQueue: RESPONSE
dynamicQNameSeparator: .DQ.
имя очереди для ответа получит вид RESPONSE.DQ.5dddcc6455.vd4j9
2. Режим синхронного чтения ответов из всех подключенных брокеров по корреляционному идентификатору
В этом случае используется схема адресации с одной очередью на адрес. Сценарий:
при отправке запроса АС-поставщику конкретный Pod Шлюза MQ в заголовке ReplyToQ передает имя общей очереди для ответа;
АС поставщик отвечает в эту очередь, не уточняя имя брокера или имя Pod шлюза;
Конкретный Pod Шлюза читает очереди ответов на всех подключенных брокерах, но считывает только «свои» сообщения, используя селектор по корреляционному идентификатору (JMSCorrelationID).
3. Режим синхронного чтения ответов из всех подключенных брокеров по селектору
Это смешанный режим - вычитка происходит со всех менеджеров по селектору, но селектор не по CorrelId, а по фиксированному, заранее созданному селектору, индивидуальному для каждого Pod (соответствует имени Pod).
В этом случае используется схема адресации с одной очередью на адрес. Сценарий:
при отправке запроса АС-поставщику конкретный Pod Шлюза MQ передает:
в заголовке ReplyToQ имя общей очереди для ответа.
в заголовке GWResponseSelector значение для селектора, по которому текущий Pod читает сообщения из очереди для ответа.
АС поставщик отвечает в эту очередь, не уточняя имя брокера или имя Pod шлюза, но обязательно сохраняет и передает в сообщении заголовок GWResponseSelector и его значение
Конкретный Pod Шлюза читает очереди ответов на всех подключенных брокерах, но считывает только «свои» сообщения, используя селектор по GWResponseSelector.
В качестве значения селектора используется только имя Pod в заголовке GWResponseSelector
Используемый заголовок можно поменять через настройку
mq.sync-receiver.selectorName.
Выбор режима работы:
Выбор осуществляется настройкой:
mq:
sync-receiver:
replyToMode: UNIQUE #SHARED # SHARED_SINGLE # SHARED_SELECTOR
#selectorName: GWResponseSelector # для режима SHARED_SELECTOR
где UNIQUE - вычитка из динамической очереди (п.1), SHARED и SHARED_SINGLE - вычитка по CorrelId (п.2) из всех или одного менеджера (в который был отправлен запрос) соотвественно, SHARED_SELECTOR - вычитка из всех менеджеров по селектору (п.3)
Для сохранения обратной совместимости по умолчанию используется режим SHARED_SINGLE.
Режим ALL - совмещенный синхронный и асинхронный режим#
Начиная с версии 0.2.0.17 Шлюз MQ может работать в совмещенном (синхронном и асинхронном) режиме. Для этого предусмотрен режим работы ALL.
Совмещенный режим устанавливается настройкой mq.workMode = all.
Система-потребитель может отправлять запросы в две очереди - для синхронных и для асинхронных запросов. Шлюз MQ в режиме потребителя слушает обе очереди. При получении асинхронного запроса он вызывает микросервис-поставщик асинхронно, как описано выше при асинхронном взаимодействии. Отправку ответов микросервис поставщика выполняет самостоятельно, вызывая асинхронный API Шлюза MQ.
Входящая очередь для асинхронных запросов устанавливается настройкой
mq.connection.receiveQueue.
Входящая очередь для синхронных запросов устанавливается настройкой
mq.connection.receiveQueueSync.
При получении запроса из синхронной очереди Шлюз MQ выполняет вызов микросервиса-поставщика синхронно, получает ответ и отправляет его в очередь потребителя, как описано выше при синхронном взаимодействии.
В режиме поставщика Шлюз MQ поднимает два API: синхронный и асинхронный. При получении вызова от микросервиса-потребителя по синхронному API Шлюза MQ отправляет полученное сообщение в очередь поставщика и запускает режим синхронного чтения ответов, как было описано выше при синхронном взаимодействии. При получении ответа от поставщика, Шлюз MQ возвращает его потребителю внутри той же сессии.
Очередь по умолчанию для синхронных ответов устанавливается настройкой mq.sync-receiver.defaultReceiveQueue.
При получении вызова от микросервиса по асинхронному API Шлюза MQ отправляет полученное сообщение в очередь поставщика и завершает вызов, возвращая потребителю HeartBeat, как было описано ранее при асинхронных взаимодействиях. Ответ в этом случае поставщик отправляет через отдельную очередь для асинхронных ответов, которую постоянно слушает Шлюз MQ.
Валидация сообщений на Шлюзе MQ#
Валидация сообщений на Шлюзе MQ выполняется внешним компонентом (Валидатор запросов IGEG (REQV)).
Возможна валидация как входящих (из MQ) так и исходящих (в MQ) сообщений в XML и JSON форматах.
Для синхронных вызовов можно настроить отправку информационных ответов об ошибках валидации, если форматы (схемы) ответных сообщений это позволяют.
Схема включения валидатора#
Схема. Схема включения валидатора
Получив сообщение, по одному из своих основных интерфейсов, Шлюз MQ, в соответствии со своими настройками, отправляет тело сообщения на валидацию в контейнер с валидатором. Валидатор по своим настройкам определяет нужную схему сообщения и проводит валидацию, после чего возвращает Шлюзу MQ результат. В случае положительного результата валидации Шлюз отправляет сообщение дальше в точку назначения. В случае отрицательного результата валидации Шлюз MQ фиксирует ошибку, сообщение дальше не отправляется.
Если сообщение пришло в синхронном вызове, и в настройках валидатора для этого сообщения задан шаблон ответа об ошибке, то, в случае отрицательного результата валидации, валидатор формирует и передает Шлюзу MQ ответ об ошибке, а Шлюз MQ возвращает его инициатору.
Чтобы Валидатор мог определить схему сообщения для валидации, ему в заголовках HTTP-вызова должны быть переданы два параметра: metod и path. Значение заголовка path на шлюзе определяется динамически, на основании параметров сообщения, по правилам заданным в настройках шлюза. Соответствие значений path схемам сообщений задается в конфигурации валидатора.
Важно!
При работе валидатора совместно со Шлюзом MQ:
Заголовок
metodвсегда имеет значение POST.Валидатор, при ошибке валидации должен всегда возвращать код 403 (используется по умолчанию).
Возвращаемые валидатором заголовки
error-headersШлюзом MQ не обрабатываются.Шлюз MQ при вызове валидатора самостоятельно заполняет заголовок
x-request-idзначением RqUID определенным для целей логирования.
Подключение валидатора#
Для подключения валидатора необходимо в Deployment Шлюза MQ добавить контейнер валидатора, а также тома с его конфигурацией (артефакт ConfigMap или Secret содержащий файл appl.yml) и схемами сообщений (артефакт ConfigMap или Secret содержащий схемы сообщений) и их точки монтирования.
Ссылку на актуальный образ можно получить в описании Валидатора.
Пример подключения валидатора в Deployment Шлюза MQ: Deployment: spec.template.spec.containers
containers
- name: validator
resources:
limits:
cpu: 200m
memory: 160Mi
requests:
cpu: 100m
memory: 150Mi
terminationMessagePath: /dev/termination-log
env:
- name: PROJECT_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
ports:
- containerPort: 50051
protocol: TCP
imagePullPolicy: IfNotPresent
volumeMounts:
- name: validator-schemas
readOnly: true
mountPath: /schemas
- name: validator-config
mountPath: /config
- name: synapselogs
mountPath: /var/log/synapse
terminationMessagePolicy: File
image: >-
registry.example.com/ci01994970/ci03302438_synapse-igeg/rhel7-java-synapse-sidecar-validator-ci03302438:0.1.0.26
Deployment: spec.template.spec
volumes:
- name: validator-config
configMap:
name: validator-config
defaultMode: 256
- name: validator-schemas
configMap:
name: validator-schemas
defaultMode: 256
В настройках Валидатора нужно задать порт, на котором Валидатор будет принимать запросы. Порт валидатора по номеру не должен совпадать с портом, который использует Шлюз MQ. Также в настройках задается соответствие параметров вызова валидатора конкретной схеме валидации.
Пример настройки Валидатора: validator-config
app.yaml: |
port: 8787
log_level: info
schemas:
- name: 'foo'
method: POST
path: '/ping'
schema: /schemas/foo.json
type: 'json'
error_response: '{"error-message":{"x-request-id": "{{get .Header "x-request-id"}}", "error": "{{get .Error "error_message"}}"}}'
error_headers:
- key: content-type
value: application/json
- key: foo
value: '{{get .Header "foo"}}'
headers:
- key: x-foo-first
value: foo1
error_code: 200
- name: bar
method: POST
path: '/'
schema: /schemas/bar.xsd
type: 'xml'
error_code: 403
error_response: '<error_rs>{{get .Error "error_message"}}</error_rs>'
- name: bar5
method: POST
path: '/inbound/sync/rq/bar'
schema: /schemas/barn.xsd
type: 'xml'
error_response: '<error_rs>{{get .Error "error_message"}}</error_rs>'
- name: bar6
method: POST
path: '/outbound/sync/rq/bar'
schema: /schemas/bar.xsd
type: 'xml'
#error_response: '<error_rs>{{get .Error "error_message"}}</error_rs>'
- name: bar7
method: POST
path: '/inbound/sync/rs/bar'
schema: /schemas/bar.xsd
type: 'xml'
#error_response: '<error_rs>{{get .Error "error_message"}}</error_rs>'
- name: bar8
method: POST
path: '/outbound/sync/rs/bar'
schema: /schemas/bar.xsd
type: 'xml'
#error_response: '<error_rs>{{get .Error "error_message"}}</error_rs>'
- name: bar9
method: POST
path: '/inbound/async/_/bar'
schema: /schemas/bar.xsd
type: 'xml'
- name: bar10
method: POST
path: '/outbound/async/_/bar'
schema: /schemas/bar.xsd
type: 'xml'
- name: pass-path
method: GET
path: '/json'
schema: ''
type: 'pass'
Дополнительно, к валидатору, в точку монтирования /schemas должен быть смонтирован артефакт типа ConfigMap или Secret, содержащий схемы для валидации сообщений.
Пример артефакта со схемами: validator-schemas
bar.xsd: |
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="RqUID" type="xs:string"/>
<xs:element name="street" type="xs:string"/>
<xs:element name="street-number" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="zip" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
barn.xsd: |
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="RqUID" type="xs:string"/>
<xs:element name="street" type="xs:string"/>
<xs:element name="street-number" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="zip" type="xs:string"/>
<xs:element name="country-n" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
foo.json: |
{
"$id": "http://json-schema.org/draft/2019-09/json-schema-core.html",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$comment": "sample comment",
"title": "Config dump",
"type": "object",
"properties": {
"configs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@type": {
"type": "string"
},
"last_updated": {
"type": "string"
},
"bootstrap": {
"type": "object",
"properties": {
"admin": {
"type": "object"
},
"dynamic_resources": {
"type": "object"
},
"node": {
"type": "object"
},
"static_resources": {
"type": "object"
},
"stats_config": {
"type": "object"
}
},
"required": ["admin", "dynamic_resources", "node", "static_resources", "stats_config"]
}
},
"required": ["@type", "last_updated", "bootstrap"]
}
}
},
"required": [
"configs"
]
}
foon.json: |
{
"$id": "http://json-schema.org/draft/2019-09/json-schema-core.html",
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$comment": "sample comment",
"title": "Config dump",
"type": "object",
"properties": {
"configs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@type": {
"type": "string"
},
"last_updated-n": {
"type": "string"
},
"bootstrap": {
"type": "object",
"properties": {
"admin": {
"type": "object"
},
"dynamic_resources": {
"type": "object"
},
"node": {
"type": "object"
},
"static_resources": {
"type": "object"
},
"stats_config": {
"type": "object"
}
},
"required": ["admin", "dynamic_resources", "node", "static_resources", "stats_config"]
}
},
"required": ["@type", "last_updated-n", "bootstrap"]
}
}
},
"required": [
"configs"
]
}
Настройка шлюза для выполнения валидации#
Включить валидацию#
Добавить в конфигурацию Шлюза MQ блок параметров верхнего уровня validator. Параметры могут быть добавлены в существующий файл конфигурации application.yml, но учитывая их критичность, можно выделить их в отдельный файл validator-secret.yml, и поместить его в артефакт типа Secret.
Отдельный артефакт необходимо подключить к контейнеру шлюза в Deployment: Пример подключения секрета с настройками валидации
volumes:
- name: test-gw-validator
secret:
secretName: test-gw-validator
defaultMode: 256
. . .
containers
- name: test-gw
volumeMounts:
- name: test-gw-validator
mountPath: /deployments/config/validator
Установить настройку validator.enabled в true.
Установить параметры вызова валидатора#
Установить настройки validator.http.client.url и validator.http.client.timeout
URL нужно задать в формате http://localhost:<port>/check, где port соответствует номеру порта заданному в настройках валидатора.
Установить правила выбора профиля настроек#
Заполнить секцию validator.serviceName. Секция задает правило выбора профиля настроек валидации по параметрам сообщения. Принцип заполнения такой же, как для секции routing, но дефолтный профиль не предусмотрен.
Обычно (но не обязательно) профиль соответствует наименованию сервиса, и может быть получен, например, из пользовательского заголовка ServiceName (если он используется) либо из тега ServiceName в теле сообщения.
Пример заполнения секции validator.serviceName
serviceName: # правила определения схем для сообщений
valueFrom:
- type: fromRfh2Header
value: ServiceName
- type: fromBody
value: //ServiceName
valueJson: $..ServiceName
expr:
Установить правила выбора пути для поиска схемы#
Заполнить секцию validator.serviceList. Секция задает список профилей, и для каждого профиля - правила вычисления значения заголовка path для вызова валидатора. Принцип заполнения такой же как для секции routing. Значение path определяется как результат вычисления выражения заданного в pathExpression над списком переменных. Значения переменных определяются из параметров конкретного сообщения по заданным в профиле правилам.
В большинстве случаев (но не всегда) достаточно привязать path к корневому тегу сообщения:
Пример заполнения секции serviceList
serviceList:
- serviceName: testServiceName
pathExpression: ServiceNameRootTag
variables:
- name: ServiceNameRootTag
valueFrom:
- type: fromBody
value: local-name(/*)
expr:
предопределенные переменные#
Для более точной идентификации схемы можно использовать встроенные переменные, которые шлюз заполняет самостоятельно.
direction - направление движения сообщения. варианты:
outbound - в MQ
inbound - из MQ
workMode - режим вызова, варианты:
sync - сообщение получено в синхронном вызове
async - сообщение получено в асинхронном вызове
msgType - тип сообщения (определяется только для синхронных вызовов), варианты:
rq - синхронный запрос
rs - ответ на синхронный запрос
«_» - сообщение в асинхронном вызове
Пример использования предопределенных переменных
serviceList:
- serviceName: testServiceName
pathExpression: ("/"+variables['direction']+"/"+variables['workMode']+"/"+variables['msgType']+"/"+ServiceNameRootTag)
variables:
- name: ServiceNameRootTag
valueFrom:
- type: fromBody
value: local-name(/*)
expr:
Заголовки#
В вызов валидатора могут быть добавлены дополнительные заголовки. Они используются для уточненного поиска схемы, а также для параметризации ответа об ошибке валидации (подробности см. в разделе «Параметры настройки» документа «Руководство оператора» на компонент Request Validator (REQV) Platform V Synapse Service Mesh).
Для добавления заголовка в вызов нужно для конкретного профиля настроек заполнить параметр headers:
Пример настройки заполнения заголовков
serviceList:
- serviceName: testServiceName
headers: # Заполняется, если требуется передать дополнительные заголовки для вставки в ответ.
RqUID:
- type: fromBody
value: //RqUID
# Matching values
valueJson: $..RQID
pathExpression: ("/"+variables['direction']+"/"+variables['workMode']+"/"+variables['msgType']+"/"+ServiceNameRootTag)
variables:
- name: ServiceNameRootTag
valueFrom:
- type: fromBody
value: local-name(/*)
expr:
Выполнение валидации на шлюзе#
Если на шлюзе включена валидация, то к каждому проходящему через шлюз сообщению применяются правила определения профиля валидации.
Если профиль найден, то вычисляется параметр path по правилам заданным в профиле настроек, и тело сообщения отправляется на валидацию.
Если профиль для сообщения не задан, то, по умолчанию, сообщение будет признано валидным и отправлено дальше по маршруту. (Это поведение можно изменить установкой параметра настройки invalidateByDefault в true)
При положительном результате валидации сообщение будет оправлено дальше по маршруту.
При отрицательном результате валидации, будет вызвано исключение. Если вызов был синхронным и валидатор вернул информационный ответ об ошибке, этот ответ будет возвращен инициатору. В остальных случаях:
для входящего сообщения из MQ обработка прекращается
для входящих по GRPC/HTTP - возвращается исключение и статус/код завершения соответственно.
Возврат ответа#
Если шлюз при обработке синхронного вызова получил от валидатора ошибку валидации с информационным ответом, то:
для синхронного запроса из MQ ответ отправляется в очередь и менеджер указанные в заголовках
MQMD.ReplyToQиMQMD.ReplyToQMgrвходящего запроса. По умолчанию заголовокMQMD.CorrelIdответа заполняется значением заголовкаMQMD.MsgIdзапроса (стандартное поведение, может быть изменено установкой параметраreplyByCorrelIdвtrue)для ответа на синхронный запрос из MQ тело ответа заменяется полученным от валидатора сообщением, заголовки не модифицируются.
Для вызовов по GRPC/HTTP ответ возвращается в вызове.
Вызов завершается штатно, исключение не вызывается.
Логирование и мониторинг#
Шлюз MQ фиксирует в прикладном логе заголовок PATH и результат вызова валидатора. Тело сообщения по-умолчанию не логируется (поведение можно изменить установкой параметра настройки loggingBody в true).
Шлюз отображает в метрике errors_gateways количество ошибок валидации и количество ошибок вызова валидатора раздельно по кодам 604 и 605 соответственно.
Ошибки валидации на шлюзе:
ошибка |
Описание |
код |
HTTP статус |
grpc статус |
|---|---|---|---|---|
ошибка валидации сообщения |
Сюда относятся только ошибки несоответствия сообщения его схеме, в случаях, когда информационный ответ об ошибке инициатору не возвращается |
604 |
403 Forbidden |
aborted |
ошибка вызова валидатора (синхронные запросы и асинхронные вызовы) |
Сюда относятся все ошибки вызова валидатора, при которых результат проверки остается неизвестным. (транспортные ошибки включая таймауты, внутренние ошибки валидатора) |
605 |
502 Bad gateway Есть возможность повтора вызова через другой экземпляр шлюза (если настроены повторы) |
unavailable Есть возможность повтора вызова через другой экземпляр шлюза (если настроены повторы) |
ошибки вызова валидатора (ответы на синхронные запросы) |
Сюда относятся все ошибки вызова валидатора, при которых результат проверки остается неизвестным. (транспортные ошибки включая таймауты, внутренние ошибки валидатора) |
605 |
400 Bad request Не выполняется повтор вызова, чтобы избежать дублирования запроса |
internal Не выполняется повтор вызова, чтобы избежать дублирования запроса |
Хранение секретов в SecurityManager#
Важно! Получение пространства в хранилище секретов и соответствующих доступов выполняется в соответствии с «Руководством для пользователей Secret Management».
В данной инструкции не рассматривается вопрос выпуска ключей и сертификатов.
Общие параметры настройки#
Для обеспечения взаимодействия с SecMan требуется добавить через Annotations deployment Шлюза инъекцию sidecars SecMan и монтирование секретов в виде файлов.
Шлюз MQ использует хранилище сертификатов в формате JKS (CMS в реализации на Golang), в то время как хранилище секретов SecMan не поддерживает хранение файлов. Для целей интеграции необходимо файл хранилища сертификатов предварительно закодировать в Base64.
Полученное значение и содержимое ssl-secret.yml необходимо указать в секрете в хранилище SecMan.
Сами секреты в хранилище могут выглядеть так:
Пример секрета для Java-Шлюза
{
"config": "mq:\n sslConfigs:\n - sslConfigName: TestConfig\n sslCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n sslFipsRequired: false\n sslResetCount: 0\n sslSocketFactory:\n protocol: TLSv1.2\n keyStore: /mnt/secrets/keystore.jks\n keyStorePassword: <Пароль для keystore>\n keyStoreType: JKS\n trustStore: /mnt/secrets/keystore.jks\n trustStorePassword: <Пароль для traststore>\n trustStoreType: JKS\n sslCertAlias: gateway",
"keystore": "/u3+7........soDc",
"masking": "logger:\n mask:\n enabled: true\n cardNumElements: CardNum\n fioElements: FIO,FirstName,MiddleName\n elements: Addr3",
"validator": "validator:\n enabled: false\n invalidateByDefault: false",
"ttl": "3m"
}
Здесь:
keystore - представление в Base64 файла keystore.jks
config - содержимое файла ssl-secret.yml (пробелы важны)
masking - содержимое файла masking-secret.yml (пробелы важны)
validator - содержимое файла validator-secret.yml (пробелы важны)
ttl - время жизни секрета - определяет период ротации секрета в Pod
Пример секрета для Go-Шлюза
{
"config": "mq:\n sslConfigs:\n - sslConfigName: TestConfig\n sslCipherSuite: ECDHE_RSA_AES_128_GCM_SHA256\n sslFipsRequired: false\n sslResetCount: 0\n sslSocketFactory:\n protocol: TLSv1.2\n keyStore: /mnt/secrets/key.kdb\n sslCertAlias: gateway",
"key.kdb": "/u3+7........soDc",
"key.sth": "ZqtuG3........33==",
"masking": "logger:\n mask:\n enabled: true\n cardNumElements: CardNum\n fioElements: FIO,FirstName,MiddleName\n elements: Addr3",
"validator": "validator:\n enabled: false\n invalidateByDefault: false",
"ttl": "3m"
}
Здесь:
key.kdb - представление в Base64 файла key.kdb
key.sth - представление в Base64 файла key.sth
config - содержимое файла ssl-secret.yml (пробелы важны)
masking - содержимое файла masking-secret.yml (пробелы важны)
validator - содержимое файла validator-secret.yml (пробелы важны)
ttl - время жизни секрета - определяет период ротации секрета в Pod
Имена полей можно указать любые, они используются в аннотациях deployment.
Важно! - если в именах полей есть «.» (точка), то в template в deployment надо указывать команду index для их чтения:
Index для полей с точкой
{{ index .Data.masking }}
Параметры deployment Шлюза MQ#
В аннотациях агента SecMan применяются Consul Template, основанные на go-template.
Важно!: при inject секретов, особенно кодированных в base64, важно строго соблюдать нотацию и указывать символ «-» в начале и конце управляющих конструкций (как в примерах ниже). Иначе переносы строк и интервалы будут восприняты как текстовое содержимое секретов и добавлены в секрет, что приведет к некорректной записи файлов при декодировании base64
Без Istio
В deployment Шлюза MQ добавляются следующие аннотации и метки, обеспечивающие подключение агента SecMan и монтирование секретов из него:
Пример конфигурации Deployment для Java-Шлюза
apiVersion: apps/v1
kind: Deployment
spec:
template:
metadata:
labels
secman-injector: enabled #обеспечивает подключение подов к SecMan
annotations:
# базовые аннотации
vault.hashicorp.com/agent-init-first: "true" # смонтировать секреты до запуска основного контейнера
vault.hashicorp.com/agent-inject: "true" # обеспечивает инъекцию init-контейнера и Sidecar Vault-Agent в поды Шлюза MQ
vault.hashicorp.com/namespace: CIxxxxxxxx_CIxxxxxxxx #имя пространства в хранилище секретов формата CIxxxxxxxx_CIxxxxxxxx
vault.hashicorp.com/agent-pre-populate: "true" #обеспечить наличие секретов до запуска основного контейнера
vault.hashicorp.com/role: ciNNNNNNNN_xxx # роль в SecMan, получаемая при подключении к хранилищу (по тикету в Jira)
vault.hashicorp.com/agent-volumes-default-mode: "0400" # задание прав на созданные файлы с секретами в примонтированном EmptyDir
# подключение секрета настроек маскирования сообщений
vault.hashicorp.com/secret-volume-path-masking-secret.yml: /deployments/config/masking # путь монтирования файла конфигурации маскирования из секрета
vault.hashicorp.com/agent-inject-secret-masking-secret.yml: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, masking-secret.yml - имя секрета
vault.hashicorp.com/agent-inject-file-masking-secret.yml: masking-secret.yml # имя файла конфигурации маскирования из секрета, будет создан по пути выше
vault.hashicorp.com/agent-inject-template-masking-secret.yml: | # шаблон извлечения секрета конфигурации маскирования
{{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}} #полный путь к секрету в хранилище
{{ .Data.masking }} #имя свойства секрета, в котором хранится значение секрета
{{- end -}}
# подключение секрета настроек валидатора
vault.hashicorp.com/secret-volume-path-validator-secret.yml: /deployments/config/validator # путь монтирования файла конфигурации валидатора из секрета
vault.hashicorp.com/agent-inject-secret-validator-secret.yml: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, validator-secret.yml - имя секрета
vault.hashicorp.com/agent-inject-file-validator-secret.yml: validator-secret.yml # имя файла конфигурации валидатора из секрета, будет создан по пути выше
vault.hashicorp.com/agent-inject-template-validator-secret.yml: | # шаблон извлечения секрета конфигурации валидатора
{{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}}
{{ .Data.validator }}
{{- end -}}
# подключение секрета настроек ssl-соединения
vault.hashicorp.com/secret-volume-path-ssl-config.yaml: /deployments/config/ssl # путь монтирования файла конфигурации SSL из секрета
vault.hashicorp.com/agent-inject-secret-ssl-config.yaml: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, ssl-config.yaml - имя секрета
vault.hashicorp.com/agent-inject-file-ssl-config.yaml: ssl-secret.yml # имя файла конфигурации SSL из секрета, будет создан по пути выше
vault.hashicorp.com/agent-inject-template-ssl-config.yaml: | # шаблон извлечения секрета конфигурации SSL
{{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}}
{{ .Data.config }}
{{- end -}}
# подключение секрета файла хранилища сертификатов для ssl-соединения
vault.hashicorp.com/secret-volume-path-keystore.jks: /mnt/secrets # путь монтирования keystore из секрета (соответствует путю keyStore/trustStore из ssl-secret.yml)
vault.hashicorp.com/agent-inject-secret-keystore.jks: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, keystore.jks - имя секрета
vault.hashicorp.com/agent-inject-file-keystore.jks: keystore.jks # имя файла конфигурации keystore из секрета, будет создан по пути выше (соответствует имени файла keyStore/trustStore из ssl-secret.yml)
vault.hashicorp.com/agent-inject-template-keystore.jks: | # шаблон извлечения секрета keystore с декодированием из Base64
{{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}}
{{ base64Decode .Data.keystore }} #декодирование Base64 значения секрета
{{- end -}}
Пример конфигурации Deployment для Go-Шлюза
apiVersion: apps/v1
kind: Deployment
spec:
template:
metadata:
labels
secman-injector: enabled #обеспечивает подключение подов к SecMan
annotations:
# базовые аннотации
vault.hashicorp.com/agent-init-first: "true" # смонтировать секреты до запуска основного контейнера
vault.hashicorp.com/agent-inject: "true" # обеспечивает инъекцию init-контейнера и Sidecar Vault-Agent в поды Шлюза MQ
vault.hashicorp.com/namespace: CIxxxxxxxx_CIxxxxxxxx #имя пространства в хранилище секретов формата CIxxxxxxxx_CIxxxxxxxx
vault.hashicorp.com/agent-pre-populate: "true" #обеспечить наличие секретов до запуска основного контейнера
vault.hashicorp.com/role: ciNNNNNNNN_xxx # роль в SecMan, получаемая при подключении к хранилищу (по тикету в Jira)
vault.hashicorp.com/agent-volumes-default-mode: "0400" # задание прав на созданные файлы с секретами в примонтированном EmptyDir
# подключение секрета настроек маскирования сообщений
vault.hashicorp.com/secret-volume-path-masking-secret.yml: /deployments/config/masking # путь монтирования файла конфигурации маскирования из секрета
vault.hashicorp.com/agent-inject-secret-masking-secret.yml: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, masking-secret.yml - имя секрета
vault.hashicorp.com/agent-inject-file-masking-secret.yml: masking-secret.yml # имя файла конфигурации маскирования из секрета, будет создан по пути выше
vault.hashicorp.com/agent-inject-template-masking-secret.yml: | # шаблон извлечения секрета конфигурации маскирования
{{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}} #полный путь к секрету в хранилище
{{ .Data.masking }} #имя свойства секрета, в котором хранится значение секрета
{{- end -}}
# подключение секрета настроек валидатора
vault.hashicorp.com/secret-volume-path-validator-secret.yml: /deployments/config/validator # путь монтирования файла конфигурации валидатора из секрета
vault.hashicorp.com/agent-inject-secret-validator-secret.yml: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, validator-secret.yml - имя секрета
vault.hashicorp.com/agent-inject-file-validator-secret.yml: validator-secret.yml # имя файла конфигурации валидатора из секрета, будет создан по пути выше
vault.hashicorp.com/agent-inject-template-validator-secret.yml: | # шаблон извлечения секрета конфигурации валидатора
{{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}}
{{ .Data.validator }}
{{- end -}}
# подключение секрета настроек ssl-соединения
vault.hashicorp.com/secret-volume-path-ssl-config.yaml: /deployments/config/ssl # путь монтирования файла конфигурации SSL из секрета
vault.hashicorp.com/agent-inject-secret-ssl-config.yaml: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, ssl-config.yaml - имя секрета
vault.hashicorp.com/agent-inject-file-ssl-config.yaml: ssl-secret.yml # имя файла конфигурации SSL из секрета, будет создан по пути выше
vault.hashicorp.com/agent-inject-template-ssl-config.yaml: | # шаблон извлечения секрета конфигурации SSL
{{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}}
{{ .Data.config }}
{{- end -}}
# подключение секрета файла хранилища сертификатов для ssl-соединения
vault.hashicorp.com/secret-volume-path-key.kdb: /mnt/secrets # путь монтирования key.kdb из секрета (соответствует пути keyStore/trustStore из ssl-secret.yml)
vault.hashicorp.com/agent-inject-secret-key.kdb: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, key.kdb - имя секрета
vault.hashicorp.com/agent-inject-file-key.kdb: key.kdb # имя файла key.kdb из секрета, будет создан по пути выше (соответствует имени файла keyStore/trustStore из ssl-secret.yml)
vault.hashicorp.com/agent-inject-template-key.kdb: | # шаблон извлечения секрета key.kdb с декодированием из Base64
{{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}}
{{ base64Decode .Data.key_kdb }} #декодирование Base64 значения секрета
{{- end -}}
# подключение секрета файла пароля хранилища сертификатов для ssl-соединения
vault.hashicorp.com/secret-volume-path-key.sth: /mnt/secrets # путь монтирования key.sth из секрета (соответствует пути keyStore/trustStore из ssl-secret.yml)
vault.hashicorp.com/agent-inject-secret-key.sth: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, key.sth - имя секрета
vault.hashicorp.com/agent-inject-file-key.sth: key.sth # имя файла key.sth из секрета, будет создан по пути выше (соответствует имени файла keyStore/trustStore из ssl-secret.yml)
vault.hashicorp.com/agent-inject-template-key.sth: | # шаблон извлечения секрета key.sth с декодированием из Base64
{{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}}
{{ base64Decode .Data.key_sth }} #декодирование Base64 значения секрета
{{- end -}}
С Istio
Для образов с поддержкой ожидания секретов SecMan:
Добавить SE для SecMan. Также необходимо исправить параметр host в SE, в зависимости от стенда согласно таблице в инструкции
SE-secman.yamlapiVersion: networking.istio.io/v1beta1 kind: ServiceEntry metadata: name: se-secman spec: exportTo: - . hosts: - t.secrets.domainname.ru location: MESH_EXTERNAL ports: - name: https-443 number: 443 protocol: HTTPS resolution: DNSДобавить configMap со списком секретов, которые нужно ждать, и прокинуть в контейнер с приложением по пути /tmp/secman/config.
configmap-secman-files.yamlkind: ConfigMap apiVersion: v1 metadata: name: gateway-all-sp-secman data: secman.txt: |- /mnt/secrets/keystore.jks #путь к файлу хранилища сертификатов для ssl-подключения. Должен быть указан так же в deployment (пример в секции подключение секрета файла хранилища сертификатов для ssl-соединения ниже) # далее - стандартные пути секретов конфигураций, неизменяемые /deployments/config/ssl/ssl-secret.yml /deployments/config/masking/masking-secret.yml /deployments/config/validator/validator-secret.ymlПуть ``/tmp/secman/config`, по которому должен быть смонтирован файл с конфигурацией, зафиксирован в образе шлюза и не может быть изменен.
В deployment Шлюза MQ добавляются аннотации и метки, обеспечивающие подключение агента SecMan и монтирование секретов из него. Пример файла с необходимыми аннотациями и монтированиями:
Пример
deploymentkind: Deployment apiVersion: apps/v1 spec: template: metadata: labels: secman-injector: enabled #обеспечивает подключение подов к SecMan annotations: # базовые аннотации sidecar.istio.io/inject: 'true' #подключение Sidecar istio vault.hashicorp.com/agent-init-first: "false" # ОТКЛЮЧИТЬ (смонтировать секреты до запуска основного контейнера) vault.hashicorp.com/agent-inject: "true" # обеспечивает инъекцию init-контейнера и Sidecar Vault-Agent в поды Шлюза MQ vault.hashicorp.com/namespace: CIxxxxxxxx_CIxxxxxxxx #имя пространства в хранилище секретов (Только для Enterprise версии Vault) vault.hashicorp.com/agent-pre-populate: "false" #ОТКЛЮЧИТЬ (обеспечить наличие секретов до запуска основного контейнера_ vault.hashicorp.com/role: ciNNNNNNNN_xxx # роль в SecMan, получаемая при подключении к хранилищу (по тикету в Jira) vault.hashicorp.com/agent-run-as-same-user: "true" # запуск контейнера Vault-Agent от имени того же пользователя, что и приложение в containers[0] (только для DropApp/Kubernetes) vault.hashicorp.com/agent-volumes-default-mode: "0400" # задание прав на созданные файлы с секретами в примонтированном EmptyDir # подключение секрета настроек маскирования сообщений vault.hashicorp.com/secret-volume-path-masking-secret.yml: /deployments/config/masking # путь монтирования файла конфигурации маскирования из секрета vault.hashicorp.com/agent-inject-secret-masking-secret.yml: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, masking-secret.yml - имя секрета vault.hashicorp.com/agent-inject-file-masking-secret.yml: masking-secret.yml # имя файла конфигурации маскирования из секрета, будет создан по пути выше vault.hashicorp.com/agent-inject-template-masking-secret.yml: | # шаблон извлечения секрета конфигурации маскирования {{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}} #полный путь к секрету в хранилище {{ .Data.masking }} #имя свойства секрета, в котором хранится значение секрета {{- end -}} # подключение секрета настроек валидатора vault.hashicorp.com/secret-volume-path-validator-secret.yml: /deployments/config/validator # путь монтирования файла конфигурации валидатора из секрета vault.hashicorp.com/agent-inject-secret-validator-secret.yml: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, masking-secret.yml - имя секрета vault.hashicorp.com/agent-inject-file-validator-secret.yml: validator-secret.yml # имя файла конфигурации валидатора из секрета, будет создан по пути выше vault.hashicorp.com/agent-inject-template-validator-secret.yml: | # шаблон извлечения секрета конфигурации валидатора {{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}} {{ .Data.validator }} {{- end -}} # подключение секрета настроек ssl-соединения vault.hashicorp.com/secret-volume-path-ssl-config.yaml: /deployments/config/ssl # путь монтирования файла конфигурации SSL из секрета vault.hashicorp.com/agent-inject-secret-ssl-config.yaml: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, ssl-config.yaml - имя секрета vault.hashicorp.com/agent-inject-file-ssl-config.yaml: ssl-secret.yml # имя файла конфигурации SSL из секрета, будет создан по пути выше vault.hashicorp.com/agent-inject-template-ssl-config.yaml: | # шаблон извлечения секрета конфигурации SSL {{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}} {{ .Data.config }} {{- end -}} # подключение секрета файла хранилища сертификатов для ssl-соединения vault.hashicorp.com/secret-volume-path-keystore.jks: /mnt/secrets # путь монтирования keystore из секрета (соответствует путю keyStore/trustStore из ssl-secret.yml) vault.hashicorp.com/agent-inject-secret-keystore.jks: 'true' # признак монтирования секрета -vault.hashicorp.com/agent-inject-secret - статичная часть, keystore.jks - имя секрета vault.hashicorp.com/agent-inject-file-keystore.jks: keystore.jks # имя файла конфигурации keystore из секрета, будет создан по пути выше (соответствует имени файла keyStore/trustStore из ssl-secret.yml) vault.hashicorp.com/agent-inject-template-keystore.jks: | # шаблон извлечения секрета keystore с декодированием из Base64 {{- with secret "CIxxxxxxxx_CIxxxxxxxx/x/xxxx/xxx/x/KV/secret_name" -}} {{ base64Decode .Data.keystore }} #декодирование Base64 значения секрета {{- end -}} spec: volumes: # подключение файла со списком путей к файлам, создание которых ожидается - name: secman-config configMap: name: gateway-all-sp-secman items: - key: secman.txt path: secman.txt defaultMode: 256 containers: volumeMounts: - name: secman-config readOnly: true mountPath: /tmp/secman/configПосле старта Pod в логах образа Шлюза можно наблюдать счетчик времени ожидания, там же видно какие файлы ожидаются и какие из них найдены. Если он не останавливается длительное время, можно посмотреть в логи vault-agent - там должны быть следующие строки:
логи агента secman
[INFO] (runner) rendered "(dynamic)" => "/deployments/config/ssl/ssl-secret.yml" [INFO] (runner) rendered "(dynamic)" => "/deployments/config/masking/masking-secret.yml" [INFO] (runner) rendered "(dynamic)" => "/mnt/secrets/keystore.jks" [INFO] (runner) rendered "(dynamic)" => "/deployments/config/validator/validator-secret.yml"Если их нет - в конфигурации допущена ошибка.
Для старых образов, в которых нет поддержки SecMan
Для образов, в которых нет скрипта ожидания монтирования секретов, есть обходной путь (только для DEV-окружения):
Дополнительно к действиям выше необходимо:
Убрать аннотацию traffic.sidecar.istio.io/excludeOutboundPorts если присутствует
Добавить скрипты ожидания секретов и запуска приложения шлюза
all-sp-entrypoint.yamlkind: ConfigMap apiVersion: v1 metadata: name: gateway-all-sp-secman-wait data: entrypoint.sh: |- #!/bin/sh java -Dfile.encoding=UTF-8 -Dspring.jmx.enabled=false -Duser.timezone=Europe/Moscow -XX:+ExitOnOutOfMemoryError -Dcom.ibm.mq.cfg.useIBMCipherMappings=false -Dspring.config.location=classpath:application-default.yml,file:/deployments/config/common/common-config.yml,file:/deployments/config/application.yml,file:/deployments/config/environment/environment.yml,file:/deployments/config/ssl/ssl-secret.yml,file:/deployments/config/masking/masking-secret.yml,file:/deployments/config/validator/validator-secret.yml -Djava.security.egd=file:/dev/./urandom -cp ./app/resources:./app/classes:./app/libs/* com.sbt.synapse.mq.MqGatewayApplication waitingSecrets.sh: |- #!/bin/sh config_file=/tmp/secman/config/secman.txt checked_files=`cat $config_file` files_count=0 for file in $checked_files ; do files_count=$(( files_count + 1 )) done exists_files_count=0 time_counter=0 while [ $exists_files_count != $files_count ]; do exists_files_count=0 for file in $checked_files ; do if [ -f $file ]; then exists_files_count=$(( exists_files_count + 1 )) fi done sleep 1 time_counter=$(( time_counter + 1 )) echo "Waiting $time_counter s." doneДля Go-шлюза entrypoint выглядит так:
all-sp-entrypoint.yamlkind: ConfigMap apiVersion: v1 metadata: name: gateway-all-sp-secman-wait data: entrypoint.sh: |- #!/bin/sh /go/src/app/bin/go-mq-gateway -c "/deployments/config/common/common-config.yml,/deployments/config/application.yml,/deployments/config/environment/environment.yml,/deployments/config/ssl/ssl-secret.yml,/deployments/config/masking/masking-secret.yml,/deployments/config/validator/validator-secret.yml" waitingSecrets.sh: |- #!/bin/sh config_file=/tmp/secman/config/secman.txt checked_files=`cat $config_file` files_count=0 for file in $checked_files ; do files_count=$(( files_count + 1 )) done exists_files_count=0 time_counter=0 while [ $exists_files_count != $files_count ]; do exists_files_count=0 for file in $checked_files ; do if [ -f $file ]; then exists_files_count=$(( exists_files_count + 1 )) fi done sleep 1 time_counter=$(( time_counter + 1 )) echo "Waiting $time_counter s." doneДобавить следующие поля в описание контейнера:
entrypointkind: Deployment apiVersion: apps/v1 spec: template: spec: containers: # ниже - команда и аргументы вызова скриптов ожидания секретов и запуска шлюза command: - /bin/sh args: - '-c' - /tmp/secman/waitingSecrets.sh && /tmp/secman/entrypoint.shДобавить в deployment монтирование файлов и команду запуска (разрешить исполнение):
Пример
deploymentkind: Deployment apiVersion: apps/v1 spec: template: spec: volumes: - name: secman-config configMap: name: gateway-all-sp-secman items: - key: secman.txt path: secman.txt defaultMode: 256 - name: secman-waiting configMap: name: gateway-all-sp-secman-wait items: - key: waitingSecrets.sh. # скрипт ожидания секретов path: waitingSecrets.sh - key: entrypoint.sh # скрипт запуска шлюза path: entrypoint.sh defaultMode: 493 # разрешение на исполнение скриптов containers: volumeMounts: - name: secman-config readOnly: true mountPath: /tmp/secman/config - name: secman-waiting readOnly: true mountPath: /tmp/secman/ # ниже - команда и аргументы вызова скриптов ожидания секретов и запуска шлюза command: - /bin/sh args: - '-c' - /tmp/secman/waitingSecrets.sh && /tmp/secman/entrypoint.sh
Динамическая загрузка TLS сертификатов#
При изменении файлов секретов с хранилищами ключей внешней системой хранения сертификатов, Шлюз MQ должен перечитать новые значения и использовать их для создания подключений.
В реализации на Java#
Шлюз MQ отслеживает изменение файлов с хранилищами ключей. Новые значения применяются при создании новых подключений. При работе с менеджерами IBM MQ в конфигурацию Шлюза добавлены дополнительные настройки:
mq:
connection:
certificateReloadEnabled: true
certificateReloadDelay: 2s
При работе с провайдерами MQ отличными от IBM эти настройки игнорируются
В реализации Шлюза MQ на Golang#
Последовательность действий при обновлении сертификатов:
Шлюз MQ мониторит изменения (обновление, удаление, создание) файлов хранилищ сертификатов (расположение файлов указывается в конфигурации).
При наступлении события изменения файлов, Шлюз MQ:
выдерживает паузу определяемую алгоритмом временного смещения для снижения коллизий (перекрытия по времени недоступности двух или более pods общего Deployment (см. ниже описание алгоритма));
останавливает обработку сообщений;
останавливает все коннекты к менеджерам MQ;
перечитывает файлы хранилищ сертификатов;
восстанавливает коннекты с использованием новых сертификатов;
возобновляет обработку сообщений.
Сообщения принятые по каналам HTTP и gRPC во время обновления сертификатов (с момента остановки обработки до ее возобновления) накапливаются в пуле потоков обработки, и обрабатываются после восстановления коннектов.
Если пул потоков обработки исчерпан, а процедура обновления еще не завершена, новые входящие вызовы по каналам HTTP и gRPC будут отклоняться шлюзом, и средствами istio будет выполняться повтор вызова на другие pod-ы шлюза.
Предусмотрена настройка, позволяющая задать состояние Readiness пробы на время проведения процедуры обновления. По умолчанию Readiness - проба не опускается. Снятие Readiness-пробы выводит шлюз из балансировки на время приостановки обработки. Это позволяет предотвратить повторы вызовов на уровне сервис-меша и связанное с этим увеличение латентности в прикладных интеграциях.
Чтобы исключить существенное негативное влияние на поток, предусмотрен механизм, снижающий вероятность одновременного обновления ключей на нескольких экземплярах одного deployment приложения.
С этой целью Шлюз сдвигает начало применения новых сертификатов от момента изменения файлов хранилищ. Время сдвига определяется для каждого экземпляра независимо, по вероятностному алгоритму, который позволяет минимизировать количество коллизий перезапуска коннектов, но не исключает их полностью.
Описание алгоритма#
Алгоритм использует хэш от имени пода, распределенный по заданному количеству сегментов, для получения временного смещения начала перезапуска коннектов, относительно момента изменения файлов хранилищ сертификатов.
Временное смещение определяется исходя из номера сегмента для конкретного пода и размера интервала обновления.
Интервал обновления — это время необходимое одному поду шлюза, для выполнения процедуры применения сертификатов
Произведение интервала обновления и количества сегментов дает общее время выполнения процедуры обновления сертификатов на deployment шлюза.
При уменьшении фактора репликации вероятность коллизий будет снижаться, при этом величина коллизий (количество подов одновременно начавших процедуру применения сертификатов) и общее время выполнения процедуры на deployment сохраняются.
Шлюз вносит дополнительную фиксированную временную задержку, задаваемую параметром mq.sslCertUpdate.certificateReloadDelay для того, чтобы при несинхронном изменении файлов ключей и сертификатов со стороны внешней системы хранения сертификатов исключить излишние повторы процедуры обновления.
Конфигурирование#
Для настройки алгоритма используются следующие параметры (mq.sslCertUpdate):
enable - включение(true)/выключение(false) механизма обновления сертификатов. Если установлено значение false, контроль за изменением файлов хранилища сертификатов отключается.
checkInterval - устанавливает периодичность проверки изменения файлов хранилища сертификатов.
downReadinessProbe - задает нужно (true), или не нужно (false) опускать Readiness-пробу на время выполнения обновления сертификатов. Если значение true, то Readiness-проба на каждом конкретном экземпляре шлюза будет переводится в состояние down с момента остановки обработки сообщений до момента ее возобновления этим конкретным экземпляром.
intervalsNumber - количество интервалов (сегментов распределения). С его уменьшением вероятность коллизий будет увеличиваться. Можно уменьшить, если у deployment малый фактор репликации (количество запущенных подов < 5), чтобы уменьшить обще время выполнения процедуры замены сертификатов.
updateTimeInSeconds - Интервал обновления — время применения сертификатов для одного пода Шлюза. Нужно увеличивать, если у шлюза задано большое количество коннектов (очередей и брокеров), так как в этом случае отключение и подключение всех коннектов потребует больше времени. Пропорционально увеличивает общее время выполнения для deployment.
roundTimeInMinutes - интервал на который округляется (в большую сторону) момент изменения файлов хранилищ ключей. Это позволяет нивелировать временную разницу распространения обновленных файлов по node(s), и обеспечить независимые поды общей точкой отсчета для начала процедуры. Нужно увеличивать, если в проекте наблюдается значительный разброс времени распространения файлов по node(s). Приводит к сдвигу общего времени выполнения процедуры для deployment на величину roundTimeInMinutes.
certificateReloadDelay - Дополнительная фиксированная временная задержка, для того, чтобы при несинхронном изменении файлов ключей и сертификатов со стороны внешней системы хранения сертификатов исключить излишние повторы процедуры обновления.
Пример настройки (приведены значения по умолчанию):
mq:
sslCertUpdate:
enable: false
checkInterval: 30000ms
downReadinessProbe: false
intervalsNumber: 20
updateTimeInSeconds: 2
roundTimeInMinutes: 10
certificateReloadDelay: 30s
Обработка внештатных ситуаций#
В процессе обновления сертификатов возможно возникновение исключительных ситуаций:
Файл хранилища был удален, но новый файл не был доставлен. В действующей реализации попытка прочитать реквизиты удаленного файла завершиться неудачей, шлюз продолжит работу со старыми сертификатами, и продолжит попытки проверить реквизиты файла. Когда будет смонтирован файл хранилища, то шлюз выполнит проверку реквизитов, и в случае их изменения обновит сертификаты.
Новый файл хранилища поврежден (файл есть, но «битый»). При попытке загрузить новые сертификаты возникнет ошибка инициализации библиотек криптографии. Будет снята Readiness - проба, шлюз будет пытаться инициализировать библиотеку. После устранения ошибки будут загружены сертификаты, и восстановлена Readiness-проба.
Сертификаты в новом файле хранилища не валидные. После обновления сертификатов возникнет ошибка установки соединения с менеджерами MQ. Readiness-проба будет снята. Шлюз будет пытаться установить соединение с менеджерами. После устранения ошибки подключения будут восстановлены, и Readiness-проба поднята.
Особенности использования реализации Шлюза MQ на Golang#
Deployment#
До релиза 3.2 в deployment Шлюза MQ при использовании реализации на Golang должны были добавляться точки монтирования /IBM и //.mqm на тома типа emptyDir.
Пример:
volumes:
- name: ibm
emptyDir: {}
- name: mqm
emptyDir: {}
...
volumeMounts:
- name: ibm
mountPath: /IBM
- name: mqm
mountPath: //.mqm
С релиза 3.2 эта необходимость исключена! Папки перенесены внутрь образа шлюза, права выданы только на чтение, т. к. требование безопасности установить параметр readOnlyRootFileSystem = true все равно заблокирует запись в них. Вследствие этого файлы журналов клиента MQ создаваться не будут. Если эти журналы нужны, то нужно переадресовать их в каталог на который есть права на запись (например /var/log/synapse). Сделать это можно, добавив в Deployment Шлюза переменную окружения MQ_OVERRIDE_DATA_PATH.
Пример:
env:
- name: MQ_OVERRIDE_DATA_PATH
value: /var/log/synapse
В аргументах, при необходимости, могут быть заданы уровень логирования, и файлы конфигурации, если их название не совпадает с принятым по умолчанию.
Пример:
args:
- '-l'
- debug
- '-c'
- /deployments/config/application.yml,/deployments/config/ssl/ssl-secret-go.yml
Важно Уровень логирования по требованию безопасности должен быть выставлен в info (или выше).
Важно В средах k8s или DAPP может измениться расположение HOME каталога, поэтому в этих средах требуется явно задать привязку домашнего каталога приложения к корневому через деплоймент Шлюза.
Пример:
env:
- name: HOME
value: /
Хранилище сертификатов#
Шлюз MQ в реализации на Golang использует хранилище сертификатов в формате CMS (.kdb). Может быть получено из хранилища ключей в формате JKS путем конвертации.
Для конвертации выполнить команду:
runmqckm -keydb -convert -db <имя файла хранилища>.jks -new_format cms -pw <пароль на хранилище> -stash
(В среде Windows надо вызывать runmqckm.exe)
В каталоге будут созданы три файла с расширениями .kdb, .rdb, .sth. Файл .rdb не нужен для работы Шлюза MQ.
Настройки SSL#
Важно! Go-шлюз использует коды схем шифрования IBM (как на MQ-менеджере), которые в общем случае не совпадают с кодами Java. Поэтому при переходе надо проверять, и, при необходимости, корректировать название схемы шифрования (параметр sslCipherSuite)
Настройки SSL в Файле ssl-secret.yml в основном совпадают с настройками для реализации на java. Изменяется имя хранилища на полученное после конвертации в формат CMS, лишние параметры нужно убрать в соответствии с примером.
Пример:
mq:
sslConfigs:
- sslConfigName: <Имя профиля> # Связывает профиль настроек TLS с настройками соединения по одноименному параметру. Если не задано, используется 'default'
sslCipherSuite: ECDHE_RSA_AES_128_GCM_SHA256
sslFipsRequired: false
sslResetCount: 0
sslSocketFactory:
protocol: TLSv1.2
keyStore: /mnt/secrets/<имя хранилища>.kdb # было: /mnt/secrets/<имя хранилища>.jks меняем на .kdb
# keyStorePassword: <пароль на хранилище> убираем
# keyStoreType: JKS убираем
# trustStore: /mnt/secrets/<имя хранилища>.jks убираем
# trustStorePassword: <пароль на хранилище> убираем
# trustStoreType: JKS убираем
sslCertAlias: ibmwebspheremqsnp99usr
Конфигурация#
В общем случае Шлюз MQ в реализации на Golang более критичен к формату параметров в файле конфигурации.
Параметр port в секции mq должен иметь значение в формате целого числа:
Пример:
mq:
connection:
connections:
- channel: <Имя канала>
queueManager: <Имя менеджера>
hostname: <Имя хоста>
port: 3012
sslConfigName: <Имя профиля> # задает имя профиля настроек TLS для данного соединения. Если не задано, будет использован профиль с именем 'default'
в формате json:
mq:
connection:
connections: [{'channel': '<Имя канала>', 'queueManager': '<Имя менеджера>', 'hostname': '<Имя хоста>', 'port': 3012}]
Для элементов типа list значение всегда должно иметь формат списка, даже если в списке всего один элемент. Например, для входящей очереди:
# либо:
receiveQueue:
- SYNAPSE.ASAP.IN
# либо:
receiveQueue: [SYNAPSE.ASAP.IN]
Название параметра должно быть задано точно так, как указано в документации. Например, параметр syncReceiver должен быть задан в camel case, а параметры mq-http и http-mq - через дефис.
Шлюз MQ в реализации на Golang всегда работает в совмещенном sync/async режиме (workMode: all), поэтому названия очередей должны задаваться в соответствующих параметрах:
Для шлюза потребителя:
mq:
systemType: sс
workMode: all
connection:
receiveQueue:
- <Очередь для асинхронного режима> # задается при переводе шлюзов async или all(sync/async)
receiveQueueSync:
- <Очередь для синхронного режима> # задается при переводе шлюзов sync или all (sync/async)
Для шлюза поставщика:
mq:
systemType: sp
workMode: all
connection:
receiveQueue:
- <Очередь для асинхронного режима> # задается при переводе шлюзов async
syncReceiver:
defaultReceiveQueue: <Очередь для синхронного режима> # задается при переводе шлюзов sync (если есть необходимость задать дефолтную очередь для ответов)
Синтаксис, используемый в выражениях Expression в секции routing для реализации на Golang отличается от реализации на Java.
Основное отличие:
Поскольку строка в goel - это массив символов, для обработки нужно использовать функции и/или операторы, а не методы.
Реализация на java |
Реализация на Golang |
Комментарий |
|---|---|---|
|
|
Кастомное расширение |
|
|
Кастомное расширение |
|
|
|
|
|
|
|
|
Для выделения подстроки используется slice |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Реализация на Go поддерживает тернарный оператор.
Выражения:
SCName+'-'+ServiceName[0:len(ServiceName)-2]+'-'+SPName
"variables['SCName'] + '-' + 'srv' + variables['ServiceName'][0:len(ServiceName)-2] + '-' + variables['SPName']"
destinationExpression: '"ufsfl-srvgetcarddeliverystatus-oddo-rq"'
destinationExpression: "'ufsfl-srvgetcarddeliverystatus-oddo-rq'"
destinationExpression: ('ufsfl-srvgetcarddeliverystatus-oddo-rq')
"'<строка>' == toLower(value) ? '<строка 1>':value"
будут работать в реализации на Golang.
Важно! В реализации Шлюза MQ на Golang для ExtractionRule отсутствуют типы (type): fromGrpcHeader, fromAuthority.
Важно! В реализации Шлюза MQ на Java список
routing.routeList[].destinations- регистрозависимый, а в реализации Шлюза MQ на Golang - регистронезависимый.
Шлюз MQ в реализации на Golang с релиза 2.11 при считывании сообщений из очереди, преобразует значения пользовательских заголовков сообщения в строки с учетом их исходного типа. В реализации на Java это было заложено изначально.
Размер считываемого из MQ сообщения в реализации на Golang по умолчанию ограничен 100 Кб. Необходимо явно прописать ограничение размера сообщений в настройке Шлюза MQ если оно превышает 100 кБ.
Важно! Размер сообщения установленный на шлюзе не должен превышать ограничений на размер сообщений, установленных на менеджере MQ, в противном случае будет возникать ошибка.
За ограничение размера сообщения в реализациях на Java и Golang отвечают разные настройки:
#Java
mq:
rejectMaxMessageSize: 350KB
#Go
mq:
maxMsgLength: 350
В реализации на Golang по умолчанию установлено ограничение на размер сообщения обрабатываемого gRPC сервером 4 MB. За ограничение размера сообщения в реализациях на Java и Golang отвечают разные настройки:
#Java
grpc:
maxMessageSizeInMb: 2
#Go
grpc:
server:
maxMessageSizeInMb: 2
Механизм чистки .FDC файлов#
Реализация Шлюза MQ на Golang использует в своем составе библиотеки распространяемого (redistributable) клиента IBM MQ. При его работе, в случае возникновения ошибок в каталоге /IBM/MQ/data/errors создаются файлы дампов (.FDC) большого размера. При накоплении .FDC файлов, Pod шлюза может выселяться (evict) с node(s) из-за нехватки ресурсов диска. Для экономии дискового пространства предусмотрен механизм очистки .FDC файлов.
Механизм удаляет .FDC файлы при превышении лимита занятого места в директории начиная со старых.
Лимит задается настройкой maxFileSizes. Период сканирования задается настройкой period.
Шлюз сканирует каталог и определяет размер всех файлов .FDC в нем, а так же их возраст. Если размер файлов больше значения maxFileSizes, Шлюз начинает удалять файлы начиная со старых, пока не будет очищено место в объеме большем или равном заданному настройкой freePercent (в процентах от maxFileSizes).
Дополнительно Шлюз раз в сутки, в момент времени, заданный настройкой outdatedScanTime проверяет возраст файлов .FDC и удаляет все файлы старше, чем задано настройкой maxAgeInDays.
Пример настроек:
cleanFDC:
enable: false # включение механизма очистки, по умолчанию - выключен
path: "/IBM/MQ/data/errors/" # Путь по которому искать .FDC файлы. Настройка не влияет на то где файлы формируются.
maxFilesSize: 50MB # Суммарный размер .FDC файлов, при превышении которого включается очистка.
freePercent: 80 # Сколько процентов от суммарного размера должно быть освобождено при очистке
period: "60s" # Период сканирования
outdatedScanTime: "01:00" # Время запуска удаления устаревших файлов
maxAgeInDays: 7 # возраст в днях при достижении которого файлы считаются устаревшими
retry: # Настройка количества повторных попыток, если при попытки удаления файл был заблокирован.
attempts: 3
waitDuration: 500ms
Пропуск XML-декларации при разборе тела XML-сообщений#
Канонический формат XML-сообщения предполагает наличие в начале XML-декларации (строка вида <?xml version=»1.0» encoding=»UTF-8»?>).
Параметр encoding должен соответствовать кодировке символов тела сообщения. В противном случае возможны ошибки при разборе тела сообщения содержащего символы национального алфавита.
Для исключения таких ошибок добавлена возможность пропуска XML-декларации при разборе тела сообщения. Возможность включается установкой параметра parser.xml.skipDeclaration в конфигурации шлюза в true, при этом декларация при разборе сообщения будет проигнорирована.
Реализация Шлюза MQ на Java всегда игнорирует XML-декларацию в теле сообщения.
Особенности используемого базового образа#
Базовый образ реализации шлюза MQ на Java#
Базовый образ реализации Шлюза MQ на Java собран на основе образа ubi-minimal от RedHat.
Для правильной работы приложения в образ установлена JVM: Eclipse OpenJ9 VM AdoptOpenJDK. Требуемая версия указана в разделе «Системные требования» документа «Руководство по установке»
Базовый образ реализации Шлюза MQ на Golang#
Базовый образ реализации Шлюза MQ на Golang собран на основе образа ubi-minimal от RedHat.
Для правильной работы приложения в образе, в операционной системе, включена русская локализация (ru_RU.UTF-8) и установлен распространяемый клиент IBM MQ. Требуемая версия указана в разделе «Системные требования» документа «Руководство по установке»
Настройка регистрации событий аудита#
Шлюз MQ позволяет фиксировать события аудита в локальном буфере аудита.
Перечень фиксируемых событий Аудита, которые возникают в процессе функционирования Шлюза MQ:
Код |
Событие |
Комментарий |
|---|---|---|
MQ_success |
Успешное подключение к менеджеру MQ |
Фиксируется при каждом успешном подключении Шлюза MQ к менеджеру MQ. Для многоточечного подключения (один Шлюз к нескольким менеджерам) фиксируется отдельное событие подключения к каждому менеджеру MQ. |
MQ_error |
Ошибка подключения к менеджеру MQ |
Фиксируется при ошибке подключения Шлюза MQ к менеджеру MQ. Для многоточечного подключения события фиксируются для каждого подключенного менеджера отдельно. |
MQ_gateway_audit_init |
Событие успешной инициализации отправки событий в Аудит |
Фиксируется после выполнения регистрации метамодели в Аудите. |
MQ_gateway_config_change |
Событие изменения динамически изменяемых параметров шлюза |
Фиксируется после изменений настроек в блоке transform, в блоке outbound, в блоке rateLimiter, параметра SendQueue, параметра SendToCustomDestination, параметра readEnabled, списка отключенных менеджеров disableManagerIds, в блоке routing, в блоке tracing |
MQ_gateway_audit_payload_too_large |
Событие превышения размера сообщения Аудита |
Фиксируется при отправке сообщения через HTTP в случае если в ответе приходит HTTP статус 413 (Payload Too Large). |
MQ_gateway_audit_value_too_large |
Событие превышения размера полей сообщения при отправке изменения динамических параметров в Аудит |
Фиксируется в случаях, когда событие изменения динамических параметров нарушает ограничения по размерам полей события, и не может быть корректно отправлено в Audit OPM. |
Перечень параметров событий
Общие параметры событий:
Код |
Параметр |
Комментарий |
|---|---|---|
Время события |
Время фиксируется по моменту возникновения события в конкретном экземпляре шлюза |
|
Проект Kubernetes (OpenShift) |
Наименование проекта в Kubernetes (OpenShift) с запущенным Pod шлюза, на котором произошло событие |
|
Источник события |
Имя Pod шлюза, на котором произошло событие |
|
Имя Node |
Имя Node, на которой запущен Pod шлюза |
|
IP адрес Node |
IP Node, на которой запущен Pod шлюза |
Параметры событий MQ_success, MQ_error
Код |
Параметр |
Комментарий |
|---|---|---|
Имя менеджера |
Имя менеджера, к которому выполнялось подключение |
|
Канал |
Имя канала через, который выполнялось подключение |
|
Имя хоста |
Имя хоста менеджера, к которому выполнялось подключение |
|
Номер порта |
номер порта менеджера, по которому выполнялось подключение |
|
Схема шифрования |
Схема шифрования указанная в настройках Pod шлюза |
|
DN шлюза |
DN сертификата шлюза из настроек |
|
DN менеджера MQ |
Ожидаемый DN менеджера MQ из настройки sslPeerName конфигурации шлюза. |
|
Описание ошибки |
Для успешного подключения: пусто; |
Параметры события MQ_gateway_audit_init
Код |
Параметр |
Комментарий |
|---|---|---|
audit_url |
URL эндпоинта Audit OPM, в который отправляются события. |
Параметры события MQ_gateway_config_change
Код |
Параметр |
Комментарий |
|---|---|---|
transform |
Изменения в блоке transform |
|
mq.outbound |
Изменения в блоке outbound |
|
mq.concurrentLimiter |
Изменения в блоке concurrentLimiter |
|
mq.rateLimiter |
Изменения в блоке rateLimiter |
|
mq.connection.SendQueue |
Изменения параметра SendQueue |
|
mq.connection.SendToCustomDestination |
Изменения параметра SendToCustomDestination |
|
mq.consumerParams.readEnabled |
Изменения параметра readEnabled |
|
mq.consumerParams.disableManagerIds |
Изменение списка отключенных менеджеров disableManagerIds |
|
routing |
Изменения в блоке routing |
|
tracing |
Изменения в блоке tracing |
Параметры событий MQ_gateway_audit_payload_too_large, MQ_gateway_audit_value_too_large
Код |
Параметр |
Комментарий |
|---|---|---|
error_desc |
Описание ошибки |
Для MQ_gateway_audit_payload_too_large - Превышен размер события, для MQ_gateway_audit_value_too_large - Превышен размер поля события |
Чтобы отправить событие аудита в Audit OPM, в нем предварительно должна быть зарегистрирована метамодель события.
Шлюз формирует метамодель в формате JSON:
{
"metamodelVersion": "4",
"module": "mq-gateway",
"events": [
{
"name": "MQ_gateway_audit_init",
"description": "Инициализация отправки в аудит",
"success": true,
"mode": "reliability",
"params": [
{
"name": "time",
"description": "Время события (timestamp)"
},
{
"name": "os_project",
"description": "Проект Kubernetes (OpenShift)"
},
{
"name": "event_source",
"description": "Имя пода источника события"
},
{
"name": "node_name",
"description": "Имя ноды на которой запущен под шлюза"
},
{
"name": "node_ip",
"description": "IP ноды на которой запущен под шлюза"
},
{
"name": "audit_url",
"description": "URL эндпоинта ТС Аудит"
}
]
},
{
"name": "MQ_success",
"description": "Успешное подключение к менеджеру MQ",
"success": true,
"mode": "reliability",
"params": [
{
"name": "time",
"description": "Время события (timestamp)"
},
{
"name": "os_project",
"description": "Проект Kubernetes (OpenShift)"
},
{
"name": "event_source",
"description": "Имя пода источника события"
},
{
"name": "node_name",
"description": "Имя ноды на которой запущен под шлюза"
},
{
"name": "node_ip",
"description": "IP ноды на которой запущен под шлюза"
},
{
"name": "mqm_name",
"description": "Имя менеджера к которому выполнялось подключение"
},
{
"name": "mqm_channel",
"description": "Имя канала по которому выполнялось подключение"
},
{
"name": "mqm_host",
"description": "Имя хоста менеджера к которому выполнялось подключение"
},
{
"name": "mqm_port",
"description": "Номер порта менеджера к которому выполнялось подключение"
},
{
"name": "cipher",
"description": "Используемая схема шифрования"
},
{
"name": "dn_mqgateway",
"description": "DN сертификата шлюза из настроек"
},
{
"name": "dn_mqm",
"description": "Ожидаемый DN менеджера MQ из настроек"
},
{
"name": "error_desc",
"description": "Описание ошибки"
}
]
},
{
"name": "MQ_error",
"description": "Ошибка подключения к менеджеру MQ",
"success": false,
"mode": "reliability",
"params": [
{
"name": "time",
"description": "Время события (timestamp)"
},
{
"name": "os_project",
"description": "Проект Kubernetes (OpenShift)"
},
{
"name": "event_source",
"description": "Имя пода источника события"
},
{
"name": "node_name",
"description": "Имя ноды на которой запущен под шлюза"
},
{
"name": "node_ip",
"description": "IP ноды на которой запущен под шлюза"
},
{
"name": "mqm_name",
"description": "Имя менеджера к которому выполнялось подключение"
},
{
"name": "mqm_channel",
"description": "Имя канала по которому выполнялось подключение"
},
{
"name": "mqm_host",
"description": "Имя хоста менеджера к которому выполнялось подключение"
},
{
"name": "mqm_port",
"description": "Номер порта менеджера к которому выполнялось подключение"
},
{
"name": "cipher",
"description": "Используемая схема шифрования"
},
{
"name": "dn_mqgateway",
"description": "DN сертификата шлюза из настроек"
},
{
"name": "dn_mqm",
"description": "Ожидаемый DN менеджера MQ из настроек"
},
{
"name": "error_desc",
"description": "Описание ошибки"
}
]
},
{
"name": "MQ_gateway_audit_payload_too_large",
"description": "Ошибка регистрации изменений конфигурации в ТС Аудит",
"success": false,
"mode": "reliability",
"params": [
{
"name": "time",
"description": "Время события (timestamp)"
},
{
"name": "os_project",
"description": "Проект Kubernetes (OpenShift)"
},
{
"name": "event_source",
"description": "Имя пода источника события"
},
{
"name": "node_name",
"description": "Имя ноды на которой запущен под шлюза"
},
{
"name": "node_ip",
"description": "IP ноды на которой запущен под шлюза"
},
{
"name": "error_desc",
"description": "Описание ошибки (превышен размер события)"
}
]
},
{
"name": "MQ_gateway_audit_value_too_large",
"description": "Ошибка регистрации изменений конфигурации в ТС Аудит",
"success": false,
"mode": "reliability",
"params": [
{
"name": "time",
"description": "Время события (timestamp)"
},
{
"name": "os_project",
"description": "Проект Kubernetes (OpenShift)"
},
{
"name": "event_source",
"description": "Имя пода источника события"
},
{
"name": "node_name",
"description": "Имя ноды на которой запущен под шлюза"
},
{
"name": "node_ip",
"description": "IP ноды на которой запущен под шлюза"
},
{
"name": "error_desc",
"description": "Описание ошибки (превышен размер поля события)"
}
]
},
{
"name": "MQ_gateway_config_change",
"description": "изменение конфигурации шлюза",
"success": true,
"mode": "reliability",
"params": [
{
"name": "time",
"description": "Время события (timestamp)"
},
{
"name": "os_project",
"description": "Проект Kubernetes (OpenShift)"
},
{
"name": "event_source",
"description": "Имя пода источника события"
},
{
"name": "node_name",
"description": "Имя ноды на которой запущен под шлюза"
},
{
"name": "node_ip",
"description": "IP ноды на которой запущен под шлюза"
}
],
"changedParams": [
{
"name": "transform",
"description": "Изменения в блоке transform"
},
{
"name": "mq.outbound",
"description": "Изменения в блоке outbound"
},
{
"name": "mq.concurrentLimiter",
"description": "Изменения в блоке concurrentLimiter"
},
{
"name": "mq.rateLimiter",
"description": "Изменения в блоке rateLimiter"
},
{
"name": "mq.connection.SendQueue",
"description": "Изменения параметра SendQueue"
},
{
"name": "mq.connection.SendToCustomDestination",
"description": "Изменения параметра SendToCustomDestination"
},
{
"name": "mq.consumerParams.readEnabled",
"description": "Изменения параметра readEnabled"
},
{
"name": "mq.consumerParams.disableManagerIds",
"description": "Изменение списка отключенных менеджеров"
},
{
"name": "routing",
"description": "Изменения в блоке routing"
},
{
"name": "tracing",
"description": "Изменения в блоке tracing"
}
]
}
]
}
Метамодель отправляется один раз при старте приложения.
Примеры событий в JSON - формате:
Пример события MQ_success
{
"module": "mq-gateway",
"metamodelVersion": "4",
"name": "MQ_success",
"userNode": "NO-USERNODE",
"userLogin": "NO-USER",
"createdAt": 1234567890000,
"session": "NO-SESSION",
"userName": "",
"tags": [
"<project_name>",
"<deployment_name>"
],
"params": [
{
"name": "time",
"value": "1234567890"
},
{
"name": "os_project",
"value": "ci01994970-idevgen2-synapse-esbub-dev"
},
{
"name": "event_source",
"value": "test-gw-56558d77b6-hcl9v"
},
{
"name": "node_name",
"value": "nodename.domainname.ru"
},
{
"name": "node_ip",
"value": "192.0.0.1"
},
{
"name": "mqm_name",
"value": "SYNAPSE.DEV.MGR"
},
{
"name": "mqm_channel",
"value": "SRV.SSL.CHANNEL"
},
{
"name": "mqm_host",
"value": "hostname.domainname.ru"
},
{
"name": "mqm_port",
"value": "1452"
},
{
"name": "cipher",
"value": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
},
{
"name": "dn_mqgateway",
"value": "CN=TEST-GW"
},
{
"name": "dn_mqm",
"value": "CN=SYNAPSE"
},
{
"name": "error_desc",
"value": ""
}
]
}
Пример события MQ_error
{
"module": "mq-gateway",
"metamodelVersion": "4",
"name": "MQ_error",
"userNode": "NO-USERNODE",
"userLogin": "NO-USER",
"createdAt": 1234567890000,
"session": "NO-SESSION",
"userName": "",
"tags": [
"<project_name>",
"<deployment_name>"
],
"params": [
{
"name": "time",
"value": "1234567890"
},
{
"name": "os_project",
"value": "ci01994970-idevgen2-synapse-esbub-dev"
},
{
"name": "event_source",
"value": "test-gw-56558d77b6-hcl9v"
},
{
"name": "node_name",
"value": "nodename.domainname.ru"
},
{
"name": "node_ip",
"value": "192.0.0.1"
},
{
"name": "mqm_name",
"value": "SYNAPSE.DEV.MGR"
},
{
"name": "mqm_channel",
"value": "SRV.SSL.CHANNEL"
},
{
"name": "mqm_host",
"value": "hostname.domainname.ru"
},
{
"name": "mqm_port",
"value": "1452"
},
{
"name": "cipher",
"value": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
},
{
"name": "dn_mqgateway",
"value": "CN=TEST-GW"
},
{
"name": "dn_mqm",
"value": "CN=SYNAPSE"
},
{
"name": "error_desc",
"value": "nested exception is com.ibm.mq.MQException: JMSCMQ0001: Не удалось выполнить вызов IBM MQ; код завершения '2' ('MQCC_FAILED'), причина '2398' ('MQRC_SSL_PEER_NAME_MISMATCH')
com.ibm.mq.jmqi.JmqiException: CC=2;RC=2398;AMQ9204: Запрос на подключение к хосту 'hostname.domainname.ru(1452)' отклонен. [1=com.ibm.mq.jmqi.JmqiException[CC=2;RC=2398;AMQ9636: Отличительное имя SSL не соответствует имени узла; канал '?'. [4=CN=SYNAPSE]],3=hostname.domainname.ru(1452),5=RemoteTCPConnection.protocolConnect]"
}
]
}
Пример события MQ_gateway_audit_init
{
"module": "mq-gateway",
"metamodelVersion": "4",
"name": "MQ_gateway_audit_init",
"userNode": "NO-USERNODE",
"userLogin": "NO-USER",
"userName": "NO-USER",
"session": "NO-SESSION",
"createdAt": 1707393578849,
"tags": [
"<project_name>",
"<project_name>"
],
"params": [
{
"name": "time",
"value": "1707393578561"
},
{
"name": "os_project",
"value": "ci01994970-idevgen2-synapse-esbub-dev"
},
{
"name": "event_source",
"value": "test-gw-56558d77b6-hcl9v"
},
{
"name": "node_name",
"value": "nodename.domainname.ru"
},
{
"name": "node_ip",
"value": "192.0.0.1"
},
{
"name": "audit_url",
"value": "<url Аудита>"
}
]
}
Пример события MQ_gateway_config_change
{
"module": "mq-gateway",
"metamodelVersion": "4",
"name": "MQ_gateway_config_change",
"userNode": "NO-USERNODE",
"userLogin": "NO-USER",
"userName": "NO-USER",
"session": "NO-SESSION",
"createdAt": 1707895735902,
"tags": [
"<project_name>",
"<project_name>"
],
"params": [
{
"name": "time",
"value": "1707895645666"
},
{
"name": "os_project",
"value": "ci01994970-idevgen2-synapse-esbub-dev"
},
{
"name": "event_source",
"value": "test-gw-56558d77b6-hcl9v"
},
{
"name": "node_name",
"value": "nodename.domainname.ru"
},
{
"name": "node_ip",
"value": "192.0.0.1"
}
],
"changedParams": [
{
"name": "mq.concurrentLimiter",
"value": "{\"Enable\":true,\"LimitConcurrent\":200}",
"oldValue": "{\"Enable\":true,\"LimitConcurrent\":10000}"
}
]
}
Пример события MQ_gateway_audit_payload_too_large
{
"module": "mq-gateway",
"metamodelVersion": "4",
"name": "MQ_gateway_audit_value_too_large",
"userNode": "NO-USERNODE",
"userLogin": "NO-USER",
"userName": "NO-USER",
"session": "NO-SESSION",
"createdAt": 1707895735903,
"tags": [
"<project_name>",
"<project_name>"
],
"params": [
{
"name": "time",
"value": "1707895645666"
},
{
"name": "os_project",
"value": "ci01994970-idevgen2-synapse-esbub-dev"
},
{
"name": "event_source",
"value": "test-gw-56558d77b6-hcl9v"
},
{
"name": "node_name",
"value": "nodename.domainname.ru"
},
{
"name": "node_ip",
"value": "192.0.0.1"
},
{
"name": "error_desc",
"value": "Превышен размер события"
}
]
}
Пример события MQ_gateway_audit_value_too_large
{
"module": "mq-gateway",
"metamodelVersion": "4",
"name": "MQ_gateway_audit_value_too_large",
"userNode": "NO-USERNODE",
"userLogin": "NO-USER",
"userName": "NO-USER",
"session": "NO-SESSION",
"createdAt": 1707895735903,
"tags": [
"<project_name>",
"<project_name>"
],
"params": [
{
"name": "time",
"value": "1707895645666"
},
{
"name": "os_project",
"value": "ci01994970-idevgen2-synapse-esbub-dev"
},
{
"name": "event_source",
"value": "test-gw-56558d77b6-hcl9v"
},
{
"name": "node_name",
"value": "nodename.domainname.ru"
},
{
"name": "node_ip",
"value": "192.0.0.1"
},
{
"name": "error_desc",
"value": "Превышен размер поля события"
}
]
}
Получение данных об имени проекта, имени Node и ее IP-адреса.
Чтобы приложение Шлюза получило доступ к этим данным, в deployment Шлюза должны быть добавлены следующие настройки:
env:
- name: PROJECT_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
- name: NODE_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
Шлюз при заполнении данных о событиях должен передать в параметре os_project значение переменной окружения PROJECT_NAME, в параметре node_name значение переменной окружения NODE_NAME, в параметре node_ip значение переменной окружения NODE_IP.
Для включения регистрации событий, аудита нужно в конфигурацию Шлюза MQ добавить секцию:
audit:
enable: true
transportType: file
directory: /var/log/synapse
В этом случае Шлюз MQ при старте создает в каталоге, указанном в параметре directory два файла:
event.aud
metamodel.amm
В файл metamodel.amm однократно выгружается метамодель событий. В файл event.aud начинают выгружаться события аудита по мере их возникновения на Шлюзе.
Эту схему можно использовать, если при регистрации событий аудита не предполагается использовать Audit OPM, либо передача событий в Audit OPM должна выполняться сторонними компонентами, выбранными архитектором и разработчиком конечной системы.
Для прямой отправки событий в Audit OPM, секцию audit нужно настроить следующим образом.
audit:
enable: true
transportType: http
http:
client:
httpMethod: POST
url: http://хост:порт
eventPath: /event_endpoint
metamodelPath: /metamodel_endpoint
responseTimeout: 10000
maxBufferSize: 3000
retry:
attempts: 10
При старте шлюз подключится к endpoint Audit OPM зарегистрирует метамодель и начнет отправку событий.
Одна и та же версия метамодели может регистрироваться произвольное количество раз, но при этом описатель метамодели не должен меняться. Поэтому версия метамодели вместе с самой метамоделью прошита в коде шлюза и меняется только при перепоставке.
Это гарантирует, что шлюзы одной и той же версии установленные в разных проектах всегда будут оправлять консистентную модель.
При возникновении ошибок отправки, шлюз сохраняет событие в локальном буфере в памяти приложения и пытается отправить его повторно. Количество попыток настраивается в параметре audit.http.retry.attempt. Количество событий, которое может быть сохранено в буфере настраивается в параметре audit.http.maxBufferSize.
Если возникает ошибка при регистрации модели, то шлюз повторяет попытки, пока модель не будет зарегистрирована, при этом readiness проба не поднимается, и перевод трафика на этот экземпляр шлюза не происходит.
Особенности настройки SSL к менеджерам MQ в реализации Шлюза MQ на Java#
В Шлюзе MQ настройки SSL заданы в секции mq.sslConfigs[]. В этой секции содержатся как параметры настройки SSL, так и пароли на хранилища ключей, например:
mq:
sslConfigs:
- sslConfigName: default
sslCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
sslFipsRequired: false
sslResetCount: 0
sslSocketFactory:
protocol: TLSv1.2
keyStore: /mnt/secrets/key.jks
keyStorePassword: <пароль на keystore>
keyStoreType: jks
trustStore: /mnt/secrets/key.jks
trustStorePassword: <пароль на truststore>
trustStoreType: jks
sslCertAlias: test-gw
- sslConfigName: Name1
sslCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
sslFipsRequired: false
sslResetCount: 0
sslSocketFactory:
protocol: TLSv1.2
keyStore: /mnt/secrets/key.jks
keyStorePassword: <пароль на keystore>
keyStoreType: jks
trustStore: /mnt/secrets/key.jks
trustStorePassword: <пароль на truststore>
trustStoreType: jks
sslCertAlias: test-gw
Чтобы пароли можно было хранить отдельно от основной конфигурации в настройки добавлен блок sslConfigData, который содержит секции с параметрами keyStorePassword, trustStorePassword, которые можно заполнить значениями паролей, и который можно разместить в отдельном файле ssl-secret.yml в артефакте типа Secret. Имена секций должны совпадать с именами профилей настроек SSL (sslConfigName) из sslConfigs[]. Например:
sslConfigData:
default:
keyStorePassword: <пароль на keystore>
trustStorePassword: <пароль на truststore>
Name1:
keyStorePassword: <пароль на keystore>
trustStorePassword: <пароль на truststore>
Шлюз MQ при загрузке считывает значения паролей и сопоставляет их с соответствующими им хранилищами ключей по совпадению имени секции из блока sslConfigData с именем профиля настройки SSL из sslConfigs[]. Если в профиле настроек SSL в блоке mq.sslConfigs имя профиля (sslConfigName) не указано, то используются значения паролей из секции sslConfigData.default.
Эта логика реализуется в коде Шлюза и не меняется.
Если значение настроек sslConfigs[].sslSocketFactory.keyStorePassword и sslConfigs[].sslSocketFactory.trustStorePassword задано, а настройки в блоке sslConfigData не заданы, то будут использованы настройки из sslConfigs[].
Если пароли заданы в двух местах, будут использованы пароли из sslConfigData.
Шлюз обеспечивает приоритет загрузки конфигурации (в частности секции sslConfigs) из файлов environment.yml (используется в некоторых проектах), application.yml, общий механизм загрузки при этом остается без изменений.
Это значит, что если одноименные параметры настройки заданы в нескольких файлах конфигурации, то используются значения заданные в application.yml, если там нет, то в environment.yml, если нет и там, то в ssl-secret.yml.
Изменение каталогов назначения для логеров шлюза#
В некоторых случаях по требованиям безопасности, следует исключать монтирование эфемерных файловых ресурсов типа emptyDir в контейнер приложения к точкам монтирования, отличным от /var/log.
Это требование налагает запрет на использование /opt/synapse/logs в качестве ресурса, через который агент журналирования забирает прикладные логи.
Поэтому, первым делом требуется изменить точку монтирования эфемерного ресурса в контейнер, для чего:
Настроить в Deployment монтирование эфемерного ресурса к нужной точке монтирования, например:
volumeMounts:
- name: synapselogs
mountPath: '/var/log/synapse'
Настроить в Deployment сам ресурс:
volumes:
- name: synapselogs
emptyDir: {}
Далее изменить каталог для вывода логов в приложении шлюза, используя один из следующих способов:
Реализация на Java#
Через переменные окружения Deployment Java-шлюза задать опцию java-машины -Dlog.dir=/var/log/synapse
env:
- name: JAVA_TOOL_OPTIONS
value: >-
-Dlog.dir=/var/log/synapse
Также, вместо опций JVM можно использовать переменную окружения log.dir напрямую:
env:
- name: log.dir
value: /var/log/synapse
При использовании этого способа, сразу при старте шлюза логи пишутся в заданный каталог.
Синхронизировать в конфигурации шлюза настройку файлового транспорта аудита (если используется файловый транспорт):
audit:
enable: true
transportType: file
directory: /var/log/synapse
Реализация на Golang#
В реализации Шлюза MQ на Golang изменение размещения логов можно выполнить настройкой переменной окружения в Deployment:
env:
- name: LOG_FILENAME
value: /var/log/synapse/messages1.log
Кроме того, если используется вывод логов клиента MQ на смонтированный ресурс, то не забыть поменять также их размещение, настройкой переменной окружения в Deployment:
env:
- name: MQ_OVERRIDE_DATA_PATH
value: /var/log/synapse
Синхронизировать в конфигурации шлюза настройки очистки FDC файлов, или убрать параметр path, тогда по дефолту будет использован тот, что задан в MQ_OVERRIDE_DATA_PATH:
cleanFDC:
enable: true
path: /var/log/synapse/errors/
Синхронизировать в конфигурации шлюза настройки вывода файлов событий аудита в режиме файлового транспорта (Если используется файловый транспорт):
audit:
enable: true
transportType: file
directory: /var/log/synapse
Настройки Агента журналирования#
Дополнительно, в обеих реализациях, потребуется изменить настройки агента журналирования (fluent-bit, или filebeat, смотря что используется в прикладной конфигурации)
Например, для fluent-bit нужно изменить в его конфигурации параметр, помеченный стрелкой в комментарии:
fluent-bit.conf
[SERVICE]
Flush 1
Daemon Off
Log_Level Debug
Parsers_File /fluent-bit/etc/parsers.conf
[INPUT]
Name tail
Path /var/log/synapse/*.log # <---- Здесь
Tag fluent
Parser custom
Buffer_Chunk_Size 400k
Buffer_Max_Size 6MB
Mem_Buf_Limit 6MB
[OUTPUT]
Name kafka
Brokers <список брокеров в формате хост:порт через запятую>
Timestamp_Key fluent_time
Topics syn.logs
rdkafka.security.protocol ssl
rdkafka.ssl.ca.location /fluent-bit/cert/ca_cert.pem
rdkafka.ssl.certificate.location /fluent-bit/cert/tls.crt
rdkafka.ssl.key.location /fluent-bit/cert/tls.key
rdkafka.log.connection.close false
rdkafka.request.required.acks 1
Маршрутизация gRPC-вызовов в Шлюзе MQ#
Маршрутизация на Шлюзе MQ - это определение имени сервиса-получателя (точки назначения), которому необходимо направить gRPC-вызов с преобразованным в формат ProtoMessage входящим сообщением.
В Шлюзе MQ реализована динамическая маршрутизация по гибким правилам, на основании параметров входящего MQ-сообщения.
Механизм маршрутизации работает следующим образом:
В настройках Шлюза MQ в блоке gRPC прописывается наименование сервиса-заглушки empty-service:
grpc:
client:
settings:
default:
hostname: empty-service
port: 5454
При выполнении gRPC-вызова он перенаправляется механизмами Kubernetes (OpenShift) в сервис, имя которого задано в параметре вызова Authority.
Шлюз MQ позволяет динамически задать значение этого параметра при вызове, применяя правила, заданные в конфигурации Шлюза MQ к параметрам (элементам тела и транспортным заголовкам) входящего сообщения.
Правила определения Authority задаются в конфигурации шлюза в блоке routing.
Процесс маршрутизации сообщения состоит из нескольких шагов:
Шаг 1. Определение имени маршрута#
Правила определения имени маршрута задаются в секции routeName блока routing.
Секция определения имени маршрута (routeName):
routing:
routeName:
valueFrom:
- type: fromRfh2Header
value: OperationName
- type: fromBody
value: local-name(/*)
valueJson: $.operation
- type: fromConst
value: SomeServiceRq
expr: value.substring(0,value.length()-2)
routeList:
...
В этой секции задается список правил, каждое правило состоит из пары type и value.
type определяет источник, из которого будет извлечено значение, value определяет правило извлечения значения. Для различных type возможны различные варианты значений value.
Для type= fromBody есть дополнительный тег valueJson, в котором можно задать выражение JsonPath для работы с сообщениями в формате JSON.
Возможные варианты:
type |
описание |
Значения value |
|---|---|---|
fromHeader |
Значение извлекается из блока системных заголовков. При работе с IBM MQ это соответствует блоку заголовков MQMD. |
value должно содержать название системного (MQMD) заголовка, из которого будет взято значение |
fromRfh2Header |
Значение извлекается из блока пользовательских заголовков. При работе с IBM MQ это соответствует блоку заголовков MQRFH2 |
value должно содержать название пользовательского (MQRFH2) заголовка, из которого будет взято значение |
fromBody |
Значение будет извлечено из тела сообщения. |
value должно содержать XPath * запрос, которым из тела сообщения будет получено значение. Работает только с сообщениями в формате XML |
fromConst |
Значение задается константой непосредственно в конфигурации. |
value должно содержать непосредственно нужное значение |
* При вычислении значений на основе содержимого тела XML-сообщения, Шлюз MQ может работать в одном из двух режимов: DOM или SAX. Режим может быть задан в параметре mq.typeParser конфигурации Шлюза MQ. По умолчанию используется значение DOM. Режим SAX более экономный в отношении использования ОЗУ, но ограничивает возможность задания value тремя типами выражений:
local-name(/*)— получение имени корневого тега XML-сообщения//tagName— получение значения элемента с именем tagName, если он встречается на любом уровне вложенности в сообщении. Возвращается значение первого найденного с таким именем элемента.//tagName/@attributeName— получение значения атрибута attributeName элемента tagName, если они встречаются на любом уровне вложенности сообщения. Возвращается значение для первого найденного с таким сочетанием имен аттрибута.
Для повышения гибкости определения маршрута в секции routeName есть параметр expr, в котором можно задать выражение для дополнительной коррекции полученного значения. Обращение к найденному значению выполняется через предопределенную переменную value. Например, выражение value.substring(0,value.length()-2) позволяет обрезать два последних символа в полученном значении.
Шлюз MQ применяет правила к входящему сообщению по порядку их следования в конфигурации до тех пор, пока не будет получено ненулевое значение. Поэтому правило c типом fromConst следует ставить в конце списка.
К найденному значению Шлюз MQ применяет выражение, заданное в expr. Полученное в результате этих действий значение и будет являться именем маршрута routeName.
Пример:
Настройки шлюза заданы, как указано выше.
Для сообщения с параметрами:
<MQRFH2>
<usr>
<OperationName>OneServiceRq</OperationName>
</usr>
</MQRFH2>
<MSG_BODY>
<Message>
<RqUID>4382730B7D4111ebB66EFA163E29F519</RqUID>
<RqTm>2021-03-05T08:44:09.000+00:00</RqTm>
<Mode>Async</Mode>
...
в routeName будет получено значение: OneService.
Шаг 2. Поиск блока настроек в списке маршрутов routeList#
Полученное на предыдущем шаге имя маршрута сопоставляется со списком элементов следующей секции блока routing - routeList.
Секция правил определения Authority (routeList):
routeList:
- routeName: AnotherService
destinationExpression: 'system1-anotherservice-system2-rs'
- routeName: default
destinationExpression: variables['SCName'] + '-' + variables['ServiceName'] + '-' + variables['SPName']
variables:
- name: ServiceName
valueFrom:
- type: fromBody
value: local-name(/*)
expr: value.substring(0,value.length()-2)
- name: SCName
valueFrom:
- type: fromRfh2Header
value: SCName
- type: fromConst
value: 'System1'
expr: value.toLowerCase()
- name: SPName
valueFrom:
- type: fromRfh2Header
value: SPName
- type: fromBody
value: //SystemID
expr: value+'-rs'
destinations:
'system1-someservice-system2-rs': 'system1-someservice-system2-async-rs'
Каждый элемент этой секции содержит именованный набор правил вычисления Authority сервиса назначения и состоит из параметров:
Параметр |
Описание |
|---|---|
|
Имя маршрута, сопоставляется с полученным значением |
|
Задает выражение, для непосредственного вычисления |
|
Задает список именованных переменных, используемых в |
|
Список соответствия вычисленных значений зафиксированным в этом списке. Позволяет при необходимости полученное значение другим |
Если в списке routeList найден блок с именем, соответствующим полученному routeName, то он используется для определения Authority.
Если нужный routeName не найден, но задан блок с routeName: default, то для определения Authority будет использован он. Если и блок default не задан, то будет сгенерировано исключение.
Шаг 3. Определение значений переменных#
Шлюз MQ вычисляет значения всех переменных, описанных в списке variables. Каждый элемент списка variables содержит имя (name), список правил вычисления (type, value) и дополнительное выражение (expr).
Секция правил определения Authority (routeList):
routeList:
- routeName: AnotherService
variables:
- name: ServiceName
valueFrom:
- type: fromBody
value: local-name(/*)
expr: value.substring(0,value.length()-2)
- name: SCName
valueFrom:
- type: fromRfh2Header
value: SCName
- type: fromConst
value: 'System1'
expr: value.toLowerCase()
Принцип применения type, value и expr был детально рассмотрен выше, при описании вычисления routeName. Например, для приведенного примера значение параметра variables['ServiceName'] будет взято из корневого тега сообщения с обрезанием двух последних символов.
Шаг 4. Определение destinationExpression#
Полученные на шаге 3 значения параметров Шлюз MQ подставляет в выражение, заданное в destinationExpression и вычислив его, получает Authority вызываемого сервиса.
В частном случае destinationExpression может быть задано константой.
Шаг 5. Подстановка#
В блоке правил может быть задан список destinations.
destinations:
'system1-someservice-system2-rs': 'system1-someservice-system2-async-rs'
Если он задан, то Шлюз MQ производит в нем поиск по наименованию значения, полученного на шаге 4. Если поиск успешен, то Шлюз MQ производит замену Authority на значение из найденного элемента. Если список destinations не задан или поиск в нем неудачен, то используется значение, полученное на шаге 4.
Шаг 6. Выполнение вызова#
Шлюз MQ выполняет вызов сервиса по найденному Authority.
Примечание
Прочие секции настроек блока
routing, кроме приведенных здесь, на маршрутизацию сообщений не влияют.
Правила определения очереди и менеджера сообщения при отправке сообщения в MQ#
Возможность изменения назначения по умолчанию (задаваемого через настройки шлюза mq.connection.sendQueue, mq.connection.connections.queueManager) активируется настройкой mq.connection.sendToCustomDestination = true.
На определение конкретной точки назначения при отправке сообщения в MQ влияет наличие во входящем gRPC-вызове и Proto-сообщении следующих заголовков:
Заголовок |
Положение |
Описание |
|---|---|---|
|
gRPC-вызов |
Заголовок в контексте gRPC-вызова. |
|
gRPC-вызов |
Заголовок в контексте gRPC-вызова. |
|
|
Поле в структуре типа |
|
|
Поле в структуре типа |
|
|
Заголовок в списке |
|
|
Заголовок в списке |
|
|
Параметр в настройках Шлюза MQ. |
Важно! Настройка
mq.connection.connections.queueManager, заголовкиx-synapse-override-destination-manager,destinationManager,ReplyToQMgrиспользуются только при работе с провайдером IBM MQ.
При работе с провайдером IBM MQ, во всех случаях, когда определена очередь, но не определен менеджер, сообщение отправляется без указания менеджера. В этих случаях менеджер назначения определяется кластером MQ.
Блок-схема алгоритма маршрутизации в MQ:
Схема. Определение очереди и менеджер назначения
Спецификация интерфейсов#
См. документ «Спецификация интерфейсов».
Преобразование интерфейсов#
См. документ «Преобразование интерфейсов».
Порядок настройки Шлюза MQ#
См. документ «Порядок настройки Шлюза MQ».
Полное описание настроек#
См. документ Полное описание настроек.
Часто встречающиеся проблемы и пути их устранения#
Проблемы, возникающие у прикладного разработчика в процессе разработки конечного продукта, аналогичны проблемам при эксплуатации компонента, описанным в разделе «Часто встречающиеся проблемы и пути их устранения» в Руководстве по системному администрированию.