Что такое синхронная и асинхронная передача данных

Синхронная и асинхронная передача данных: терминология и отличия

Что такое синхронная и асинхронная передача данных. Смотреть фото Что такое синхронная и асинхронная передача данных. Смотреть картинку Что такое синхронная и асинхронная передача данных. Картинка про Что такое синхронная и асинхронная передача данных. Фото Что такое синхронная и асинхронная передача данных

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

Асинхронная передача данных — это современная популярная тенденция в разработке. Многие нынешние инструменты по программированию имеют собственные инструменты для реализации асинхронных задач. Никто не любит просто ждать, поэтому всегда нужно тщательно определять, когда налаживать синхронное, а когда — асинхронное взаимодействие программы.

Синхронное представление в быту

У нас есть некая занятая девушка, которая запланировала на вечер познакомить родителей со своим молодым человеком. Чтобы все прошло идеально, ей нужно:

доделать дела на работе;

подготовить вечерний наряд;

сделать прическу, маникюр и накрасит ь ся;

попросить маму накрыть на стол.

Асинхронная передача данных в программировании

Асинхронная передача данных — это когда долго выполняемую функцию убирают из основного потока выполнения программы. Она не завершается, а продолжает работать в каком-нибудь другом месте. А сама программа не «зависает» и не «тормозит», а продолжает свое выполнение.

То есть при работе ресурса с фильмами выполнение главного потока программы разделится на 2 части: одна будет поддерживать взаимодействие со страницей, а вторая будет отправлять запрос и ожидать ответа от сервера. Таких асинхронных задач в программе может быть несколько. Для большого их количества придумали даже специальную очередь, которая работает по принципу : кто первый пришел, тот первый ушел.

Терминология асинхронности

Конкурентность. Данны й термин оз начает, что происходит одновременное выполнение нескольких задач. Эти задачи могут быть вообще не связаны друг с другом, поэтому не будет иметь значени я, какая из них завершит выполнение раньше, а какая — позже. Каждая такая задача формирует отдельный поток выполнения.

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

Многопоточность. Данный термин обозначает наличие нескольких потоков выполнения программы.

Заключение

Синхронная и асинхронная передача данных может осуществляться во многих сферах. Мы показали на примере программирования, как работают синхронные и асинхронные события. У обоих подходов есть свои достоинства и недостатки, поэтому использовать их в своих программах нужно обдуманно.

Нельзя утверждать, что асинхронная передача данных — это единственно правильный подход. Это совсем не так, потому что синхронный подход тоже до сих пор очень популярен и часто используется.

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

Источник

В чем разница между синхронной и асинхронной передачей данных

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

Что такое синхронная и асинхронная передача данных. Смотреть фото Что такое синхронная и асинхронная передача данных. Смотреть картинку Что такое синхронная и асинхронная передача данных. Картинка про Что такое синхронная и асинхронная передача данных. Фото Что такое синхронная и асинхронная передача данных

Содержание:

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

Ключевые области покрыты

1. Что такое синхронная передача данных
— определение, функциональность
2. Что такое асинхронная передача данных
— определение, функциональность
3. В чем разница между синхронной и асинхронной передачей данных
— Сравнение основных различий

Основные условия

Асинхронная передача данных, синхронная передача данных

Что такое синхронная и асинхронная передача данных. Смотреть фото Что такое синхронная и асинхронная передача данных. Смотреть картинку Что такое синхронная и асинхронная передача данных. Картинка про Что такое синхронная и асинхронная передача данных. Фото Что такое синхронная и асинхронная передача данных

Что такое синхронная передача данных

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

Что такое синхронная и асинхронная передача данных. Смотреть фото Что такое синхронная и асинхронная передача данных. Смотреть картинку Что такое синхронная и асинхронная передача данных. Картинка про Что такое синхронная и асинхронная передача данных. Фото Что такое синхронная и асинхронная передача данных

Рисунок 1: Синхронная и асинхронная передача данных

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

Что такое асинхронная передача данных

При асинхронной передаче данных передатчик и приемник работают на разных тактовых частотах. Он использует стартовые и стоповые биты для данных. Согласно приведенному выше примеру (фиг.1), каждый байт данных внедряется в начальный и конечный биты. «0» обозначает начальный бит, а «1» обозначает конечный бит. «1» и «0», выделенные красным цветом, являются начальным и конечным битами. Кроме того, синхронизация не является важным фактором в асинхронной передаче данных.

В цифровой системе, если другие регистры и регистры ЦП используют свои собственные частные часы, они имеют разные сигналы синхронизации. Следовательно, процессор и устройства ввода и вывода должны координировать передачу данных. Это называется асинхронной передачей данных.

Разница между синхронной и асинхронной передачей данных

Определение

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

Скорость передачи данных

Старт и Стоп Биты

