Обновить

Протоколы для систем реального времени

Уровень сложностиСредний
Время на прочтение13 мин
Охват и читатели8.6K
Всего голосов 10: ↑10 и ↓0+13
Комментарии24

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

У нас на плате несколько чипов обменивались данными через встроеный коммутатор. При использовании udp происходили пропажи датаграм и сбои функционирования, при tcp всё работало. Сами устройства по тому же протоколу типа rpc обменивались с другими устройствами того же типа по сети и с клиентскими устройствами.

При обновлении протокола сохранялась базовая обратная совместимость, что позволяло запускать апдейты на слейвах и клиентах. Для protobuf тоже можно реализовать версионирование - это пользовательская функциональность, но вроде работающая.

В теории protobuf много чего умеет, в том числе опциональные данные, но реальность такова, что никто не пытается разбираться с этими возможностями - просто де факто выясняется, что протокол изменился и больше ничего не работает. Вот вам новый файлик описания от protobuf, перегенерируйте и перепишите реализацию клиента. Причём изменения не просто уровня "новое поле", а "мы переосмыслили всё" - другие сущности, иначе вложены. Справедливости ради, JSON от такого тоже не очень спасает. Но там хотя бы это видно глазами и, как правило, корректировки возможны даже без документации.

А насчёт UDP - скорее всего, это значит, что потери, всё же, были, причём существенные. Я проводил опыт - если начинать в гигабитной сети флуд, то TCP ложится замертво на порядок раньше UDP. Но, конечно, использовать UDP можно только если протокол по сути идемпотентный и, в основном, состоит из апдейтов одного и того же потока данных. Например, передача очередного значения датчика температуры.

У нас между чипами было несколько потоков данных мегабит под 10 в максимуме, плюс устройство получало отправляло такие же потоки по сети. И на их фоне команды управления и статусы разные. Просто буферизации не хватало местами и udp пропадали. Это же и причина использования ethernet внутри - проще pci, пропускная способность, дешево, стандартный стек и поддержка и интегрируется с клиентами и удалёнными устройствами.

Про протобуф согласен - для версионности надо заморачиваться.

Кстати, а если обмен между чипами на одной плате - почему выбрали не что-то совсем низкоуровневое, типа i2c/spi?

А есть какие-то мысли/опыт по поводу MessagePack? Или всё сказанное про protobuf относится и к MessagePack?

Относится, конечно. Прямого опыта применения, впрочем, не имею.

мне 300 лет ...

мне тоже, поэтому вероятно использовал бы ASN.1, общий packet parser для всех устройств в стиле "C" - while(1) {switch (tag) .... }, и по возможности унифицировал state machines для всех протоколов, от разработчиков отдельных устройств по возможности требовал бы их табличных описаний,

от советского модема на 300 бод ...

это супер интересно, такие модемы видеть приходилось, но людей разработчиков не встречал, может быть Вам и ППД программировать приходилось, например для работы X.25?

Нет, X.25 обошёл меня стороной. :) Если не считать истории со взломом ситибанка, которая меня почти коснулась. :)

ASN.1 - интересная мысль, но, пожалуй, тоже у простого JSON не выигрывает. Для протокола на базе JSON стейт машина как таковая не нужна. Читаем строку, парсим строку в абстрактное JSON дерево, смотрим значение ключевого поля, которое обозначает тип пакета (в плохом случае, если такого поля нет, - проверяем наличие в пакете полей с данными, однозначно идентифицирующими тип), конвертируем пакет в объект соответствующего класса. Это, фактически, три оператора.

в свое время много прошлось работать с протоколами OSI/ISO, там ASN.1 основной инструмент определения, достаточно простой и удобный для расширения, поэтому для работ подобных Вашим может быть полезным,

меня таки заинтересовал Ваш исторический опыт с 300 бод модемами, что примерно Вы с ними делали?

в далеком прошлом приходилось иметь дело с модемами, но тогдв уже Hayes interface был фактически стандартом

ЦСУ СССР, сбор статистических данных с союзных республик. Там оно в основном по телеграфным каналам шло, отсюда такая работа называлась "телеграфка", но проводили и эксперименты с модемами. Мы, правда, больше внутри Москвы экспериментировали. Если память не изменяет, с Госпланом. Позже были уже компактные настольные модемы 1200КН. На них я даже делал набор номера быстрой манипуляцией линии DTR. :)

понятно, спасибо

Все такие системы разворачиваются в закрытом контуре, и внутри контура все свои. Ключ для подсоединения к VPN – необходимая и достаточная часть защиты. Остальное только создаёт проблемы. Особенно при учёте того очевидного факта, что во всех реальных протоколах, которые нам встретились, логин и пароль передаются открытым текстом. То есть, если закрытый контур будет таки взломан, то первый же запуск wireshark сведёт ценность защиты паролем к абсолютному нулю.

