Master-master репликация и масштабирование приложений между всеми IoT-устройствами и облаком

МЕНЮ


Искусственный интеллект
Поиск
Регистрация на сайте
Помощь проекту

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


На фото представлены устройства, использованные для прототипирования. Как видно, за основу взята платформа Intel Edison, так как она поддерживает многие архитектуры, в том числе MIPS и ARM.

Всем привет. В этой статье я хотел бы поделиться опытом решения одной интересной проблемы, связанной с синхронизацией данных между IoT-устройствами и облачным приложением. Сначала я расскажу об основной идее и целях моего проекта, а затем подробно опишу его техническую сторону и реализацию: речь пойдет об ОС Contiki, базах данных, протоколах и подобных аспектах. В заключение я кратко перечислю технологии, использованные при построении системы.

Вкратце о проекте

Для начала давайте поговорим об основной идее проекта. Ниже схематично изображен принцип работы готовой системы: Есть пользователь, который через облачный сервис или напрямую (по Wi-Fi) подключается к IoT- устройству. Также где-то в Интернете имеется облачный сервер приложения. Облаком может служить что угодно: скажем, инстанс AWS или Azure или выделенный сервер. Для обмена данными между сервером приложения и IoT-устройствами устанавливается соединение по какому-то протоколу. IoT-устройства каким-то образом соединены друг с другом (например, по Ethernet или Wi-Fi). Помимо этого, есть отдельная группа IoT-устройств, генерирующих телеметрические данные (такие как показатели освещенности или температура).

В общей сложности, может набраться больше 100 или даже больше 1000 устройств. Моя основная задача заключалась в том, чтобы обеспечить обмен данными между облаком и этими IoT-устройствами. Прежде чем двигаться дальше, стоит упомянуть, какие требования предъявлялись к системе:

  • Она должна синхронизировать данные между IoT-устройствами.
  • Она должна собирать данные с IoT-устройств.
  • Она должна синхронизировать данные между IoT-устройствами и облаком.

Техническая реализация


Здесь все довольно просто: пользователь подключается к серверу приложения по HTTP(S), WebSocket или подобному протоколу. Небольшая задачка для читателей: как вы думаете, что можно использовать для соединения между сервером приложения и IoT-устройством?

Если вы подумали про MQTT, вы однозначно правы! Равно как и те, кто выбрал HTTP(S). На самом деле подойдет любой протокол — выбирайте на свой вкус! Мой же выбор пал на — барабанная дробь — асинхронную репликацию! Я имею в виду обычную для баз данных репликацию.

Вы можете спросить, зачем мне репликация. Ответ прост: репликация используется для синхронизации данных, поэтому я могу повсюду — включая облако и IoT-устройства — поддерживать одну версию базы данных. Однако репликацию довольно сложно реализовать. Хочешь репликацию — заведи базу данных, которая ее поддерживает, потому что — повторюсь — репликация естественно присуща базам данных.

Здесь я бы хотел сказать пару слов о тех базах данных, которые я рассматривал при работе над проектом: SQLite, Redis, MySQL, PostgreSQL и Tarantool.

Я сравнил их характеристики и попробовал запустить несколько штук — за исключением MySQL и PostgreSQL — прямо на IoT-устройстве. Ниже расскажу, что из этого вышло.

SQLite — однозначно хорошее решение для хранения данных непосредственно на IoT-устройстве, но у нее нет репликации, и она не поддерживает параллельный доступ из разных процессов.
Redis не поддерживает master-master репликацию и поэтому не может решить мою проблему, так как мне необходима двусторонняя репликация.

MySQL и PostgreSQL слишком тяжеловесны для IoT-устройства, так что я даже не пытался их устанавливать. Но если вы все-таки решите это сделать, смело делитесь своим опытом в комментариях.

Последней в моем списке шла база данных Tarantool. Сразу скажу, что я являюсь коммитером в проект Tarantool, поэтому хорошо знаю сам проект и людей, которые его разрабатывают. К тому же, в Tarantool есть master-master репликация. В общем, для меня это был определенно лучший вариант. Вы же можете использовать в своем проекте другую базу данных. Основная идея, которую я пытаюсь донести, в том, что IoT-устройства могут использовать базы данных с master-master репликацией для обмена данными.

До настоящего момента я лишь поверхностно знакомил вас с проектом. Теперь давайте немного погрузимся в его технические аспекты.