При синхронной передаче не возникает никаких дополнительных затрат на дополнительные стартовые и стоповые биты. С другой стороны, асинхронная передача данных использует стартовые и стоповые биты.

Разрыв между данными

При синхронной передаче данных нет промежутков между данными и потоками данных в виде непрерывного потока. Однако при асинхронной передаче данных между данными могут быть промежутки.

Интервалы времени

Синхронная передача использует постоянные интервалы времени. Напротив, асинхронная передача использует случайные или нерегулярные интервалы времени. Это еще одно различие между синхронной и асинхронной передачей данных.

Примеры

Например, чаты и видеоконференции используют синхронную передачу данных, в то время как электронные письма используют асинхронную передачу данных.

Заключение

Основное различие между синхронной и асинхронной передачей данных заключается в том, что при синхронной передаче данных передатчик и приемник синхронизируются с одним и тем же тактовым импульсом, тогда как при асинхронной передаче данных передатчик и приемник не используют общий синхронизирующий сигнал.

Ссылка:

1. Синхронная передача данных | COA, Образование 4u, 11 декабря 2017 года,

Источник

Синхронный и асинхронный режимы передачи цифрового сигнала

В предыдущих постах я разобрал, что такое синхронизация при передаче цифрового сигнала и как на уровне логических элементов может происходить прием цифрового сигнала в получателе сигнала.

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

Что такое синхронная и асинхронная передача данных. Смотреть фото Что такое синхронная и асинхронная передача данных. Смотреть картинку Что такое синхронная и асинхронная передача данных. Картинка про Что такое синхронная и асинхронная передача данных. Фото Что такое синхронная и асинхронная передача данных
Рисунок 1
На рисунке 1 верхний из двух сигналов может быть передан по одному каналу, а нижний (тактовый сигнал) — по другому, параллельному, каналу. Задний фронт (помечен зеленым цветом) импульса тактового сигнала командует источнику сигнала отправить очередной бит цифрового сигнала. Передний фронт (помечен красным цветом) импульса тактового сигнала командует получателю сигнала взять образец сигнала, приходящего от источника. Именно поэтому задний фронт каждого импульса тактового сигнала на рисунке 1 совпадает с началом каждого бита передаваемого от источника сигнала, а передний фронт каждого импульса тактового сигнала совпадает с серединой каждого бита передаваемого от источника сигнала.

Тут может быть два варианта: 1) тактовый импульс идет из источника сигнала. В этом случае получатель сигнала по полученному тактовому импульсу подстраивается под скорость работы источника сигнала. 2) Тактовый импульс идет из получателя сигнала к источнику сигнала. Источник сигнала начинает посылать цифровой сигнал с цифровыми данными при получении тактового сигнала от получателя сигнала. В этом случае источник сигнала подстраивается под скорость работы получателя сигнала.

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

Существует еще так называемый «асинхронный режим» передачи цифрового сигнала или просто «асинхронная» передача цифрового сигнала. На самом деле, несмотря на то, что такая передача цифрового сигнала называется «асинхронной» (то есть «не синхронной»), при получении цифрового сигнала в получателе в этом случае всё равно требуется (и выполняется) синхронизация получателя и источника сигнала. Просто в случае асинхронной передачи цифровой сигнал с цифровыми данными идет кусками (пакетами, байтами и тому подобное), а между этими кусками есть промежутки, на которых канал остается пустым (без полезной нагрузки цифровыми данными). В итоге синхронизация нужна (и выполняется) только в момент приема получателем очередного куска цифрового сигнала с цифровыми данными, а в пустом промежутке между кусками сигнала с данными синхронизация не нужна (и не выполняется). То есть под словом «асинхронная» понимается не полное отсутствие синхронизации, а прерывистое выполнение синхронизации — она то выполняется, то не выполняется.

В википедии асинхронный режим передачи данных проиллюстрирован такой картинкой:

Что такое синхронная и асинхронная передача данных. Смотреть фото Что такое синхронная и асинхронная передача данных. Смотреть картинку Что такое синхронная и асинхронная передача данных. Картинка про Что такое синхронная и асинхронная передача данных. Фото Что такое синхронная и асинхронная передача данных
Из-за излишней компактности иллюстрации некоторые соседние надписи на этой иллюстрации сливаются и зритель с первого раза может воспринять некоторые соседние надписи на этой картинке за одну надпись, что может сильно затруднить понимание. На самом деле, на этой картинке только надпись «data bits» состоит из двух слов. Все остальные надписи — однословные.

Чтобы соседние надписи не сливались, я отделил их друг от друга цветом, кое-где добавил к линиям стрелки для понятности, и некоторые линии сделал пунктирными:

