Онлайн-чеки по федеральной сети посредством RabbitMQ, 1С и черной магии
В прошлом году к нам обратился ИТ-директор одного из крупнейших аграрно-промышленных холдингов в России. Подход к бизнесу, который реализовал наш клиент, был впечатляющим. Он одним из первых реализовал идею предприятия полного цикла — от поля до полки в продуктовом магазине. Благодаря доступности и высокому качеству продукции этот холдинг стал признанным брендом, который знают и выбирают. В тот момент в холдинг входило более 650 торговых точек и более 20 000 сотрудников, распределенных по всей территории РФ.
Заказчику требовалось обеспечить максимально быструю доставку чеков до центра со всех торговых точек России, включая продуктовые ларьки в глухих селах с эпизодическим Интернетом и минимальной компьютеризацией.
С учетом указанной специфики решение задачи превратилось в увлекательное приключение с бубном, шаманами и кроличьими лапками в лице RabbitMQ. Как мы строили федеративный кластер очередей и с чем столкнулись — под катом.
О проблематике заказчика
Холдинг с большим количеством распределенных торговых точек, работающий в условиях жесткой конкуренции, требовал от своих руководителей оперативного принятия управленческих решений. Для этого, руководителям требовалась информация по чекам в режиме «реального времени».
В действующей системе время доставки чека от кассы до центральной системы доходило до 3-х суток. При этом периодически фиксировалась не доведение информации о совершенной покупке (потеря чека).
Требовалось обеспечить получение информации о продажах «здесь и сейчас» для оптимизации доставки продукции в торговые точки и оперативного реагирования на изменение остатков товара и спроса. Эта информация должна одновременно попадать в центральную ИС холдинга на базе 1С и на компьютер товароведа в конкретном магазине/торговой точке.
Кроме того, требовалось обеспечить централизованную и оперативную переоценку товаров по сети, а также, подключить к BI-системе холдинга поток онлайн-данных со всех торговых точек.
В итоге, были сформированы критерии успешности проекта:
- чеки, пробитые на кассе магазина, должны быть видны в центральной системе 1С не более, чем через 1 секунду от момента его «фискализации» на кассе при наличии (исправной работе) связи;
- при временных сбоях связи обеспечить оперативную гарантированную доставку чеков в центральную систему после восстановления канала связи.
Для команды «Серебряной Пули» это была очень заманчивая штука, поскольку мы на тот момент уже разработали наш адаптер 1С для RabbitMQ и возможность запустить его в бой на больших расстояниях и нагрузках манила гиковские умы. Сама концепция «позиция чека в центре за 1 секунду» не так уж нова, мы еще в 2013 году презентовали идеи применения очередей в 1С, и даже аппробировали ее на одной торговой сети, но на тот момент это были сильно экспериментальные костыли, включавшие помимо Rabbit+1С еще C#, WCF и даже немного С++.
Разумеется, мы это все подсмотрели в умных книжках, которые написались еще ранее. Поэтому, не возьмусь судить, когда именно идея очередей в интеграционных проектах начала захватывать мир.
Так или иначе, теоретическая часть и архитектурный концепт свою эффективность доказали, оставалось самая малость — все сделать, протестировать, пофиксить баги, задокументировать и задеплоить. : trollface:
Ныряем с головой
Никто лучше владельца бизнеса не знает о его сильных и слабых сторонах, поэтому все интеграционные проекты начинаются с аудита процессов клиента, понимания отправной точки (AS IS). Обычно, обследование бизнеса включает в себя:
- анализ информации о том, как бизнес-процессы протекают сейчас;
- сбор требований к системе путем опроса ключевых сотрудников с целью понять, как, по их мнению, должны быть устроены бизнес-процессы;
- формализацию и оптимизацию бизнес-процессов.
Чтобы исключить разночтения мы используем формализацию бизнес-процессов AS IS и TO BE в виде схем BPMN. Так и самим разбираться легче, и заказчику проще ничего не упустить, когда совместно с ним проходим по схеме процессов.
Из технических особенностей, в процессе аудита выяснили, что:
- торговые точки используют Windows-машины — с установленными разнородными операционными системами (в зависимости от года создания) от Windows XP до Windows 7, 8, 10;
- кассовые аппараты работают под управлением встроенного ПО «Frontol» с Windows Embedded на борту;
- контур управления розничной торговлей использует разнородное ПО, включающее как решения от вендоров 1С, так и собственные разработки;
- каналы связи реализованы по принципу «что есть, то есть» в диапазоне от кабельных линий до USB модемов-свистков;
- розничные торговые точки обслуживаются местными компаниями-аутсорсерами, а не централизованной службой холдинга;
- функционирование системы существенным образом зависит от исправности (надлежащего функционирования) компьютера товароведа на каждой торговой точке.
Схема интеграции AS-IS
Аудит также показал, что бизнес-процессы компании практически не нуждаются в корректировке, но требуется доработка инфраструктуры, обеспечивающая бесперебойное прохождение информации.
В общем и целом, перед нами стояла задача надежной доставки документов между тремя компонентами процесса: кассовым аппаратом, компьютером товароведа и центральным офисом. При этом, компьютер товароведа — это машина «под столом». Она может включаться/выключаться по желанию товароведа. Или вообще быть выключенной 23 часа из 24-х. Однако, при выходе из сумрака товаровед должен видеть актуальный набор цен, остатков, номенклатурных позиций и т.п.
Выбор решения, сбор граблей и расстановка костылей
Событийная интеграция на очередях уже давно стала общеупотребимым паттерном. Когда вам нужно передавать что-то куда-то, со многими звеньями, в ненадежной среде, и при этом потоки данных маршрутизировать — вам нужны события и очереди. Поэтому, мы выбрали RabbitMQ, поскольку он легко интегрируется в любую (нам так казалось) среду, включая платформу 1С, для которой у нас уже был собственноручно сделанный адаптер AMQP-протокола.
RMQ является своего рода менеджером потоков данных и позволяет обеспечить интеграцию в режиме «почти реального» времени, сохраняя при этом слабую связанность систем, выдерживая нагрузки, и прочее и прочее… Хороший сервер, одним словом, на Хабре про него написано немало.
Одной из приятных особенностей является кластеризация «из коробки» и возможность построения распределенных кластеров серверов, работающих совместно.
Мне всегда нравились картинки с архитектурой интеграции на очередях. Они всегда состоят из трех кубиков, в центре которых — брокер сообщений. Пусть такая картинка будет и в этой статье, дабы не нарушать канон.
При построении схемы встал вопрос –, а где должен располагаться сервер очередей? В каких состояниях у нас может оказываться система? Выяснили, что существует 5 штатно-нештатных ситуаций при которых работа не должна останавливаться.
- Все блоки включены.
- Отключен центр, есть доступ к товароведу.
- Отключен товаровед, есть доступ к центру.
- Отключены и товаровед и центр.
- Отключена система 1С.
Чеки во всех этих ситуациях должны пробиваться, торговля не должна останавливаться. При восстановлении канала — чеки должны прибывать подписчику. Напомню, что торговая точка — это может быть и сельский ларек. Там не установлен отдельный сервер, на который можно было бы поставить RMQ. Получалось, что брокер сообщений должен стоять непосредственно на кассе. Сервера — это непозволительная роскошь, а Rabbit достаточно легковесный и вполне может работать на маленьком POS-терминале. Так почему бы и не да?
Разумеется, мы не стали делать POS единственной нодой кластера RMQ, но один из узлов федеративного кластера разместили прямо на торговом терминале, под управлением Windows Embedded. Сказать это было несколько легче чем сделать, но мы весело и азартно справились. О чем сейчас и расскажу.
Как поставить RMQ терминал Frontol
Надо сказать, что Erlang и сам сервер RMQ на винду терминала встал практически без проблем. Проблемы возникли в клиенте, который должен взаимодействовать с сервером из кассового ПО.
Кассовое ПО Frontol имеет достаточно годную документацию, из которой мы выяснили, что имеется возможность кастомизировать поведение с помощью Javascript. «Йухху!» — сказали мы и принялись гуглить JS-клиент для RabbitMQ. Довольно быстро выяснилось, что нас ждет облом. На фронтоле не совсем Javascript. Ну т.е. формально да, синтаксис там такой же, но сама машина JavaScript там от Windows Script Host, того самого, который VBScript, cscript.exe и прочее. Если кратко, то он очень старый, микрософто-специфичный, и ни один вменяемый JS-клиент кролика на нем не заработает.
Однако, в экосистеме WSH можно использовать COM-объекты, и мы направились в сторону клиента RMQ для .NET.
Современные версии этого клиента уже не поддерживают .NET 3.5, который имелся в нашем распоряжении на POS-терминале, но к счастью, исходники клиента открыты, а кроме того, на гитхабе проекта сохранились теги, в которых .NET 3.5 все еще поддерживался. Слава Опенсорсу! Оставалось выкачать исходники старой версии .NET клиента, поставить там галочку Com-Visible и задеплоить на терминал.
Взаимодействие с кассой Frontol
Кассовое ПО имеет API, который можно использовать для взаимодействия.
function init() {
frontol.addEventListener("openDocument", "beforeOpenDocument", true);
frontol.addEventListener("closeDocument", "beforeCloseDocument", true);
frontol.addEventListener("closeDocument", "afterCloseDocument", false);
frontol.addEventListener("closeSession", "beforeCloseSession", true);
frontol.addEventListener("closeSession", "afterCloseSession", false);
addPolyfills(); // полифиллы рулят )
initRmqVariables();
createRMQConnection();
}
Возможности родного JS, который есть на борту Windows — крайне бедны. Например, там нет Array.indexOf или JSON.stringify. Но мир ведь не без добрых людей. Мы вспомнили популярный браузерный костыль по имени «полифиллы», и радостно встроили их в кассу. Если отбросить шутки в сторону, то все магические трюки с JS намеренно дотошно комментировались, чтобы поколения будущих админов могли четко и быстро понять — что происходит, откуда взялось и как работает.
Довольно быстро выяснилось, что JS-API не покрывает часть наших кейсов, однако, поскольку Frontol имеет в своем составе СУБД Firebird, а к ней существует ODBC-провайдер, то с помощью того же самого ADODB мы сможем из Javascript обращаться сразу в базу кассы и доставать там нужные нам данные.
function afterCloseSession() {
var connection = getDatabaseConnection();
var qSelect = new ActiveXObject("ADODB.Command");
qSelect.ActiveConnection = connection;
qSelect.CommandText =
"SELECT ChequeNumber " +
"FROM Document " +
"WHERE " +
" State = 1 " +
" AND(ChequeType IN(0, 1, 2)) "
Ну и наконец, непосредственная работа с сервером очередей из нашего JS выглядит следующим образом:
function createRMQConnection() {
factory = new ActiveXObject("RabbitMQ.Client.ConnectionFactory");
factory.UserName = rmqUser;
factory.Password = rmqPass;
factory.VirtualHost = "/";
factory.HostName = "localhost";
try {
rmqConnection = factory.CreateConnection(0);
} catch (e) {
throw new Error("Ошибка подключения к локальному серверу очередей. Обратитесь к администратору!\n" + e.message);
}
rmqChannel = rmqConnection.CreateModel();
rmqMessageProperties = rmqChannel.CreateBasicProperties();
rmqMessageProperties.ContentType = "text/plain";
rmqMessageProperties.ContentEncoding = "string";
rmqMessageProperties.DeliveryMode = 2;
}
Общая картина решения
В архитектуру заложили следующие принципы:
- федерация RabbitMQ серверов — режим серверов RMQ — Federation, чтобы события были доставляемы всем получателям;
- локальная выживаемость кассы — если событие произошло на кассе, то оно должно быть доставлено в любом случае, даже если в настоящий момент кассовое рабочее место недоступно по сети;
- 2 доставщика данных — в обычном режиме данные доставляется через сервер (компьютер товароведа), в случае недоступности (в результате аварии или по другим причинам) компьютера товароведа — доставку осуществляет сама касса, когда компьютер товароведа будет включен он получит свою порцию событий позже центра, но гарантированно;
- RMQ — служба RabbitMQ сервер с отрытыми TCP портами для кластеризации и обмена сообщениями;
- для обеспечения гарантированности доставки сообщений применено резервирование потоков данных. Таким образом обеспечивается минимизация влияния сетевого соединения между узлами системы;
- в 1C: Центр было решено установить комплект серверов RMQ в режиме НА — высокая доступность центрального сервера, в т.ч. есть двойное резервирование и репликация событий, для гарантии доставки.
В соответствии с архитектурным решением создана следующая схема потоков данных:
- Frontol, 1C — начальная и конечная точки обмена — объекты являющиеся источниками и получателями сообщений;
- федеративная очередь — специальный тип очереди, позволяющий создать распределенную систему для передачи сообщений, в которой публикация в очередь может выполняться в узле, расположенном на торговой точке (upstream), а их получение из очереди в центре (downstream). За передачу сообщений от upstream к downstream отвечает специальный плагин (Federation PlugIn), устанавливаемый на центральном сервере очередей;
- Shovel — (буквально «лопата») — механизм передачи сообщений из одного объекта (очереди) в другой. Объекты могут принадлежать как одному серверу, так и разным;
- архитектура системы предусматривает дистанционное управление развёртыванием, то есть возможность установки серверов RabbitMQ на любой компьютер сети холдинга из единого центра;
- настройка и соединения в федерацию проходится с помощью так называемых Post-Install скриптов, которые штатно идут с поставкой RMQ серверов, с необходимой настройкой сетевых параметров, принятых в сети Заказчика.
Полученная в результате схема обеспечивает заказанные характеристики по скорости и стабильно работает при выпадании любого из компонентов. После пропадания и восстановления связи накопленные данные уходят в центр в течении 5–10 секунд. На практике доказала свою эффективность технология слабо связанных систем. Все события происходят как будто в одном офисе, без учёта различного вида задержек, связанных с территориальной распределённостью и свойственной ей различной степенью доступности каналов связи.
Краткое заключение
Хочется отдельно порадоваться тому, в какое удивительное время мы живем. Еще недавно применение Open Source в продуктиве было этакого рода подвижничеством. «У вас открытое ПО работает? Ну и как вам по вкусу этот кактус?» Сегодня же, это абсолютная максима.
Я не могу себе представить компанию, в которой бы не было в продуктиве продуктов с открытым исходным кодом. Благодаря доступности информации, открытым исходникам, реализовывать бизнес-идеи стало намного проще и быстрее. Нужно поставить RMQ на древний нестандартный Javascript и Windows? Нет ничего проще. Нагугленное решение немного не подходит? — посмотри, как сделано и добавь недостающее. Time-to-Market при применении открытых продуктов сокращается в разы. А все знают, что быстрый выпуск решения означает конкурентное преимущество.
Github, Stackoverflow, открытые документации и стандарты позволяют за считанные недели запустить то, что раньше бы потребовало многолетней экспертизы в самых разных областях компьютерного знания.
Ну и конечно, отдельно радует, что сообщество 1С-ников с каждым годом все дальше выходит из своего закрытого мирка и вливается в глобальный IT. Например,»1C: Enterprise» на сегодня — это один из официальных языков, поддерживаемых Github, и «научили» его этому языку люди из сообщества 1С. Эта история, наверное, заслуживает отдельной статьи, возможно, я когда-нибудь ее напишу. А пока — всего вам доброго и удачи!
Спасибо за уделенное время!