Тут же по-разному. Напр в zero trust подходе, описанный Вами случай попросту не произошёл бы, банально потому, что смотреть особо нечего, даже располагаясь внутри одного "слоя" периметра. Понятно, что это можно вытащить всё в изолированный сегмент сети, вход в который на основании ААА. Зависит от заказчика, в общем.

Хорошо, предположим, я вас уговорил и вы не захлопнули сейчас крышку ноутбука с криком «двоичный протокол требует меньшей пропускной способности канала!» – а если захлопнули, то хотя бы посчитали, сколько вам этой полосы надо. А то в поганые 3 мегабита плохого LTE пролезает в секунду 360 пакетов размером в килобайт. И почти 4 тысячи пакетов по 128 байт.

С передачей данных могут быть разные засады. Например передать нужно по BLE 4.0. Тут будешь ужиматься, как сможешь. Или через Ямал что-то тянуть с удалённого объекта.

Не спорю - текстовый, он, конечно, удобен. Но задачи - важнее. Если стоит задача - максимально удобно его читать - то спору нет.

Небольшой бенчмарк ужималки массива данных длинной 8192 байта, как раз для нужд передачи по BLE:

- chosing delta method: delta=1168 bytes, delta-of-delta=1206 bytes
- chosing lzma comp: zstd=759 bytes, lzma=744 bytes
orig bytes : 8192
enc bytes  : 766
ratio      : 10.695x
equal check: True

Почему не XML:

  • Закрывающие теги реально впустую тратят полосу в канале. Даже мне жалко.

  • Его тяжелее парсить. Это мелочь, но она будет зря потреблять ваш процессор.

  • Его тяжелее читать глазами. Отладка будет сложнее.

Критичной разницы нет, и если XML вам очень мил, то – пожалуйста. Но по сумме баллов он уступает везде, и ни в чём не выигрывает.

Кроме того, библиотеки работы с JSON типично поддерживают прямое преобразование пакета в объект целевого языка.

Немного не понял про проблемы парсинга XML. Генерим структуру по wsdl, делаем десериализацию... вроде всё? Или имелось что-то другое ввиду?

Это не в минус JSON сказано, просто хотел понять текст написанного.

Спасибо за статью! Очень интересная!

Конечно, ситуации, когда нужно выжать кисоньку до отказа, тоже бывают. И бинарные протоколы в этом случае - неизбежность. Пример с ямалом - вообще отличный, респект.

Парсинг XML просто жрёт больше процессора, чем JSON, и обвязка парсера получается более тяжеловесная.

А, я понял Ваш пойнт, сразу wsdl не заметил. Не будет там с той стороны никто предоставлять, как правило. Хотя, конечно, всяко бывает.

Спустя несколько лет ковыряния как своих, так и чужих протоколов пришел к выводу, что лучше потратить время, но сделать поддержку как бинарного, так и текстового протокола (не обязательно json). Первый даст скорость и компактность кому это реально нужно, а второй в самом плохом варианте даст достучаться до железки на коленке, без мучительных попыток вспомнить "да где ж тот самый бит в этом месиве".

После протокола одних "коллег по цеху" глаз до сих пор дёргается, вот вроде и пакуют в стандартную строку ASCII символов, но нет же, байты бьют на "полубайты" (если что, это цитата из документации), докидывают 0x30 и начинаешь ловить по итогу и спецсимволы и текст там, где ждёшь числа. Зачем и чего ради непонятно, ибо получается и хуже битового и сложнее для парсинга, чем текстовые. И все это при фиксированной длине пакета.

О да, фиксированная длина пакета, совсем забыл про эту прелесть. Хуже только длина пакета, которая прописана в спеке протокола отдельно для каждого типа пакета. :)

Не хуже) длина пакета с данными программируется в dma и тот помещает весь блок данных с порта ввода в буфер соответствующего процесса

Перечитал свой пост ещё раз - в огороде бузина, а дядька запил) просто хотел сказать, что мы использовали протокол с фиксированным заголовком+данные для скоростного обмена в сети микропроцессоров. Текста там, правда, взяться было неоткуда, как и сериализации никакой