Что такое синхронная и асинхронная передача данных. Смотреть фото Что такое синхронная и асинхронная передача данных. Смотреть картинку Что такое синхронная и асинхронная передача данных. Картинка про Что такое синхронная и асинхронная передача данных. Фото Что такое синхронная и асинхронная передача данных
Понятия «mark» и «space», использующиеся в англоязычной литературе, посвященной передаче цифрового сигнала, по-русски означают «логическая единица» и «логический нуль» соответственно или для булевой алгебры — «true» («истина») и «false» («ложь»). В физическом смысле логическая единица может представляться, к примеру, высоким уровнем сигнала, а логический нуль — низким уровнем сигнала, или наоборот. В англоязычной википедии по этим понятиям есть статья «Mark and space». На рассматриваемом рисунке логическая единица («mark») показана высоким уровнем цифрового сигнала, а логический нуль («space») показан низким уровнем цифрового сигнала.

Как и описывалось выше при определении понятия «асинхронной» передачи цифрового сигнала, на рисунке есть промежутки, в течение которых канал заполнен данными, и промежутки, в течение которых канал не заполнен данными (последнее состояние на рисунке обозначено английским словом «idle», которое по-русски в данном случае значит «пустой» или «незанятый»). На рисунке таких незанятых промежутка два: перед стартовым битом первого байта и после стопового бита второго байта. Между двумя байтами, изображенными на рисунке, незанятого данными промежутка «idle» нет, но он может быть. Просто в данном случае байты переданы впритык друг к другу, но могли быть переданы и разделенными промежутком «idle».

На рисунке промежуток «idle» показан высоким уровнем сигнала, но в разных протоколах он может быть представлен как высоким уровнем сигнала, так и низким уровнем сигнала. Тут есть условие: стартовый бит всегда должен быть представлен уровнем сигнала, противоположным уровню промежутка «idle», а стоповый бит должен быть представлен уровнем сигнала, совпадающим с уровнем промежутка «idle». Очевидно, что стартовый и стоповый биты должны быть представлены противоположными уровнями сигнала.

Как работает асинхронный режим передачи цифрового сигнала? Каждый кусок (пакет, байт и тому подобное) данных может быть передан отдельно, отделяясь (или не отделяясь) друг от друга промежутками «idle» (канал не занят данными). При приеме очередного байта получателю сигнала нужно достичь синхронизма с источником сигнала, чтобы получить данные без ошибок. Для этого получателю сигнала необходимо знать момент начала входящего байта. Этот момент на обсуждаемом рисунке обозначен передним фронтом стартового бита («start»): в данном случае это переход из высокого уровня сигнала к низкому уровню сигнала. После передачи битов данных («data bits») обязательно нужно вернуть цифровой сигнал на уровень промежутка «idle», то есть в случае, показанном на обсуждаемом рисунке — на высокий уровень сигнала. Это нужно для того, чтобы было возможно отметить начало следующего байта передним фронтом стартового бита следующего байта. Возврат на уровень сигнала, соответствующий промежутку «idle», выполняется стоповым битом («stop»).

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

Как видно из рисунка, к каждому байту данных при асинхронной передаче необходимо добавлять два дополнительных бита: стартовый и стоповый. Такую передачу цифрового сигнала часто называют «старт-стопной» передачей данных. Очевидно, что эти дополнительные биты уменьшают скорость передачи данных по сравнению с синхронным режимом передачи данных, при котором все байты передаются впритык друг к другу, без дополнительного обозначения начала и конца байта с помощью стартового и стопового битов.

Скорость передачи данных при асинхронном режиме передачи меньше еще и из-за того, что между байтами могут вставляться промежутки «idle» (канал не занят данными), а при синхронном режиме передачи между передаваемыми данными нет никаких лишних промежутков.

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

Источник

Интеграция: синхронное, асинхронное и реактивное взаимодействие, консистентность и транзакции

Продолжаю серию статей про хорошую интеграцию. В первой статье я говорил, что хорошая админка обеспечит быстрое решение инцидентов — как ключевой фактор для устойчивости работы всего комплекса систем. Во второй — про использование идемпотентных операций для устойчивой работы в условиях асинхронного взаимодействия и при сбоях. В этой статье рассмотрим синхронный, асинхронный и реактивный способы взаимодействия между сервисами и более крупными модулями. А так же способы обеспечить консистентность данных и организовать транзакции.

Что такое синхронная и асинхронная передача данных. Смотреть фото Что такое синхронная и асинхронная передача данных. Смотреть картинку Что такое синхронная и асинхронная передача данных. Картинка про Что такое синхронная и асинхронная передача данных. Фото Что такое синхронная и асинхронная передача данных

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

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

Синхронное взаимодействие

Синхронное взаимодействие — самое простое. Оно скрывает все детали удаленного вызова, что для вызывающего сервиса превращается в обычный вызов функции с получением ответа. Для его организации есть множество протоколов — например, давно известные 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-чат.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *