Руководство прикладного разработчика#
В настоящем документе приведены инструкции для прикладного разработчика компонента JDBC Gateway (код JDBC), далее — Шлюз БД, из состава программного продукта Platform V Synapse Enterprise Integration (код SEI).
Термины и определения#
Термин/Аббревиатура |
Определение |
|---|---|
Деплоймент |
Файл, содержащий набор инструкций для запуска приложения в Kubernetes |
Pod |
Абстрактный объект, набор контейнеров в архитектуре Kubernetes |
БД |
База данных |
Курсор |
тип параметра в базе данных, результирующий набор строк |
gRPС |
Высокопроизводительный фреймворк, разработанный компанией Google для вызова удаленных процедур (RPC) |
SVPX |
Компонент Сервисный прокси Продукт Platform V Synapse Service Mesh (SSM). Применение сетевых политик кластера для внутренних взаимодействий |
LOGA |
Компонент Журналирование Продукт Platform V Monitor (OPM). Извлечение записей журнала Шлюза БД и передача их в подсистему журналирования |
PSQL |
Компонент Platform V Pangolin Продукт Platform V Pangolin SE (PSQ). Система управления базами данных, основанная на PostgreSQL |
HashiCorp Vault |
Компонент HashiCorp Vault, реализующий безопасное хранилище секретной информации |
Системные требования#
Системные требования описаны в Руководстве по установке в разделе "Системные требования".
Подключение и конфигурирование#
Подключение Шлюза БД#
Для установки экземпляра Шлюза БД на стенд необходимо в соответствующий проект загрузить конфигурационные артефакты:
Артефакт |
Содержание |
Описание |
|---|---|---|
Deployment |
Параметры запуска контейнера приложения в Kubernetes |
Наименование экземпляра приложения, ссылка на образ контейнера приложения, запрашиваемые ресурсы, публикуемые порты, параметры liveness и readiness проб, необходимость и параметры подключения sidecar контейнеров, точки монтирования конфигурационных артефактов в файловую систему контейнера. |
ConfigMap |
application.yml |
Файл содержащий параметры конфигурации приложения |
ConfigMap |
db_explorer.yml |
Файл содержащий параметры вызова процедур/функций в базе данных |
ConfigMap |
secman.txt |
Файл со списком секретов, которые нужно ждать из HashiCorp Vault |
Service |
Артефакт для регистрации приложения в service discovery Kubernetes |
Селекторы и порты для подключения приложения к механизмам распределения трафика Kubernetes |
ServiceEntry |
Артефакт для регистрации внешнего сервиса в Kubernetes |
Содержит адреса хостов и номера портов для подключения к базе данных. Если для нужных хостов и портов уже установлены ServiceEntry в проекте, то грузить их повторно не требуется |
Последовательность загрузки артефактов:
ConfigMap c application.yml
ConfigMap c db_explorer.yml
ServiceEntry
Deployment
Service
Порядок действий при загрузке артефактов в проект описаны в Руководстве по установке раздел Установка Шлюза БД .
Конфигурирование Шлюза БД#
Описание конфигурации application.yml - должна быть определена как ConfigMap
server:
port: 8787 # порт приложения
grpc:
maxMessageSizeInMb: 8 # максимальный размер сообщения в мегабайтах
server:
server-port: 5454 # порт gRPC сервера
threadpool:
maxconcurrentconsumers: 20 # максимальное количество потоков обработки gRPC-запросов, которое может использовать сервер
concurrentconsumers: 15 # количество потоков обработки gRPC-запросов при старте сервера
threadkeepaliveseconds: 30 # параметр, который определяет сколько времени (в секундах) неиспользуемые потоки будут оставаться в памяти
client:
settings:
default:
hostname: <hostname> # имя клиента gRPC
port: 5454 # порт клиента gRPC
service:
settings:
enable-param-logging: false # логический параметр, отвечающий за журналирование IN/OUT-параметров, передаваемых и получаемых из хранимых процедур
scheduler:
enabled: true # логический параметр, отвечающий за отправку метрик жизненного цикла сертификата
initial-delay: 1000 # начальная задержка перед отправкой метрик жизненного цикла сертификата в миллисекундах
check-interval: 10000 # периодичность отправки метрик жизненного цикла сертификата в миллисекундах
datasource:
jdbc-url: <hostname БД>:<port>/<database> # подключения к базе данных
maximum-pool-size: 2 # максимальное количество подключений в пуле базы данных
pool-name: HHHGGGFFFF # имя пула базы данных
username: <username> # логин пользователя базы данных
ds:
prop:
tlsprotocol: TLSv1.2 # требуемая версия TLS протокола JVM
cipherSuites: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 # требуемый алгоритм шифрования JVM
ssl: true # логический параметр, отвечающий за включение ssl соединения
sslmode: verify-full # метод аутентификации при заданном параметре ssl: true
sslhostnameverifier: com.sbt.synapse.dnverify.DnHostnameVerifier # проверка всего DN серверного сертиката (работает при sslmode=verify-full)
sslalloweddn: 'CN=10.xx.xx.xx, O=SBRF, C=RU' # разрешенный список DN серверных сертификатов в формате строки
dnmaxcount: 1 # параметр, указывающий максимальное количество разрешенных DN
sslrootcert: /deployments/config/db/ca.crt # путь к корневому сертификату
sslcert: /deployments/config/db/cert.crt # путь к клиентскому сертификату
sslkey: /deployments/config/db/key.pk8 # путь к файлу клиентского ключа
crypto:
enabled: true # логический параметр, отвечающий за включение/выключение поддержки шифрования
keyExpirationHours: 24 # интервал обновления симметричного ключа (в часах)
keySize: 256 # размер симметричного ключа в битах
asymmetricAlgorithmName: RSA # алгоритм ассиметричного шифрования
asymmetricMode: ECB # режим ассиметричного шифрования
asymmetricPadding: PKCS1Padding # схема заполнения для ассиметричного шифрования
symmetricAlgorithmName: AES # алгоритм симметричного шифрования
symmetricMode: GCM # режим симметричного шифрования
symmetricPadding: PKCS7Padding # схема заполнения для ассиметричного шифрования
provider: BC # крипто провайдер
returnAsIsOnError: true # если true и при расшифровке сообщения возникнет ошибка Шлюз БД вернет сообщение в исходном виде
# если он равен false, то при возникновении ошибки выбросит exception и обработка сообщения прекратится
# флаг нужен для одновременной работы с зашифрованными и незашифрованными данными
secman:
role: "role-ga-secman-syte" # роль HashiCorp Vault в кластере
vault-addr: "t.secrets.delta.sbrf.ru" # стенд HashiCorp Vault
os-addr: "dev-gen2.delta.sbrf.ru" # стенд кластера
vault-path: "CI01994970_CI02129749/A/SNPS/OSH/IFT/KV/CRYPTOCIPHER" # путь в HashiCorp Vault, где лежат сертификаты шифратора (Важно! Ключи в секрете должны быть названы pem и key)
vault-ns: "CI01994970_CI02129749" # имя хранилища в HashiCorp Vault
Описание конфигурации db_explorer.yml - должна быть определена как ConfigMap
entity:
entityName: "test" # название справочника
schema: "SRD_LOG" # название схемы, в которой хранятся процедуры/функции
actions:
- name: "getOrderId" # название для связи процедуры с запросом. В запросе записывается в поле `Entity.Action`
packageName: # название пакета, в котором хранится процедура. Может быть пустым
functionName: "getorderbyid" # название функции в базе данных
commit: true # логический параметр, обозначающий фиксацию транзакции (необязательный параметр, требуется для функций с курсорами)
fields:
- type: "INTEGER" # тип параметра. Поддерживаются типы: `STRING`, `NUMBER`, `DATA`, `TIMESTAMP`, `CLOB`, `BLOB`, `CURSOR`, `INTEGER`, `DOUBLE`, `TEXT`, `BYTEA`, `BIGINT`, `DECIMAL`, `BOOLEAN`.
name: "orderId" # название параметра для связи параметра с запросом.
nameInProcedure: "order_id" # название параметра процедуры/функции в базе данных.
input: true # параметр логического типа, обозначающий входящие и исходящие параметры процедур/функций.
encrypt: false # при значении true, входящий параметр отправится в базу данных в зашифрованном виде, а исходящий параметр расшифруется из базы данных
- type: "CURSOR"
name: "out_result"
nameInProcedure: "OUT_CURSOR"
input: false
encrypt: true # для параметров типа CURSOR, нужно указывать значение шифратора
cursorColumns: # для параметра типа `CURSOR` задается описание каждого столбца курсора.
- type: "INTEGER" # тип поля курсора. Поддерживаются типы: `STRING`, `NUMBER`, `DATA`, `TIMESTAMP`, `CLOB`, `BLOB`, `INTEGER`, `DOUBLE`, `TEXT`, `BYTEA`, `BIGINT`, `DECIMAL`, `BOOLEAN`.
name: "id" # название столбца курсора. Используется для связи с описанием при обращении к полям в Proto-сообщении.
nameInProcedure: "id" # название столбца курсора в процедуре/функции базы данных.
- type: "INTEGER"
name: "customerid"
nameInProcedure: "customerid"
- type: "TIMESTAMP"
name: "orderdate"
nameInProcedure: "orderdate"
- type: "NUMBER"
name: "totalprice"
nameInProcedure: "totalprice"
- type: "TEXT"
name: "description"
nameInProcedure: "description"
encrypt: true
- name: "get"
packageName:
procedureName: "GETAGGREGATION" # название процедуры в базе данных
fields:
- type: "STRING"
name: "msgId"
nameInProcedure: "IN_MSG_ID"
input: true
- type: "STRING"
name: "aggId"
nameInProcedure: "IN_AGGR_ID"
input: true
- type: "CLOB"
name: "message"
nameInProcedure: "OUT_MSG_BODY"
input: false
Описание конфигурации ServiceEntry
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: db-service-entry
labels:
app: db-gateway
spec:
exportTo:
-.
hosts:
- <hostname БД> # хост для подключения к базе данных
ports:
- name: tcp
number: 1523 # порт для подключения к базе данных
protocol: tcp
resolution: DNS
Описание конфигурации Deployment
kind: Deployment
apiVersion: apps/v1
metadata:
name: service-db-module
labels:
app: service-db-module
secman-injector: enabled
spec:
replicas: 2
selector:
matchLabels:
app: service-db-module
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 25%
template:
metadata:
labels:
app: service-db-module
name: service-db-module
secman-injector: enabled # включение сайдкара vault-agenta ФП HashiCorp Vault
annotations:
sidecar.istio.io/inject: 'true' # включение/отключение контейнера Компонента «Сервисный прокси» (например, true/false)
sidecar.istio.io/proxyCPU: 500m # ресурсы для cpu request для контейнера Компонента «Сервисный прокси» (например, 500m)
sidecar.istio.io/proxyCPULimit: 500m # ресурсы для cpu limit для контейнера Компонента «Сервисный прокси» (например, 500m)
sidecar.istio.io/proxyMemory: 512Mi # ресурсы для memory request для контейнера Компонента «Сервисный прокси» (например, 512m)
sidecar.istio.io/proxyMemoryLimit: 512Mi # ресурсы для memory limit для контейнера Компонента «Сервисный прокси» (например, 512m)
vault.hashicorp.com/agent-inject: 'true' # включение/отключение sidecar HashiCorp Vault
vault.hashicorp.com/namespace: <NAMESPACE_VAULT> # имя созданного хранилища в HashiCorp Vault (например, CI01994970_CI02129749)
vault.hashicorp.com/role: <SECMAN_ROLE> # имя полученной роли в HashiCorp Vault (например, ci01994970_ci02129749_a_snps_osh_ift_ac)
vault.hashicorp.com/secret-volume-path: <SECMAN_MOUNT_PATH> # путь для загрузки секретов в файловой системе Pod (например, /deployments/config/db/certs)
vault.hashicorp.com/agent-inject-secret-cert.crt: 'true' # включить чтение секрета cert.crt
vault.hashicorp.com/agent-inject-template-cert.crt: | # создается файл cert.crt в файловой системе
{{- with secret <SECRET_PATH> -}} # путь до секрета в HashiCorp Vault (например, CI01994970_CI02129749/A/SNPS/OSH/IFT/KV/JDBC)
{{ base64Decode .Data.cert }} # значение из HashiCorp Vault по ключу cert, переведенное из base64
{{- end }}
vault.hashicorp.com/agent-inject-secret-ca.crt: 'true' # включить чтение секрета ca.crt
vault.hashicorp.com/agent-inject-template-ca.crt: | # создается файл ca.crt
{{- with secret <SECRET_PATH> -}} # путь до секрета в HashiCorp Vault (например, CI01994970_CI02129749/A/SNPS/OSH/IFT/KV/JDBC)
{{ base64Decode .Data.ca }} # значение из HashiCorp Vault по ключу ca, переведенное из base64
{{- end }}
vault.hashicorp.com/agent-inject-secret-key.pk8: 'true' # включить чтение секрета key.pk8
vault.hashicorp.com/agent-inject-template-key.pk8: | # создается файл key.pk8 в файловой системе
{{- with secret <SECRET_PATH> -}} # путь до секрета в HashiCorp Vault (например, CI01994970_CI02129749/A/SNPS/OSH/IFT/KV/JDBC)
{{ base64Decode .Data.key }} # значение из HashiCorp Vault по ключу key, переведенное из base64
{{- end }}
vault.hashicorp.com/agent-inject-secret-db-secret.yml: 'true' # включить чтение секрета db-secret.yml
vault.hashicorp.com/agent-inject-template-db-secret.yml: | # создается файл db-secret.yml
{{- with secret <SECRET_PATH> -}} # путь до секрета (например, CI01994970_CI02129749/A/SNPS/OSH/IFT/KV/JDBC)
{{ base64Decode .Data.secret }} # значение из HashiCorp Vault по ключу db-secret.yml, переведенное из base64
{{- end }}
prometheus.io/path: /actuator/prometheus # путь контроллера, с которого собирать метрики
prometheus.io/scheme: http # схема работы, по которой будет отправлять метрики prometheus
prometheus.io/port: '8787' # порт, на котором доступен web server для prometheus
spec:
volumes:
- name: application-config
configMap:
name: service-db-module-app-config # ConfigMap c параметрами конфигурации Шлюза БД
items:
- key: application.yml
path: application.yml
defaultMode: 256
- name: entity-config
configMap:
name: service-db-module-entity-config # ConfigMap c параметрами вызова процедур/функций в базе данных
items:
- key: db_explorer.yml
path: db_explorer.yml
defaultMode: 256
- name: synapselogs
emptyDir: {}
- name: db-postgre-secman
configMap:
name: db-postgre-secman # ConfigMap со списком секретов, которые нужно ждать из HashiCorp Vault
items:
- key: secman.txt
path: secman.txt
defaultMode: 256
containers:
- resources:
limits:
cpu: 500m # cpu limit для контейнера приложения (например, 500m)
memory: 700Mi # memory limit для контейнера приложения (например, 700Mi)
requests:
cpu: 500m # cpu request для контейнера приложения (например, 500m)
memory: 700Mi # memory request для контейнера приложения (например, 700Mi)
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8787
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
terminationMessagePath: /dev/termination-log
securityContext:
capabilities:
drop:
- ALL
privileged: false
runAsNonRoot: true
readOnlyRootFilesystem: true
name: service-db-module
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8787
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
periodSeconds: 10
successThreshold: 1
failureThreshold: 5
env:
- name: TZ
value: Europe/Moscow
- name: PROJECT_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: JAVA_OPTS
value: '-XX:+UseG1GC -XX:+ExitOnOutOfMemoryError' # параметры запуска JVM (например, -XX:+UseG1GC -XX:+ExitOnOutOfMemoryError)
ports:
- containerPort: 5454
protocol: TCP
- containerPort: 8787
protocol: TCP
imagePullPolicy: Always
volumeMounts:
- name: application-config
readOnly: true
mountPath: /deployments/config
- name: entity-config
readOnly: true
mountPath: /deployments/config/entity
- name: synapselogs
mountPath: /opt/synapse/logs
- name: db-postgre-secman
readOnly: true
mountPath: /tmp/secman/config
terminationMessagePolicy: File
image: >-
<ссылка на Docker-образ Шлюза БД в целевом репозитории> # ссылка на Docker-образ Шлюза БД
restartPolicy: Always
terminationGracePeriodSeconds: 60
dnsPolicy: ClusterFirst
Описание конфигурации Service
apiVersion: v1
kind: Service
metadata:
name: service-db-module
labels: { app: service-db-module }
spec:
selector: { app: service-db-module} # Селектор для подключения приложения Шлюза БД
ports: # Порт для подключения приложения Шлюза БД
- name: grpc
port: 5454
targetPort: 5454
- name: http
protocol: TCP
port: 8787
targetPort: 8787
Миграция на текущую версию#
Для миграции на текущую версию требуется:
Уточнить в разделе Примечания к релизу необходимость внесения изменений в конфигурацию Шлюза БД при переходе на текущую версию.
При необходимости подготовить ConfigMap с новой конфигурацией.
Подготовить артефакт Deployment, в котором заменить ссылку на Docker-образ Шлюза БД в репозитории ссылкой на Docker-образ с текущей версией.
Если это указано в разделе Примечания к релизу, изменить значения выделяемых ресурсов.
Остановить Шлюз БД.
Загрузить новую конфигурацию Шлюза БД.
Загрузить новый Deployment.
Запустить Шлюз БД (если в Deployment указано количество реплик > 0, Шлюз БД запустится автоматически).
Дополнительно:
Порядок действий при обновлении - см. в Руководстве по установке раздел Обновление.
Остановка/запуск Шлюза БД - см. в разделе Руководство по системному администрированию
Быстрый старт#
Установка компонентов#
Порядок действий для подключения Шлюза БД, общих компонентов, сервера базы данных описан в Руководстве по установке раздел Установка .
Настройка базы данных#
Для настройки базы данных требуется создать в базе данных необходимые PL/SQL-процедуры, предварительно создав пользователя базы данных и предоставив ему права на вызов процедур. Инструкция по созданию пользователя и выдаче прав в базе данных находится в Руководстве по системному администрированию раздел Авторизация.
Запуск и проверка работоспособности Шлюза БД#
Запустите Pod Шлюз БД в Kubernetes (подробнее в Руководстве по системному администрированию раздел Изменение количества Pod).
Проверка работоспособности Шлюза БД описана в Руководстве по установке разделе Проверка работоспособности.
Проверки корректности работы Шлюза БД описаны в Программе и методике испытаний разделе Методы испытаний.
Использование программного компонента#
Использование пользовательских метрик мониторинга#
Формирование метрик мониторинга осуществялется с помощью Spring Actuator. Метрики в формате prometheus доступны по адресу:
< ip >:< spring_boot_server_port >/actuator/prometheus.
Помимо стандартных метрик Spring Boot приложения добавлена метрика "sended_requests_total", показывающая общее количество вызовов каждой процедуры и функции.
Вызов Шлюза БД по gRPC из другого приложения#
Для вызова Шлюза БД необходимо подключить к приложению, делающему вызов, библиотеки com.sbt.synapse:synapse-esb-database-api и com.sbt.synapse:synapse-grpc-core:
dependencies {
implementation "com.sbt.synapse:synapse-esb-database-api:1.3.0"
implementation "com.sbt.synapse:synapse-grpc-core:2.0.34"
}
Также необходимо задать адрес подключения к Шлюзу БД в spring-настройках. Обычно это делается в файле application.yml для самостоятельных spring-приложений или в ConfigMap для Kubernetes-сервисов:
grpc:
client:
settings:
channelName:
hostname: <grpc server host> # имя хоста grpc сервера Шлюза БД, указанное в Service Шлюза БД
port: <grpc server port> # порт grpc сервера Шлюза БД, указанный в Service Шлюза БД
Вызов осуществляется следующим образом:
@Service
public class Scenario {
// Ссылка на Шлюз БД
@Grpc(serviceName = "com.sbt.synapse.database.DbService/dbCallStoredProcedure")
GrpcService<ProtoDBAction, ProtoDBAction> grpcService;
public void callDBcreateOrder(Order order){
// Вызов Шлюза БД
ProtoDBAction.newBuilder().setMessageId("1111-2222-3333").setCorrelationId("9999-8888-7777").setBody("big big body")
.setEntity(Entity.newBuilder().setName("name").setAction("createOrder")
.putFields("customerid", BuilderUtils.singleParam(String.valueOf(order.getCustomerId())))
.putFields("totalprice", BuilderUtils.singleParam(String.valueOf(order.getTotalPrice())))
.putFields("description", BuilderUtils.singleParam(String.valueOf(order.getDescription()))).putAllSystemHeaders( new HashMap<String, String>() {{
put("x-synapse-rquid", "rquid1");
put("x-synapse-operationname", "operation2");
}}).putAllUserHeaders(new HashMap<String, String>() {{
put("RqUID", "rquid1");
put("OperationName", "operation2");
put("ServiceName", "initService");
}})).build();
CallInfo<ProtoDBAction, ProtoDBAction> call = grpcService.invoke(protoDBAction);
try {
ProtoDBAction response = call.execute();
if (response.hasEntity()) { // Обработка прошла успешно
String newIdStr = BuilderUtils.getParamValue(response, "orderid");
} else if (response.hasProcedureAppException()) {
log.error("Процедура вернула ошибку через RAISE_APPLICATION_ERROR");
}
} catch (Exception e) {
log.error("ERROR with grpc invoking", e);
}
}
}
Также поддерживаются асинхронные вызовы:
@Service
public class Scenario {
// Ссылка на Шлюз БД
@Grpc(serviceName = "com.sbt.synapse.database.DbService/dbCallStoredProcedure")
GrpcService<ProtoDBAction, ProtoDBAction> grpcService;
public void testSynapseStubAsync(ProtoDBAction request) throws Exception {
CountDownLatch latch = new CountDownLatch(10);
CallInfo<ProtoDBAction, ProtoDBAction> call = grpcService.invoke(request).channel("agg");
long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
call.executeAsync(new FutureCallback<ProtoDBAction>() {
@Override
public void onSuccess(@NullableDecl ProtoDBAction result) {
log.info("Time to result: " + (System.currentTimeMillis() - start));
assertTrue(result.getEntity().getFieldsMap().isEmpty());
latch.countDown();
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
fail("Request failed");
}
});
}
long end = System.currentTimeMillis();
log.info("Time to request: " + (end - start));
latch.await(10L, TimeUnit.SECONDS);
}
}
Пусть существует процедура получения времени создания документа по его идентификатору:
create function createordercipherreturnid(customer_id integer, total_price numeric, descr text) returns integer
language plpgsql
as
$$
DECLARE
order_id integer;
BEGIN
INSERT INTO synapse."order"(customerid,orderdate,totalprice,description)
VALUES (customer_id, CURRENT_TIMESTAMP,total_price,descr) RETURNING id into order_id;
RETURN order_id;
END;
$$;
Тогда справочник db_explorer.yaml в ConfigMap — service-db-module-entity-config может иметь следующий вид:
entity:
entityName: "test"
schema: synapse
actions:
- name: "createOrder"
packageName:
functionName: "createordercipherreturnid"
commit: true
fields:
- type: "INTEGER"
name: "customerid"
nameInProcedure: "customer_id"
input: true
- type: "NUMBER"
name: "totalprice"
nameInProcedure: "total_price"
input: true
- type: "TEXT"
name: "description"
nameInProcedure: "descr"
input: true
encrypt: true
- type: "INTEGER"
name: "orderid"
nameInProcedure: "order_id"
input: false
Если рассмотреть пример с обращением к функции createordercipherreturnid из примера выше, то в соответствии со справочником объект request необходимо будет сформировать следующим образом:
ProtoDBAction.newBuilder().setEntity(Entity.newBuilder().setName("name").setAction("createOrder")
.putFields("customerid", BuilderUtils.singleParam(String.valueOf(order.getCustomerId())))
.putFields("totalprice", BuilderUtils.singleParam(String.valueOf(order.getTotalPrice())))
.putFields("description", BuilderUtils.singleParam(String.valueOf(order.getDescription()))).putAllSystemHeaders( new HashMap<String, String>() {{
put("x-synapse-rquid", "rquid1");
put("x-synapse-operationname", "operation2");
}}).putAllUserHeaders(new HashMap<String, String>() {{
put("RqUID", "rquid1");
put("OperationName", "operation2");
put("ServiceName", "initService");
}})).build();
Сигнатуры доступных методов#
dbCallStoredProcedure (ProtoDBAction) returns (ProtoDBAction) — обращение к хранимой процедуре базы данных.
Необходимые данные из ответа можно получить следующим образом:
ProtoDBAction response = call.execute();
if (response.hasEntity()) {
String newIdStr = BuilderUtils.getParamValue(response, "orderid");
}
Если проекте не используется библиотека synapse-grpc-core, то вызов можно осуществить, используя базовую gRPC-библиотеку io.grpc.grpc-all. Вызов осуществляется следующим образом:
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
try {
ProtoDBAction response = DbServiceGrpc.newBlockingStub(channel).dbCallStoredProcedure(request);
if (response.hasEntity()) {
// Обработка прошла успешно
} else if (response.hasProcedureAppException()) {
// Процедура вернула ошибку через RAISE_APPLICATION_ERROR
}
} catch (Exception e) {
// Вызов закончился с ошибкой
}
channel.shutdown();
Для асинхронных вызовов:
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
ListenableFuture<ProtoDBAction> future = DbServiceGrpc.newFutureStub(channel).dbCallStoredProcedure(request);
Futures.addCallback(future, new FutureCallback<ProtoDBAction>() {
@Override
public void onSuccess(@NullableDecl ProtoDBAction response) {
if (response != null) {
if (response.hasEntity()) {
// Обработка прошла успешно
} else if (response.hasProcedureAppException()) {
// Процедура вернула ошибку через RAISE_APPLICATION_ERROR
}
}
}
@Override
public void onFailure(Throwable t) {
// Вызов закончился ошибкой
}
}, executor);
Логирование#
В процессе работы Шлюз БД фиксирует события в логах.
Системное логирование#
В системном логе фиксируются события, возникающие в приложении в процессе его работы. По умолчанию системный лог выводится в консоль контейнера приложения.
Доступ к системному логу можно получить через Web-интерфейс Kubernetes (Workloads->Pods->Имя pod->вкладка Logs).
Системный лог в формате txt может быть выгружен через консоль клиента Kubernetes командой:
kubectl logs -c <имя контейнера> <имя pod> > <имя файла>.txt
Канал вывода лога, уровень логирования и шаблон записи события задается файле logback.xml. Для изменения настроек по умолчанию в конфигурации шлюза может быть задано иное положение файла logback.xml:
logging:
config:
Также уровень системного логирования можно изменить, задав его в конфигурации шлюза БД. Уровни ведения журнала на уровне пакетов можно установить через файл конфигурации application.yml (ConfigMap "service-db-module-app-config"):
logging:
level:
com.sbt.synapse: debug # TRACE, DEBUG, INFO, WARN, ERROR, FATAL или OFF
com.sbt.synapse.log: trace
Таблица – Уровни логирования
Уровень логирования |
Детальное описание |
|---|---|
OFF |
сообщения не выводятся |
FATAL |
ошибка, после которой приложение уже не сможет работать и будет остановлено |
ERROR |
уровень ошибок, когда есть проблемы, которые нужно решить. Ошибка не останавливает работу приложения в целом. Остальные запросы могут работать корректно |
WARN |
обозначаются логи, которые содержат предостережение. Произошло неожиданное действие, несмотря на это система устояла и выполнила запрос |
INFO |
лог, который записывает важные действия в приложении. Это не ошибки, это не предостережение, это ожидаемые действия системы |
DEBUG |
логи, необходимые для отладки приложения. Для уверенности в том, что система делает именно то, что от нее ожидают, или описания действия системы (не рекомендуется для использования в ПРОМ среде) |
TRACE |
информация для точной отладки (не рекомендуется для использования в ПРОМ среде) |
Примеры событий уровней логирования
2022-10-09 20:53:11.261 [DEBUG] [[grpc-transport]-1] [c.s.s.t.g.ServerTracerHeadersInterceptor] [T:] - Метадата входящего вызова: Metadata(content-type=application/grpc,user-agent=grpc-java-netty/1.39.0,grpc-accept-encoding=gzip,grpc-timeout=60000m,x-forwarded-proto=http,x-request-id=e9743312-4ade-9dae-b098-219b8a10c7bc,x-envoy-expected-rq-timeout-ms=60000,x-b3-traceid=d751cf4d197425ea1f9765cdea09c78f,x-b3-spanid=1f9765cdea09c78f,x-b3-sampled=1)
2022-10-09 20:53:11.263 [INFO ] [[grpc-transport]-1] [com.sbt.synapse.log.ServiceLogger] [T:] - Receive dbRequest entity name: test, action: getOrderId,
2022-10-09 20:53:11.263 [INFO ] [[grpc-transport]-1] [com.sbt.synapse.log.ServiceLogger] [T:] - In Parameter orderId value 76
2022-10-09 20:53:11.263 [DEBUG] [[grpc-transport]-1] [c.s.s.l.g.SynapseMessageLoggerImpl] [T:] - Логирование события Получен запрос: getOrderId к базе данных. RqUID: null
2022-10-09 20:53:11.278 [DEBUG] [[grpc-transport]-1] [c.s.s.l.g.SynapseMessageLoggerImpl] [T:] - Логирование события Запрос: getOrderId к базе данных выполнен успешно. RqUID: null
Прикладное логирование#
В прикладном логе фиксируются следующие шаги прохождения интеграционной цепочки:
Шлюз БД получил сообщение по gRPC от микросервиса;
Шлюз БД вызвал процедуру/функцию в базе данных;
Шлюз БД получил ответ из базы данных;
Шлюз БД вызвал микросервис по gRPC и отправил ему сообщение;
Ошибка на Шлюзе БД.
Для корректного отображения в прикладном логе в Шлюзе БД должен быть заданы:
Заголовок
x-synapse-rquid;Заголовок
x-synapse-operationname;Заголовок
RqUID;Заголовок
OperationName;Переменная
ServiceName.
Прикладной лог пишется в контейнер приложения. По умолчанию прикладной лог пишется в файлы:
/opt/synapse/logs/messages*.log
Прикладные логи могут быть извлечены из контейнера приложения и отправлены в подсистему журналирования платформы. Для этого в deployment Шлюза БД должен быть сконфигурирован sidecar-контейнер с компонентом платформы Synapse — Агентом журналирования.
Порядок подключения и использования Агента журналирования приведены в разделе Подключение и конфигурирование документа Руководство прикладного разработчика на компонент Сервис Журналирование (LOGA) Platform V Backend Platform.
Часто встречающиеся проблемы и пути их устранения.#
При работе с OUT-параметрами типа CLOB возникают ограничения, связанные с особенностями реализации spring-jdbc. Так, отсутствует возможность получить параметр из процедуры LOB напрямую. Шлюз БД ожидает, что LOB-данные будут входить в состав курсора.
Следовательно, процедура, которой необходимо передать ответ в виде CLOB или BLOB, должна передавать его в виде курсора. Пример реализации такого подхода на уровне PL/SQL-кода приведен ниже:
CREATE OR REPLACE PROCEDURE GET_MESSAGE(IN_MSG_ID IN VARCHAR2, OUT_MSG_BODY OUT SYS_REFCURSOR) AS
BEGIN
OPEN OUT_MSG_BODY FOR
SELECT MESSAGE_BODY FROM MESSAGE_TABLE WHERE MSG_ID = IN_MSG_ID;
END GET_MESSAGE;
Если хранимая процедура формирует исключения через RAISE_APPLICATION_ERROR, то исключение обрабатывается и возвращается инициатору запроса в виде ProtoDBAction.ProcedureAppException. На стороне Шлюза БД это будет считаться нормальным поведением и будет отражено в журнале как штатная обработка запроса к базе.