Фиксированная длинна пакета не то чтобы прямо плохо и ужасно, если нет желания в бинарном протоколе сопровождать данные заголовками и надо гонять сообщения быстро, то вполне приемлемо (за исключением поддержки и ломающегося обмена при апдейтах). Там именно больше вопросов к тому, что какой смысл был пытаться в ASCII строку, если по факту с ней работать один черт невозможно, приходится выгребать все 0x30, обратно клеить "полубайты" в байты и работать по факту с бинарным протоколом. Ещё из "приколов" той же железки, при реверсе протокола выяснилось, что хендшейк идёт через запрос версии (ПО/железа? а вот черт его знает) и на один и тот же запрос приходят разные ответы. Всего ответов оказалось два для более новой железки и один для более старой. В итоге если по какой-либо причине терялся один из запросов для новой железки она наглухо отрубала обмен без какого-либо ответа. Игнорировалась даже команда для софт ребута. Пришлось городить логику с контролем ответа на первое сообщение и дальше уже либо слать второй запрос, либо уже работать по остальной части протокола. Не удивлюсь, если в новом железе тоже поменялся хендшейк.

Ну и вместо обычной unix-time метки был какой-то свой костыль который по сути отличался только начальной датой ¯\_(ツ)_/¯

CAN и его производные это одни самых простых и надёжных протоколов для систем реального времени.

Когда не хватает CAN, берут CANOpen.

Как связаны производительность аппаратной части и реальное время?

Какую JVM вы использовали для реализаций решений реального времени на Java?

Как связаны протоколы данных и системы реального времени?

Где вообще в статье что-то полезное и интересное про протоколы для системы реального времени?

По поводу WebSocket вы сильно заблуждаетесь в их ненужности и просто не представляете, какую задачу они решают.

По поводу надёжности TCP вы тоже заблуждаетесь. По поводу UDP недооцениваете сложность задач.

  1. CAN плохо работает через Интернет. Очень плохо работает. Совсем никак. Кстати, с точки зрения решения задач именно реального времени в локальной среде CAN мало чем отличается от UDP broadcast. Разница в физической среде.

  2. Производительность аппаратной части связана с реальным временем формулой Tmax = T обработки одного события * число событий в секунду. Tmax < 1000 msec.

  3. Любую. Никаких специальных свойств JVM не потребовалось. Но, в целом, предпочитаем AXIOM, потому что техподдержка рядом. Некоторые действия, которые призваны снизить риски по отношению именно к задачам реального времени были предприняты, но они не зависят от применяемой JVM.

  4. Почти никак. Только загрузка процессора на парсинг, но, в целом, можно пренебречь.

  5. Это, вероятно, сильно зависит от того, кто читает и что ему в этой жизни нужно. Даже, пожалуй, наверняка зависит.

  6. Я не заблуждаюсь по поводу WebSocket и представляю, какую задачу они решают. В тех случаях, о которых я говорю эта задача не стояла и применение WebSocket было неуместно.

  7. Я не заблуждаюсь по поводу надёжности TCP и не недооцениваю сложность задач по поводу UDP.

Я запутался. Вы говорите про системы реального времени, а потом "CAN не подходит для Internet". Но так и Internet не подходит и для систем реального времени ввиду отсутствия многих гарантий. Далее вы говорите "любая JVM", но любая JVM не сертефицирована для реального-времени, даже если не брать RTSJ целиком, а что-то попроще - детерменированный сборщих мусора. Да даже если взять EpsilonGC, то:
1. Ему всё равно нужно вызывать системный malloc(), который сам по себе не real-time.
2. Узким местом останется планировщик, который снова не real-time.

Короче говоря, либо я плохо понимаю системы реального времени, либо вы говорите про какие-то другие системы реального времени.

Конкретная система, о которой в основном идёт речь, разворачивается на региональном уровне. Она опирается на собственные TCP/IP каналы связи, выделенные для общения аппаратуры с узлами системы. Эти каналы не несут никакой иной информации и, таким образом, обеспечивают гарантированную полосу, достаточную для работы системы. Что касается сертификации - это, конечно, система мягкого реального времени. Не хочется погружаться совсем уж в детали, но если коротко, то мы живём в простых ограничениях: цикл обработки данных должен уложиться в секунду - это приемлемо. Практически - всё на два порядка лучше, укладываемся в ~100 раз меньшее время. Запас в два порядка выглядит вполне приемлемо. Что касается детерминированности GC. Практически весь объём сборки мусора в нашем случае - это молодое поколение. Объём его тоже вполне предсказуем. Сам сборщик уже давно не stop the world. Процессоров (ядер) в машине избыток. Тред GC просто идёт, в основном, на отдельном ядре и в нашем профиле работы с памятью практически не ощутим. Если бы он хоть сколько-то был ощутим, то, конечно, мы бы применили пулы объектов, чтобы вообще избежать сборки мусора как таковой. Но практически сами разработчики GC этого не рекомендуют, и довольно давно.

На самом деле, конечно, "Как связаны протоколы данных и системы реального времени?" - не совсем никак. Зависят. В частности, логика обработки длинных запросов. См. диаграммы в статье.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации