Проектирование архитектуры|Как синхронно обрабатывать асинхронные запросы?

Архитектура

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

полный текст аннотации:

  • Проблемы, которые асинхронность приносит с существующими архитектурами
  • Dubbo асинхронно-синхронное решение
  • Дизайн асинхронной и синхронной архитектуры

0x00 Предисловие

Существует существующая система, и общая архитектура выглядит следующим образом:

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

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

После доступа общая архитектура выглядит следующим образом:

Из-за политик сетевой изоляции приемники уведомлений и службы связи необходимо развертывать отдельно. Без этого требования коммуникационная служба B и приемник уведомлений могут быть объединены в одно приложение.

Кроме того, все приложения на рисунке развернуты на двух узлах.

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

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

  1. Как войти в сервис связи B бизнес потокждатьусловие? И чтобудитьПравильно ждете темы?
  2. Поскольку коммуникационная служба B развернута на двух узлах, как получатель уведомлений пересылает результат узлу, ожидающему обработки?

Решение проблемы 1 относится к дизайнерской идее Dubbo.

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

Поскольку Dubbo основан на базовойNettyОтправьте сетевой запрос, который является асинхронным процессом. Чтобы бизнес-потоки ожидали синхронно, этот процесс необходимо преобразовать из асинхронного в синхронный.

0x01.Dubbo асинхронно-синхронное решение

1.1 Синхронная блокировка бизнес-потоков

Код, с помощью которого Dubbo инициирует удаленный вызов, находится по адресуDubboInvoker#doInvoke:

Версия Dubbo: версия 2.6.X. 2,7.X рефакторингDefaultFuture, но основной принцип остается прежним.

0082zybply1gc7t3m2louj31650u0u0x

По умолчанию Dubbo поддерживает синхронный вызов, который будет создан здесь.DefaultFutureобъект.

Здесь очень важная логика, каждый запрос генерирует уникальныйID,ПотомIDа такжеDefaultFutureотношение отображения, хранящееся вMapсередина.

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

пройти черезIDЭто уникальное отношение отображения, естественно найти соответствующее емуDefaultFuture, разбудите соответствующий бизнес-поток.

来源:Dubbo 官网

вызов деловой темыDefaultFuture#getметод в блокировку. Этот код относительно прост, вызываяCondition#awaitЗаблокируйте линейный слой.

1.2 Разбудить бизнес-тред

Когда потребитель получает результат возврата от поставщика услуг, он вызываетDefaultFuture#receivedметод.

по уникальности в объекте ответаID, найти соответствующийDefaultFutureобъекта, тем самым устанавливая результатDefaultFutureобъекта, а затем разбудить соответствующий бизнес-поток.

На самом деле здесь есть точка оптимизации, использующая done#signalAll вместо done#signal. Это следует учитывать при использовании механизма уведомления об ожидании состояния.

Подробности см.:GitHub.com/Apache/Belly Daddy…

1.3 Особенности конструкции

Обычно, когда потребитель получает ответ, онFUTURESэтоMapУдалитьDefaultFuture.

但是在异常情况下,服务提供者若处理缓慢,不能及时返回响应结果,消费者业务线程将会因为超时苏醒。 В этой ситуацииFUTURESЭффективныйDefaultFutureобъект. Если его вовремя не убрать, в крайнем случае произойдетOOM.

DefaultFutureАсинхронный поток будет открыт внутри для регулярного опроса.FUTURESсудитьDefaultFutureТайм-аут, своевременная очистка недействительна (тайм-аут)DefaultFuture.

0x02.Проект схемы переадресации

Согласно решению Даббо, решение проблемы 1 относительно простое. Конкретный процесс выглядит следующим образом:

  1. Коммуникационная служба B генерирует уникальный запросID, в сторонний сервис
  2. Если запрос выполнен успешно, внутренняя версия используетMapСохраняйте переписку и делайте блокировку бизнес-треда и ждите
  3. Служба связи B получает результат асинхронного уведомления черезIDНайдите соответствующий бизнес-поток и разбудите соответствующий поток

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

业务线程等待时间=通信服务 B 接口的超时时间 - 调用第三方服务 B 接口消耗时间

Конкретный код не будет опубликован здесь. Подробный код см. в Dubbo.DefaultFuture.

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

  1. SocketServerстроить планы
  2. MQстроить планы

2.1 SocketServer

Служба связи B используетSocketServerСоздайте приемник службы, когда приемник уведомлений получает уведомление сторонней службы B, передайтеSocketНаправьте результат в службу связи B.

Общая архитектура системы выглядит следующим образом:

Благодаря развертыванию двойного узла производственной службы, программа приемника уведомлений не может мертвая запись адреса пересылки. Здесь мы спросимIDИ услуги связи Бsocketслужебный адрес депозит отношенияRedis, а затем уведомить принимающую программу черезIDНайдите правильный адрес.

Честно говоря, этот план немного сложен.

ПервыйSocketServerКодить сложно, написание эффективногоSocketServerЭто сложнее, и это может вызвать различныеBug.

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

Третье дополнительное введениеRedisЗависимость, сложность системы становится высокой.

2.2 Схема MQ

относительноSocketServerстроить планы,MQСхема относительно проста, вотMQСпособ потребления вещания, архитектура показаны на рисунке:

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

Услуга связи B включает режим потребления вещания, тянетMQИнформация.

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

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

В сравненииSocketServerстроить планы,MQОбщий процесс решения относительно прост, сложность программирования невелика и нет специальной настройки.

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

Здесь мы решили использоватьRocketMQ, долгий опросPullспособ гарантировать, что сообщение будет в реальном времени,

Таким образом, здесьMQстроить планы.

0x03.Сводка

От асинхронного к синхронному нам нужно решить проблему синхронной блокировки и как проснуться.

Блокировку/пробуждение можно использовать отдельноCondition#await/signalAll. Но этот процесс нам нужен для генерации уникального запросаID, и сохраните этоIDОтображение отношений с бизнес-потоками. Мы будем ждать, пока результат не будет возвращен, прежде чем мы сможем передать уникальныйIDРазбудите правильный ожидающий поток.

Если вы понимаете вышеперечисленные моменты, проблема асинхронного и синхронного может быть легко решена.

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

использованная литература

  1. Хироши Ватанабэ.apache.org/this-capable/docs/…
  2. Хироши Ватанабэ.apache.org/this-talent/blog/…

Лучше всего сказать (пожалуйста, обратите внимание)

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

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

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

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

Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: программа для общения, ежедневный толчок галантерейных товаров. Если вас интересует мой рекомендуемый контент, вы также можете подписаться на мой блог:studyidea.cn