какие виды типы есть интеграции в it
ИТ-интеграция – миссия выполнима
Время просмотра: 5.3 мин.
Прошло несколько лет, у нашего владельца уже 10 магазинов, 2 склада, недавно стартовали интернет-продажи. Конечно, с расширением бизнеса пришлось отказаться от Excel как средства учёта, однако отчётность всё равно готовится в нём… Почему же? Потому что для учета товара на складах так и используется уже значительно переделанная программа однокурсника, интернет-магазин был внедрён усилиями сторонней компании, бухгалтерия использует 1С, кадры выбрали решение известного производителя. И ни одно из этих решений «не знает» о существовании других. В итоге ИТ-отделу приходится каждый месяц осуществлять выгрузки из баз данных для бухгалтерии, которая затем неделю пытается свести эту информацию в хоть какую-то отчётность. А в это время владельцу интересно уже не только итоговое сальдо, ему хочется понимать, какие из его магазинов приносят прибыль, а какие – нет, какие товары лучше продаются, какие – хуже, и, в конце концов, кто его клиенты и как сделать так, чтобы они стали больше у него покупать?
Другие статьи автора
Статьи по теме
Поделиться
При всей «игрушечности» примера подобные проблемы рано или поздно встают перед практически любой организацией, будь то небольшая компания, средний бизнес или крупная корпорация. И чем крупнее компания, тем более комплексные задачи приходится решать ее ИТ-отделу. Отметим, что если задача интеграции информационных систем (ИС) в организации еще не стоит остро, но взаимодействие между ними уже должно быть автоматизировано, чаще всего на выходе будет ИТ-решение, во многом внедренное «спонтанно». К сожалению, для него не характерна системность подхода и в каждом конкретном случае задача интеграции решается, как сейчас модно говорить, ad hoc, что фактически каждый раз является «изобретением велосипеда».
Кроме того, на решение задачи интеграции оказывают влияние следующие факторы:
Как же в итоге решать задачу интеграции? Существует целый ряд средств и методов, которые позволяют реализовать задуманное. Их можно разделить на три основные группы, каждая из которых, в свою очередь, включает в себя разнообразные подходы и системы.
Первая группа – это решения для интеграции данных. Фактически подход к интеграции со стороны данных является одновременно и наиболее простым с точки зрения реализации, и наиболее многообразным относительно решаемых при этом задач. Это могут быть импорт данных в разнообразных форматах, их преобразование, очистка от ошибок, исключение дублирующих записей, обогащение, хранение, отображение и др. Ниже будут рассмотрены основные классы систем, непосредственно участвующих и обеспечивающих качественную интеграцию на уровне данных.
Вторая группа – это интеграция приложений. Если интеграция данных чаще применяется для построения хранилищ и аналитических систем, то этот подход главным образом позволяет строить распределенные корпоративные ИС, направленные на обработку оперативных данных. Интеграция приложений чаще всего реализуется с помощью обмена сообщениями и событиями между системами. За счет этого снимается необходимость ручного ввода данных в каждую из интегрируемых систем.
Третья группа – интеграция бизнес-процессов. Когда в организации уже налажены интеграционные взаимодействия между ИС и автоматизированы практически все информационные потоки, следующим этапом становится своеобразная «интеграция» людей-исполнителей и ИС. Выстраивается единая автоматизированная цепочка, в которой человек и «машина» выполняют свою часть работы, образуя эффективный бизнес-процесс.
Фактически рассмотренные нами группы представляют собой три последовательные ступени в процессе построения полноценной и эффективной интеграции. Мы начинаем с нормализации данных, затем налаживаем автоматизированное взаимодействие между приложениями и, наконец, организуем сквозные бизнес-процессы. Рассмотрим указанные группы и применимые в их рамках решения более подробно.
Интеграция данных
Практически в любой компании, независимо от рода ее деятельности, ведется учет информации о контрагентах, с которыми она взаимодействует, о товарах или услугах, которые она реализует, о контрактах, в рамках которых эти товары или услуги поставляются или закупаются. Представим, что это большая и территориально распределенная организация. Как правило, это означает наличие «зоопарка» ИТ-систем, различающихся в головном офисе и территориальных отделениях. В эти различные ИС сотрудники добросовестно заносят данные о клиентах компании. Однако одного и того же клиента могут внести в CRM, ERP и прочие учетные системы столько раз, сколько этих бизнес-приложений, собственно, насчитывается в организации. При этом оператор, вносящий информацию о новом клиенте, например, об «ООО Розмарин», в свою систему, зачастую может даже не знать о том, что его сосед по кабинету сделал то же самое, но в своем приложении. С одной оговоркой – у него новый клиент называется «Торговый дом Розмарин» или даже «Rozmarin Trade». Введенная таким образом информация будет успешно храниться в системах, обрабатываться и дополняться до тех пор, пока руководство не захочет получить консолидированную отчетность по клиенту «Розмарин». Похожая ситуация возникает, когда в разных филиалах торговой организации, имеющих один и тот же ассортимент продукции, номенклатура ведется в локальных системах и, разумеется, по-разному. Представьте себе, сколько возможных вариантов названий может быть, к примеру, у товара «кашпо декоративное керамическое “Весна”». А теперь представьте, как будет выглядеть ответ на запрос от коммерческого отдела: «Каков объем поставки кашпо “Весна” клиенту «Розмарин»?».
Разумеется, до некоторых пор вопрос можно решать организационно, как выдавая четкие инструкции операторам отдельных систем, так и создавая огромное многообразие матриц соответствий и таблиц перекодировок (этот вариант чуть более технологичен). Другой способ – ручное сведение данных в консолидированной отчетности, когда специально выделенный, к примеру, аналитик, метким взором оглядывая огромный отчет, ловко вылавливает из него всевозможные «Розмарины» и решает, один и тот же ли это клиент. Эти решения лишь на первый взгляд кажутся быстрыми, простыми и недорогими, но, к сожалению, они совершенно не приемлемы для крупных и быстрорастущих компаний.
Описанная задача является всего лишь одним из частных случаев применения систем класса MDM (Master Data Management) (подробнее – в статье «Мастерское управление данными», стр. 24). В действительности такие решения отвечают за огромный спектр задач, связанных с управлением основными данными, которые также часто называют эталонными данными или нормативно-справочной информацией (НСИ). Внедрение таких систем позволяет не только эффективно организовать процессы управления ими, обеспечив их актуальность и непротиворечивость, но и повысить качество данных, например, используя технологии их очистки (так называемое Data Quality). Используя определенные модели данных и алгоритмы их обработки, такие решения позволяют автоматически устранять определенные некорректности в данных, образовавшиеся, например, вследствие ошибок оператора. Вернемся к примеру с «Розмарином» и кашпо: возможным решением здесь могла бы стать так называемая дедупликация, которая также относится к классу задач Data Quality.
На сегодняшний день практически в любой организации возникает задача получения различного рода консолидированной отчетности, на основе которой умудренные опытом бизнес-аналитики делают выводы о текущих тенденциях бизнеса и принимают те или иные стратегические решения. Как правило, для получения подобного рода отчетности строятся громадные хранилища, в которые собираются данные из всех основных корпоративных ИС, будь то ERP, CRM или автоматизированная банковская система. Основой же для построения хранилищ практически всегда является система, которая, получая на вход данные в самых разных форматах, преобразовывает их в нужную форму и загружает в хранилище. И когда необходимо серьезное преобразование данных на их пути из одной системы в другую, на помощь приходит технология ETL (Extract, Transform, Load).
Однако сфера ее применения не ограничивается одними лишь хранилищами и BI. В качестве примера можно привести задачу расчета бонусных баллов. На данный момент в России достаточно популярны так называемые программы лояльности, участники которых являются обладателями специальных пластиковых карт. После того как владелец карты совершил покупки в определенных розничных и ресторанных сетях, на автозаправочных станциях и т.п., ему по различным схемам начисляются бонусные баллы.
Пример: абонентам одного из телеком-операторов, являющимся обладателями карт лояльности, полагаются бонусные баллы за оплату тех или иных услуг, например, минут разговора, отправленных SMS и MMS, интернет-трафика и т.п. Количество типов таких услуг и соответствующих алгоритмов начисления баллов – несколько десятков. Количество абонентов, имеющих карты лояльности, – десятки, а то и сотни тысяч. Совершенно очевидна необходимость системы, которая в автоматическом режиме выгружала бы данные об абонентах и предоставленных им услугах, рассчитывала бы баллы, полагающиеся каждому, и передавала бы их в учетную систему программы лояльности.
Нужно отметить, что исходные данные для расчетов находятся в нескольких БД, включая абонентскую базу и базу платежей. Алгоритмы расчета бонусов представляют собой наборы операций, сложность которых может быть совершенно разной – от простейших запросов к единственному источнику до сложной последовательности различных преобразований данных, полученных одновременно из нескольких систем, включая неограниченное количество условных переходов.
Эта задача может быть решена с помощью использования технологии ETL. Построенное решение по определенному расписанию будет запускать сложнейшие расчетные процедуры, в результате формируя данные по начисленным баллам. Более подробно о других решениях, построенных на базе этой технологии, вы можете прочитать во второй части – в № 1, 2013.
Как уже говорилось ранее, интеграционные задачи возникают практически при любом внедрении бизнес-приложений, но есть классы систем, в основе которых изначально лежит интеграционное решение. Примером здесь могут быть платформы класса ECM (Enterprise Content Management), ориентированные на обработку большого количества документов и любой неструктурированной информации, вплоть до аудио- и видеофайлов, а также данных из интернет-источников (более подробно – во второй части, в 1 1, 2013). В основе такого решения лежит хранилище, в которое посредством интеграции помещается информация любого требуемого типа. Перечислим некоторые виды такой интеграции. Во-первых, это сбор электронных документов из бизнес-приложений – контрактов, приказов и распоряжений, ордеров и т.п. К этому же типу задач можно отнести сбор в единое корпоративное хранилище электронных писем, отправляемых или получаемых сотрудниками. Во-вторых, это помещение в хранилище оцифрованных бумажных документов. В этом случае необходима интеграция со средствами сканирования и распознавания. И третий пример – это сбор информации из открытых источников, например, интернета. Представим себе сотрудника отдела маркетинга, ответственного за процесс формирования имиджа компании в СМИ. Одной из его задач является сбор данных об упоминаемости компании в различных источниках, в первую очередь в интернете. Ее решение может быть построено на основе автоматического сбора, структурирования информации по различным признакам (темам, авторам, источникам и т.п.) и ее помещения в единое хранилище. Совершенно очевидно, что такое решение требует интеграции с интернет-источниками.
Интеграция приложений
По вполне понятным причинам автоматизация исторически развивалась по пути наименьшего сопротивления – где болит, там и внедряем программное обеспечение, потому что результат нужен вчера, генеральный в гневе, клиенты уходят… Как ни банально это звучит, решение одной проблемы всегда вызывает к жизни две другие, не менее сложные. Внедрили «по-быстрому» системы в двух отделах – обнаружили, что в обеих реализован справочник клиентов. Прошло пару лет, и вот уже в компании создан отдел НСИ, а отчеты, сформированные из разных систем, противоречат друг другу. И с каждым годом получить достоверные цифры почему-то становится не проще, а сложнее.
Таким же путем шли и при интеграции – системы разрознены, так давайте научим их обмениваться информацией. И снова знакомая картина: чтобы получить данные о клиенте из двух разных систем, специалисты компании написали две процедуры на стороне «получателей». В результате при малейшем изменении сразу возникают проблемы. Нужно, во-первых, не забыть, кто является получателем, во-вторых, внести изменение во все зависимые системы. Здесь уместно вспомнить любимый анекдот ИТ-директора: «Работает? – Ничего не трогай!».
Итак, жизнь подтолкнула к появлению интеграционных шин, или ESB – Enterprise Service Bus. Теперь приложения получили возможность интегрироваться только с шиной и «не знать» о других системах. Как образно выразился один из наших заказчиков, «у нас появится шина, ощетинившаяся интерфейсами». Подробнее об интеграционных шинах и технических деталях их реализации читайте в статье «Дирижер в оркестре сервисов».
Интеграция бизнес-процессов
Менеджменту необходимо получать адекватные и достоверные данные для принятия управленческих решений, сотрудники хотят избежать рутины, да и просто гордиться тем, что работают в современной технологичной компании, где работа организована эффективно и позволяет сосредоточиться на своих задачах, а не продираться сквозь лес ИТ- и «не ИТ-» систем с их разрозненностью и другими несовершенствами. Речь идет о том, что, какой бы автоматизированной система ни была, для обеспечения эффективной работы бизнес-процессов необходимо, чтобы и она, и ее пользователи работали буквально в гармонии друг с другом. То есть нужно сделать так, чтобы, например, длинная цепочка «товар куплен у поставщика, оказался на складе, приобретен клиентом через интернет-магазин и, наконец, учтён в бухгалтерии» в разрезе информационных систем выглядела также единым процессом, а не разрозненным набором операций, выполняемых операторами в самых разных системах. Это под силу BPM.
Технологии BPM (Business Process Management) позволяют сократить время выполнения бизнес-процессов за счёт регламентации и автоматизации шагов, введения временных ограничений и действий «по умолчанию». Соблюдение всех предусмотренных правил и повышение эффективности бизнес-процессов обеспечиваются за счет увеличения прозрачности этих процессов для всех их участников, регламентации и средств мониторинга.
При этом применение BPM позволяет «визуализировать» процессы, причем в динамике. И самое главное – внедрение новых процессов и изменение старых осуществляются гораздо быстрее и практически прозрачно для их участников. Сотрудникам не нужно заучивать новые регламенты, запоминать новый порядок действий или согласований – все это ложится на плечи BPM-системы (подробнее о решениях этого класса – во второй части, № 1, 2013).
Разумеется, мы не претендуем на полное и детальное описание всех существующих технологий интеграции, есть еще, как минимум, интеграция сервисов и пользовательских интерфейсов. Цель этой статьи – дать общее представление о задачах и проблемах интеграции. Далее в этом номере поднятые нами вопросы будут освещены более подробно, а также описаны существующие решения вендоров и опыт реализации подобных проектов.
Интеграция: синхронное, асинхронное и реактивное взаимодействие, консистентность и транзакции
Продолжаю серию статей про хорошую интеграцию. В первой статье я говорил, что хорошая админка обеспечит быстрое решение инцидентов — как ключевой фактор для устойчивости работы всего комплекса систем. Во второй — про использование идемпотентных операций для устойчивой работы в условиях асинхронного взаимодействия и при сбоях. В этой статье рассмотрим синхронный, асинхронный и реактивный способы взаимодействия между сервисами и более крупными модулями. А так же способы обеспечить консистентность данных и организовать транзакции.
На схеме вы можете увидеть все три варианта взаимодействия. Важно то, что схема описывает обработку не одного сообщения, а нескольких из входящей очереди. Потому что для анализа производительности и устойчивости важна не просто обработка одного сообщения — важно, как экземпляр сервиса обрабатывает поток сообщений из очереди, переходя из одного к другому и откладывая незавершенную обработку.
На самом деле под любыми способами взаимодействия (в том числе синхронным) лежит посылка сообщения. Которое будет получено некоторым обработчиком и обработано сразу с отправкой сообщения-ответа. Либо поставлено в очередь на обработку с квитанцией о приеме, а содержательная обработка произойдет позднее. Однако это не означает, что все способы одинаковы — обработка сообщений и очереди организуются по-разному. Конечно, детали реализации часто скрыты протоколом обмена, библиотеками или синтаксическим сахаром языка реализации, однако для разработки эффективных и устойчивых приложений нам необходимо понимать, как именно организована обработка (включая обработку ошибок), где возникают очереди, задержки и как происходит обработка при большом потоке сообщений.
Синхронное взаимодействие
Синхронное взаимодействие — самое простое. Оно скрывает все детали удаленного вызова, что для вызывающего сервиса превращается в обычный вызов функции с получением ответа. Для его организации есть множество протоколов — например, давно известные RPC и SOAP. Но очевидная проблема синхронности в том, что удаленный сервис может отвечать не очень быстро даже при простой операции — на время ответа влияет загруженность сетевой инфраструктуры, а также другие факторы. И все это время вызывающий сервис находится в режиме ожидания, блокируя память и другие ресурсы (хотя и не потребляя процессор). В свою очередь, блокированные ресурсы могут останавливать работу других экземпляров сервиса по обработке сообщений, замедляя тем самым уже весь поток обработки. А если в момент обращения к внешнему сервису у нас есть незавершенная транзакция в базе данных, которая держит блокировки в БД, мы можем получить каскадное распространение блокировок.
Например, при распиле монолита мы выносим сервис хранения товаров отдельно. И в бизнес-логике обработки заказа (где-то в середине) нам надо получить какой-то атрибут для действий в зависимости от него — например, узнать вес и объем товара, чтобы решить: курьер довезет или нужна машина (или даже газель). Если раньше мы обращались за атрибутом локально и быстро получали ответ, то теперь мы используем удаленное обращение — и пока он идет, этот процесс держит не только свои ресурсы, но и блокировки, связанные с незавершенной транзакцией.
Казалось бы, это всё очевидно. Но я встречался со случаями, когда синхронные вызовы ставили без необходимости, искренне не понимая, что выполнение будет долгим, а накладные расходы — большими. Отдельная засада заключается в том, что современные системы разработки позволяют вынести сервисы на удаленный сервер не меняя исходного кода, — и сделать это могут администраторы при конфигурировании системы. В том числе на уровне базы данных — я встречался с идеями, когда централизованное хранение логов на полном серьезе предлагали делать просто за счет переноса локальных таблиц на общий сервис так, чтобы прямые вставки в них превратились в ставки по dblink. Да, это — простое решение. Только очень неустойчивое по производительности и чувствительное к сбоям сетевой инфраструктуры.
Другая проблема связана с падениями удаленных вызовов. Падение вызываемого сервиса еще укладывается в общую логику обработки — всякий вызов процедуры может породить ошибку, но эту ошибку можно перехватить и обработать. Но ситуация становится сложнее, когда вызываемый сервис отработал успешно, но вызывающий за это время был убит в процессе ожидания или не смог корректно обработать результат. Поскольку этот уровень скрыт и не рассматривается разработчиками, то возможны эффекты типа резервов для несуществующих заказов или проведенные клиентом оплаты, о которых интернет-магазин так и не узнал. Думаю, многие сталкивались с подобными ситуациями.
И третья проблема связана с масштабированием. При синхронном взаимодействии один экземпляр вызывающего сервиса вызывает один экземпляр вызываемого, но который, в свою очередь, тоже может вызывать другие сервисы. И нам приходится существенно ограничивать возможность простого масштабирования через увеличение экземпляров запущенных сервисов, при этом мы должны проводить это масштабирование сразу по всей инфраструктуре, поддерживая примерно одинаковое число запущенных сервисов с соответствующей затратой ресурсов, даже если проблема производительности у нас только в одном месте.
Поэтому синхронное взаимодействие между сервисами и системами — зло. Оно ест ресурсы, мешает масштабированию, порождает блокировки и взаимное влияние разных серверов.
Я бы рекомендовал избегать его совсем, но, оказывается, есть одно место, в котором протокол поддерживает только синхронное взаимодействие. А именно — взаимодействие между сервером приложений и базой данных по JDBC синхронно принципиально. И только некоторые NoSQL базы данных поддерживают реально асинхронное взаимодействие со стороны сервера приложений и вызовы callback по результату обработки. Хотя казалось бы, мы находимся в поле бэкенд-разработки, которая в наше время должна быть ориентирована на асинхронное взаимодействие. Но нет — и это печально.
Транзакции и консистентность
Раз уж зашла речь про базы данных, поговорим о транзакционности работы. Там более, что именно она часто является аргументом за синхронное взаимодействие.
Начнем с того, что транзакционность была в свое время громадным преимуществом реляционных баз данных и снимала с разработчиков громадное количество работы по обработке ошибок. Вы могли рассчитывать, что все изменения, которые бизнес-логика выполняет в базе данных при обработке одного запроса пользователя либо будут целиком зафиксированы, либо целиком отменены (если при обработке произошла ошибка), а база данных вернется в прежнее состояние. Это обеспечивал механизм транзакций реляционной базы данных и паттерн UnitOfWork в приложении-клиенте или на сервере приложений.
Как это проявлялось практически? Например, если вы резервировали заказ, и какой-то одной позиции не хватило, то ошибка обработки автоматически снимала все сделанные резервы. Или если вы исполняли сложный документ, — и при этом создавалось много проводок по разным счетам, а также изменялись текущие остатки и история, — то вы могли быть уверены, что либо все проводки будут созданы и остатки будут им соответствовать, либо ни одной проводки не останется. Поведение по умолчанию было комфортным и при этом обеспечивало консистентность, и лишь для сохранения частичных результатов (например, для частичного резерва заказа) надо было предпринимать специальные сознательные усилия. Но и в этом случае на уровне базы данных за счет механизма триггеров все равно можно было следить за консистентностью внутри транзакций при частичном сохранении результатов —, например, обеспечивая жесткое соответствие проводок и остатков по счетам.
Появление трехзвенной архитектуры и сервера приложений принципиально не изменило ситуацию. Если каждый запрос пользователя обрабатывается сервером приложений в одном вызове, то заботу о консистентности вполне можно возложить на транзакции базы данных. Это тоже было типовым шаблоном реализации.
Когда же пришла пора распределенных систем, то это преимущество решили сохранить. Особенно на уровне базы данных — потому что возможность вынести часть хранения на другой сервер средствами администратора представлялась крайне желанной. В результате появились распределенные транзакции и сложный протокол двухфазного завершения, который призван обеспечить консистентность данных в случае распределенного хранения.
Призван, но по факту — не гарантирует. Оказывается, в случае сбоев даже промышленные системы межсистемных транзакций, — такие, как взаимодействие по Oracle dblink, — могут привести к тому, что в одной из систем транзакция будет завершена, а в другой — нет. Конечно, это тот самый «исчезающе маловероятный случай», когда сбой произошел в крайне неудачный момент. Но при большом количестве транзакций это вполне реально.
Это особенно важно, когда вы проектируете систему с требованиями высокой надежности, и рассчитываете использовать базу данных как средство, обеспечивающее резервирование данных при падении сервера, передавая данные на другой сервер и получая, таким образом, копию, на которую рассчитываете оперативно переключиться при падении основного сервера. Так вот, если падение произошло в этот самый «крайне неудачный момент», вы не просто получаете транзакции в неопределенном состоянии, вы должны еще разобраться с ними вручную до запуска штатного режима работы.
Вообще, расчет на штатные средства резервирования базы данных в распределенном IT-ландшафте иногда играет злую шутку. Очень печальная история восстановления после аварии произошла, когда основной сервер одной из систем деградировал по производительности. Причина проблем была совершенно неясна, не исключены были даже проблемы на уровне железа, поэтому решили переключиться на standby — в конце концов, его именно для таких случаев и заводили. Переключились. После этого выяснилось, что потерялись несколько последних минут работы, но в корпоративной системе это не проблема — пользователей оповестили, они работу повторили.
Проблема оказалась в другом: смежные системы, с которыми взаимодействовал сервер, оказались совершенно не готовы к откату его состояния на несколько минут — они же уже обращались и получили успешный ответ. Ситуация усугубилась тем, что об этом просто не подумали при переключении, а проблемы начали проявляться не сразу — поэтому эффект оказался довольно большим, и его пришлось несколько дней искать и устранять сложными скриптами.
Так вот там, где взаимодействие было асинхронным, с этим получилось разобраться скриптами на основе сравнения журналов обоих серверов. А вот для синхронного взаимодействия оказалось, что никаких журналов не существует в природе, и восстановление консистентности данных потребовало сложных межсистемных сверок. Впрочем, для асинхронных взаимодействий при отсутствии журналов будет тоже самое, поэтому ведите их с обеих сторон.
Таким образом, механизмы межсистемных транзакций часто погребены очень глубоко на системном уровне, и это — реальная проблема в случае сбоев. А при временных нарушениях связи проблема станет еще больше из-за больших таймаутов на взаимодействие систем — многие из них разворачивались еще в тот период, когда связь была медленной и ненадежной, а управление ими при этом далеко не всегда вынесено на поверхность.
Поэтому использование транзакций базы данных и встроенных механизмов, в общем случае, не будет гарантией консистентности данных и устойчивости работы комплекса систем. А вот деградацию производительности из-за межсистемных блокировок вы точно получите.
Когда же объектно-ориентированный подход сменил процедурную парадигму разработки, мы получили еще одну проблему. Для поддержки работы с персистентными объектами на уровне сервера приложений были разработаны объектно-реляционные мапперы (ORM). Но тут-то и выяснилось, что шаблон UnitOfWork и возможность отката транзакций концептуально противоречат ORM — каждый объект инкапсулирует и сложность, и логику работы, и собственные данные. Включая активное кэширование этих данных, в том числе — и между сессиями разных пользователей для повышения производительности при работе с общими справочниками. А отката транзакций в памяти на уровне сервера приложений не предусмотрено.
Конечно, на уровне ORM или в надстройке над ним можно вести списки измененных объектов, сбрасывать их при завершении UnitOfWork, а для отката — считывать состояние из базы данных. Но это возможно, когда вся работа идет только через ORM, а внутри базы данных нет никакой собственной бизнес-логики, изменяющей данные, — например, триггеров.
Может возникнуть вопрос — а какое все это имеет отношение к интеграции, это же проблемы разработки приложения как такового? Это, было бы так, если бы многие legacy-системы не выставляли API интеграции именно на уровне базы данных и не реализовывали логику на этом же уровне. А это уже имеет прямое отношение к интеграции в распределенном IT-ландшафте.
Замечу, что взаимодействие между базами данных тоже не обязательно должно быть синхронным. Тот же Oracle имеет различные библиотеки, которые позволяют организовывать асинхронное взаимодействие между узлами распределенной базы данных. И появились они очень давно — мы успешно использовали асинхронное взаимодействие в распределенной АБС Банка еще в 1997 году, даже при скорости канала между городами, по которому шло взаимодействие, всего 64К на всех пользователей интернета (а не только нашей системы).
Асинхронное и реактивное взаимодействие
Асинхронное взаимодействие предполагает, что вы посылаете сообщение, которое будет обработано когда-нибудь позднее. И тут возникают вопросы — а как получать ответ об обработке? И нужно ли вообще его получать? Потому что некоторые системы оставляют это пользователям, предлагая периодически обновлять таблицы документов в интерфейсе, чтобы узнать статус. Но достаточно часто статус все-таки нужен для того, чтобы продолжить обработку — например, после полного завершения резервирования заказа на складе передать его на оплату.
Для получения ответа есть два основных способа:
обычное асинхронное взаимодействие, когда передающая система сама периодически опрашивает состояние документа;
и реактивное, при котором принимающая система вызывает callback или отправляет сообщение о результате обработки заданному в исходном запросе адресату.
Оба способа вы можете увидеть на схеме вместе с очередями и логикой обработки, которая при этом возникает.
Какой именно способ использовать — зависит от способа связи. Когда канал однонаправленный, как при обычном клиент-серверном взаимодействии или http-протоколе, то клиент может запросить сервер, а вот сервер не может обратиться к клиенту — взаимодействие получается асинхронным.
Впрочем, такой асинхронный способ легко превращается в синхронный — достаточно в методе отправки сообщения поставить таймер с опросом результата. Сложнее превратить его в реактивный, когда внутри метода отправки находится опрос результата и вызов callback. И вот это второе превращение — далеко не столь безобидно, как первое, потому что использующие реактивную интеграцию рассчитывают на ее достоинства: пока ответа нет, мы не тратим ресурсы и ждем реакции. А оказывается, что где-то внутри все равно работает процесс опроса по таймеру…
Реактивное взаимодействие требует определенной перестройки мышления, которая не столь проста, как кажется, потому что есть желание не просто упростить запись, а скрыть реактивное программирование и писать в традиционном стиле. Впервые я это осознал, когда был в 2014 году на конференции GoToCon в Копенгагене (мой отчет) и там же услышал про Реактивный манифест (The Reactive Manifesto). Там как раз обсуждалось создание различных библиотек, поддерживающих эту парадигму взаимодействия, потому что она позволяет гибко работать с производительностью. Сейчас это встроено в ряд языков через конструкции async/await, а не просто в библиотеки.
Но фишка в том, что такое скрытие усложняет понимание происходящего внутри приложения. Засада происходит в том случае, когда к объектам, обрабатываемым в таком асинхронном коде, обращаются из других мест — например, они могут быть возвращены в виде коллекций, запрашивающих объекты. И если не позаботиться специально, то вполне могут быть ситуации одновременного изменения объектов. Вернее, псевдо-одновременного — между двумя асинхронными вызовами объект изменяется, хотя с точки зрения разработчика мы как бы находимся внутри потока управления одной процедуры.
Впрочем, шаблоны реактивного программирования — это отдельная тема. Я же хочу заострить внимание на том, что в реактивном взаимодействии есть не только переключение потоков, но и скрытые очереди. А скрытые очереди хуже явных, потому что когда возникает дефицит ресурсов и возрастает нагрузка, все тайное становится явным. Это, конечно, не повод усложнять запись, но, как и в случае с автоматическим управлением памятью, эти механизмы надо понимать и учитывать. Особенно в интеграции, когда это используется для взаимодействия между узлами и сервисами, которые потенциально находятся на разных узлах.
Поэтому я рекомендую представить вашу конкретную интеграцию со всеми скрытыми очередями на схеме как в начале статьи,. И с ее помощью продумать, что произойдет, если один из сервисов упадет в середине обработки? Сделать это нужно как минимум трем людям:
Проектирующий систему аналитик, должен понимать, какие будут последствия для пользователей, и как именно с ними разбираться — что нужно автоматизировать, а что может решить служба поддержки.
При этом аналитик плотно взаимодействует с разработчиком, который смотря на соответствие этой схемы и фактической реализации, может указать на разные проблемы и предложить их решение.
А третий человек — тестировщик. Он должен придумать, как проверить, что в случае сбоев и падений отдельных сервисов система ведет себя именно так, как задумано — что не возникает документов в промежуточных состояниях и которые не видны ни на интерфейсах ни службе поддержки; что отсутствует случайная двойная обработка документа и так далее.
Поясню эти задачи на примерах. Пусть один сервис обрабатывает заказы от покупателей, а другой — резервирует товары по этим заказам на остатках. Задача в этом случае — несмотря на падения серверов или сбои связи, не должно быть ситуации, когда по заказу на 3 единицы они зарезервировались дважды, то есть заблокировалось 6 единиц, потому что первый раз сервис резервирования его выполнил, а квитанцию о резерве не послал или сервис обработки заказов не смог эту квитанцию обработать.
Другой пример — в дата-центре установлен сервис отправки чеков в ФНС, которая выполняется через взаимодействие со специализированным оборудованием — ККМ. И тут тоже надо обеспечить, чтобы каждый чек был отправлен в налоговую ровно один раз, при том, что сама ККМ может работать ненадежно и со сбоями. А в тех случаях, когда алгоритм не может однозначно выяснить результат обработки чека, о появлении такой ситуации должна быть оповещена служба поддержки для разбора. Это должны спроектировать аналитики с разработчиками, а тестировщики — проверить при различных сбойных ситуациях. И лучше это делать автоматически, особенно если в интеграции есть сложные алгоритмы обработки.
Конечно, никакая проверка не дает гарантий. Поэтому при межсистемном взаимодействии важно вести журналы с обеих сторон (хотя при большом объеме приходится думать, как чистить или архивировать логи). Казалось бы, это очевидно. Но нет — я встречал много систем, где журналы отсутствовали и диагностировать проблемные ситуации было невозможно. И много раз доказывал при проектировании, что журналирование обмена – необходимо.
Консистентность данных
Вернемся к вопросам консистентности данных при асинхронном взаимодействии. Как его обеспечивать, если транзакции недоступны? В прошлой статье я предложил одно из решений — шаблон идемпотентных операций. Он позволяет в случае проблем с получением ответа в обработке сообщения просто отправить его повторно, и оно при этом будет обработано корректно. Этот же прием можно применять в ряде ситуативных технических ошибок, например, при отказе из-за блокировок на сервере.
Если же вы получили содержательную ошибку, — например, отказ в отгрузке уже зарезервированного и оплаченного заказа, — то, скорее всего, с такими проблемами надо разбираться на бизнес-уровне, никакие транзакции тут не помогут. В самом деле, отказ отгрузки может в том числе означать, что зарезервированный товар присутствовал только в остатках информационной системы, а на реальном складе его не было. То есть, его действительно не получится отправить покупателю, — а значит, разбираться с этим надо на уровне бизнес-процесса, а не технически. По моему опыту, подобные ситуации достаточно распространены — многие разработчики почему-то забывают, что ИТ-системы лишь отражают реальный мир (и отражение может быть неверным) и сосредотачиваются на техническом уровне.
Организация транзакций
Обработка сообщений порождает транзакции, так как системы по обе стороны взаимодействия работают с базами данных. Но при асинхронном взаимодействии нет никакого смысла сохранять ту транзакционность обработки, какая была в исходной системе. Это опять кажется очевидным, но при обсуждении базовых механизмов интеграции эту задачу почему-то часто ставят и тратят силы не ее решение. Это — лишняя работа.
В основе транзакционной работы лежит необходимость персистентного сохранения в базу данных, при этом транзакции и кванты обработки стоит настраивать отдельно, исходя из требований производительности. По моему опыту, если у вас идет массовая загрузка или передача данных, то вовсе не обязательно оптимальным будет commit на каждую запись — всегда есть некоторое оптимальное количество. При этом внутри процедуры загрузки можно ставить savepoint с перехватом ошибок для каждой отдельной записи, чтобы откатывать не всю порцию загрузки, а лишь обработку ошибочных. Только помните, что откатываются только изменения в таблицах, а некоторые другие вещи могут не откатываться, — например, работа с переменными пакетов в Oracle или некоторые способы постановки в очереди или работа с файлами. Но это и при обычном rollback сохраняется.
Еще надо понимать, что при передаче документов со строками (например, больших накладных) принимающая система далеко не всегда работает корректно, если документ был обработан частично (даже если у строк есть отдельный атрибут-статус). При этом, что интересно, передающая система, наоборот, может не обеспечивать целостность обработки документов. И тогда надо принимать специальные меры для обеспечения согласованности.
У меня было два подобных кейса. В одном случае требовалось в принимающей системе снять ограничение, связанное с долгим проведением больших документов, потому что некоторые накладные включали тысячи позиций и обычный режим исполнения (целиком) приводил к долгим блокировкам. В этом случае для построчной обработки мы создали отдельную технологическую таблицу со статусами строк и очень аккуратно работали со статусом самого документа.
В другом случае складская система обрабатывала документы отгрузки наоборот, построчно, выдавая общий поток ответов по всем строкам всех входящих документов. Тогда как передающая система рассчитывала, что на каждый переданный документ придет целостный ответ. В этом случае нам пришлось создать специальный промежуточный сервис, который на входе запоминал, сколько строк имеет каждый передаваемый документ, фиксировал полученные ответы и передавал их обратно в отправляющую систему только когда по всем строкам получал ответ. Он также поднимал тревогу, когда ответ по некоторым строкам слишком долго задерживался. Эвристики для этого алгоритма мы прорабатывали отдельно, так как за формированием ответа стояла реальная обработка позиций документа людьми, которая, естественно, была далеко не мгновенной.
На этом я завершаю третью статью про интеграцию. Надеюсь, она помогла вам разобраться в различных вариантах взаимодействия систем, и это окажется полезным при проектировании, разработке и тестировании.
С 1 февраля стоимость очного участия в DevOpsConf 2021 составит 36000 рублей. Забронируйте билет сейчас, и у вас будет ещё несколько дней на оплату.
На данный момент Программный комитет одобрил уже около 40 докладов, но до 28 февраля ещё принимает заявки. Если вы хотите быть спикером, то подать доклад можно здесь.
Чтобы быть на связи, подписывайтесь на наши соцсети, чтобы не упустить важные новости о конференции и наших онлайн-событиях — VK, FB, Twitter, Telegram-канал, Telegram-чат.