Начну с проблем, с которыми я столкнулся при использовании Tarantool. Во-первых, Tarantool не запускалась на архитектуре ARMv7. Во-вторых, Tarantool не запускалась в 32-битном окружении, что только усугубляло ситуацию. В итоге я смог решить эти проблемы. Ниже приведу правила разработки, которые мне в этом помогли.

  1. Используйте toolchain-файлы для CMake. В противном случае вы, так же как и я, потратите много времени на исправление CMake-файлов.
  2. Не используйте беззнаковый тип и другие типы, для которых не указан размер. В libc для этого есть специальные типы, такие как uint32_t. Иначе можно получить неопределенное поведение. Это правило применимо только к C/C++.
  3. Портируйте ваши автотесты.

Ожидается, что ваши автотесты можно запустить на IoT-устройстве. Если это не так, есть риск убить много времени на отладку.

Итак, у меня есть работающая база данных с master-master репликацией. Замечательно! Следующий шаг — соединить устройства, на которых эта база данных установлена, по 6LoWPAN. Напомню, у меня есть сеть из множества IoT-устройств, соединенных друг с другом по 6LoWPAN, с которых мне необходимо собрать все телеметрические данные.


Краткая схема работы готовой системы

Устройства с сенсорами передают телеметрические данные посредством радиоволн. Этот стандарт называется 6LoWPAN (IPv6 поверх маломощных беспроводных персональных сетей). Замечу, что я не использовал в проекте LoRaWAN. Возможно, я найду применение этой технологии в будущем, но в этой статье я сосредоточусь на 6LoWPAN. Итак, для сбора телеметрических данных я буду использовать шлюз, являющийся важной частью системы. Шлюз — это MIPS-устройство (MIPS — это семейство процессоров) с WAN-антенной для сбора данных, передаваемых посредством радиоволн. Кроме этого, на шлюзе установлено приложение 6LBR, конвертирующее полученные данные в IPv6-пакеты.

Приложение 6LBR


Изображение выше иллюстрирует принцип работы 6LBR. Шлюз с установленным на него 6LBR служит конвертером между беспроводной сенсорной сетью и любой другой. На картинке изображена конвертация из беспроводной сенсорной сети в IP-сеть лишь потому, что так 6LBR работает по умолчанию. Немного позже я объясню, как изменить это поведение.

Более подробную информацию можно найти на странице 6LBR на GitHub.

Вы можете спросить, что же мне дает использование 6LBR. Во-первых, я получаю стек IP, так что я могу использовать функционал стеков TCP и UDP в моих приложениях 6LBR. Во-вторых, я могу использовать любое устройство ввода-вывода с 6LBR. Скажем, можно записать сырые данные прямо в bash. =) К сожалению, 6LBR не пишет напрямую в MQTT. MQTT-брокеры ничего не знают о сырых данных, и с этим приходится мириться.

Зачем же мне понадобилась прямая запись в MQTT-брокер? Ответ прост: дело в legacy-коде.
Здесь я бы хотел сказать пару слов о приложениях 6LBR. В общем случае приложение 6LBR — это написанный на С код с API, позволяющим использовать стек IP и делать некоторые другие вещи. Разработка такого приложения сопряжена как минимум с двумя трудностями: сложная модель потоков и сложная модель памяти. Поэтому запаситесь терпением и приготовьтесь к частым аварийным завершениям вашей программы. Ниже приведен небольшой кусок разработанного мной приложения 6LBR (заранее прошу прощения: могу выложить только картинку с нарочно запутанным кодом, потому что исходники закрыты):

Обратите внимание на одну интересную вещь — PROCESS_YIELD(). В 6LBR есть кооперативная многозадачность, а это значит, что приложения 6LBR должны возвращать управление в каждой итерации цикла. Код не должен выполняться слишком долго.

Итак, давайте еще раз посмотрим, на какой стадии находится наш проект. С помощью шлюза и установленного на него приложения 6LBR я создал mesh network для чтения и записи данных внутри нее. Мне также удалось обернуть IP-пакеты в MQTT-сообщения, каждое из которых содержит информацию об устройстве, включая телеметрические данные. Кроме того, у меня появилась возможность манипулировать устройствами ввода-вывода: скажем, я могу записывать MQTT-сообщения на UART. Но затем я столкнулся с новой проблемой: Tarantool не работает с MQTT-брокерами. Ниже расскажу, как мне удалось обойти это ограничение.

Я решил использовать libmosquitto, написанную на чистом С MQTT-библиотеку, потому что она позволяет довольно просто интегрировать MQTT в мое приложение. Ниже приведен пример использования этой библиотеки для работы с MQTT-сообщениями (ссылка):

static int mosq_poll_one_ctx(mosq_t *ctx, int revents, size_t timeout, int max_packets) { 	/** XXX 	 * I'm confused: socket < 0 means MOSQ_ERR_NO_CONN 	 */ 	int rc = MOSQ_ERR_NO_CONN;  	int fd = mosquitto_socket(ctx->mosq);  	if (fd >= 0) {  		/** Wait until event 		 */ 		revents = coio_wait(fd, revents, timeout);  		if (revents != 0) { 			if (revents & COIO_READ) 				rc = mosquitto_loop_read(ctx->mosq, max_packets); 			if (revents & COIO_WRITE) 				rc = mosquitto_loop_write(ctx->mosq, max_packets); 		}  		/** 		 * mosquitto_loop_miss 		 * This function deals with handling PINGs and checking 		 * whether messages need to be retried, 		 * so should be called fairly _frequently_(!). 		 * */ 		if (ctx->next_misc_timeout < fiber_time64()) { 			rc = mosquitto_loop_misc(ctx->mosq); 			ctx->next_misc_timeout = fiber_time64() + 1200; 		} 	}      return rc; } 

Я могу взять ссылку на дескриптор сокета и использовать собственный событийный цикл для обработки некоторых событий. И это здорово! Хотел бы обратить ваше внимание на то, что в Tarantool, так же как и в 6LBR, есть кооперативная многозадачность. Для возвращения управления Tarantool использует coio_wait().

Ах да, забыл упомянуть, что Tarantool — это еще и сервер приложений на языке Lua. Сюрприз! Поэтому я портировал libmosquitto на Lua. Ниже привожу кусок кода, в котором вызывается функция, которую вы уже видели в предыдущем примере:

__poll_forever = function(self)       local mq = self.mqtt       while true do         self.connected, _ = mq:poll_one()         if not self.connected then           if self.auto_reconect then             self:__try_reconnect()           else             log.error(               "mqtt: the client is not currently connected, error %s", emsg)           end         end         fiber.sleep(self.POLL_INTERVAL)       end     end,

Я также портировал все функции из API libmosquitto. Посмотреть на результат можно здесь. По ссылке дан пример использования. Все что нужно сделать для сбора данных со всех устройств внутри mesh network — это вызвать функцию subscribe() из определенного места и опубликовать метод get()!

Заключение

Давайте посмотрим на то, что у нас получилось: Соединение с сервером приложения установлено посредством предоставляемой Tarantool master-master репликации. Из этого вытекают два полезных свойства:

  1. Если сервер приложения изменяет какие-либо данные, эти обновленные данные доставляются на все IoT-устройства в сети.
  2. Если IoT-устройство изменяет какие-либо данные, эти обновленные данные доставляются на сервер приложения.

Именно эти свойства и являются решением моих проблем.

Я также могу соединить мои IoT-устройства посредством master-master репликации. Таким образом устройства и облако объединяются в кластер, который можно использовать для синхронизации всех данных. Все IoT-устройства и облако синхронизированы большую часть времени, за исключением случаев, когда между ними пропадает соединение. Как только соединение будет восстановлено, все данные снова синхронизируются. Просто замечательно!

Шлюз с установленным на него приложением 6LBR позволяет обмениваться данными между моими IoT-устройствами и другими IoT-устройствами. Он оборачивает каждое сообщение в MQTT-сообщение и передает его в канал UART.

IoT-устройство #N с установленным на него MQTT-брокером считывает эти сообщения из канала UART. MQTT-брокер перенаправляет сообщения в Tarantool по MQTT-соединению. Tarantool считывает их, затем для каждого сообщения сервер приложений Tarantool выполняет некоторый код.

IoT-устройство #N соединено со всеми остальными устройствами посредством предоставляемой Tarantool master-master репликации. Такая же репликация используется для соединения всех устройств с облаком.

На этом все! Я решил поставленную задачу и очень надеюсь, что мой опыт поможет вам в ваших собственных проектах в будущем. Подытожу: я использовал Tarantool и как основной фронтенд на моих выделенных серверах, и как сервер приложений. Если вас заинтересовала данная тема, рекомендую взглянуть на другую мою статью на английском языке. Оставайтесь на связи и следите на новостями!

Источник: habrahabr.ru

Комментарии: