Оригинальный адрес:Блог Лян Гуйчжао
адрес блога:blog.720ui.com
Добро пожаловать на официальный аккаунт: «Серверное мышление». Группа людей с одинаковой частотой растет вместе, вместе совершенствуется и преодолевает ограничения познания.
Эволюция от локальных транзакций к распределенным транзакциям
Что такое транзакция? Прежде чем ответить на этот вопрос, давайте рассмотрим классический сценарий: переводы с торговых площадок, таких как Alipay. Предположим, что Сяомину необходимо использовать Alipay для перевода Сяохун 100 000. В это время счет Сяомина будет на 100 000 меньше, а счет Сяохуна будет на 100 000 больше. Если в процессе перевода произойдет сбой системы, а счет Сяомина будет меньше 100 000 юаней, а сумма счета Сяохуна останется неизменной, возникнет большая проблема, поэтому в это время нам нужно использовать транзакции. См. Рисунок 6-1.
Здесь отражена очень важная особенность транзакций: атомарность. На самом деле у транзакций есть четыре основных свойства: атомарность, непротиворечивость, изоляция и долговечность. Среди них атомарность, то есть операции внутри транзакции либо все завершаются успешно, либо все терпят неудачу, и не заканчиваются на определенном звене посередине. Непротиворечивая, база данных должна быть в согласованном состоянии даже до и после выполнения транзакции. Если транзакция завершается со сбоем, ее необходимо автоматически откатить в исходное состояние.Другими словами, после фиксации транзакции результаты, видимые другими транзакциями, будут такими же, и после отката транзакции другие транзакции могут только увидеть состояние до отката. Изоляция, то есть в параллельной среде, когда разные транзакции изменяют одни и те же данные одновременно, одна незавершенная транзакция не влияет на другую незавершенную транзакцию. Постоянство, то есть после фиксации транзакции ее измененные данные будут постоянно сохранены в базе данных, а ее изменения будут постоянными.
Локальные транзакции обеспечивают надежную согласованность данных с помощью ACID. ACID — это аббревиатура от Atomic, Consistency, Isolation и Durability. В реальном процессе разработки мы более или менее используем локальные транзакции. Например, обработка транзакций MySQL использует для начала транзакцию, rollback для отката транзакции и commit для подтверждения транзакции. Здесь после фиксации транзакции изменения записываются в журнал повторов, а журнал отмены используется для отката, когда не удается обеспечить атомарность транзакции. Автор добавляет, что все разработчики, использующие язык Java, знакомы со Spring. Spring использует аннотацию @Transactional для обработки транзакционных функций. На самом деле, Spring инкапсулирует эти детали.При создании связанных компонентов, когда вам нужно внедрить связанные компоненты, аннотированные с помощью @Transactional, используйте прокси для внедрения и откройте для нас транзакцию фиксации/отката в прокси. См. Рисунок 6-2.
При стремительном развитии бизнеса, в условиях массивных данных, например, десятков миллионов или даже сотен миллионов данных, время, затрачиваемое на запрос, станет больше, и даже единичная точка давления на базу данных будет вызванный. Поэтому мы должны рассмотреть подбиблиотеки и подтабличные решения. Целью подбазы данных и подтаблицы является снижение нагрузки на единую базу данных и единую таблицу базы данных, повышение производительности запросов и сокращение времени запроса. Здесь давайте сначала рассмотрим сценарий разделения библиотеки заказов. Фактически, стратегию разделения таблицы можно обобщить как вертикальное разделение и горизонтальное разделение. Вертикальное разделение, разделение полей таблицы, то есть разделение таблицы с множеством полей на несколько таблиц, что делает данные строки меньше. С одной стороны, количество байтов, передаваемых по сети между клиентской программой и базой данных, может быть уменьшено, поскольку производственная среда использует ту же пропускную способность сети, а увеличение количества одновременных запросов может вызвать узкие места и блокировки пропускной способности. С другой стороны, блок данных может содержать больше данных, что уменьшает количество операций ввода-вывода при запросе. Разделить по горизонтали, чтобы разделить строки таблицы. Поскольку количество строк в таблице превышает несколько миллионов строк, она будет замедляться.В это время данные одной таблицы можно разбить на несколько таблиц для хранения. Для горизонтального разделения существует множество стратегий, например, модульное разделение, разделение по временным измерениям и т. д. В этом сценарии, хотя мы разделяем таблицы по определенным правилам, мы все же можем использовать локальные транзакции. Однако подтаблица в библиотеке лишь решает проблему, заключающуюся в том, что данные одной таблицы слишком велики, но не распределяет данные одной таблицы по разным физическим машинам, поэтому не снижает нагрузку на сервер MySQL. , а на той же физической машине по-прежнему есть данные Конкуренция за ресурсы и узкие места, включая ЦП, память, дисковый ввод-вывод, пропускную способность сети и т. д. Для сценария разделения подбазы данных он делит данные одной таблицы на разные базы данных, а структура таблиц нескольких баз данных одинакова. На этом этапе, если мы направим данные, необходимые для использования транзакций, в одну и ту же библиотеку в соответствии с определенными правилами, мы сможем обеспечить их строгую согласованность с помощью локальных транзакций. Однако для вертикального разделения по бизнесу и функциям бизнес-данные будут разделены на отдельные базы данных. Здесь сплит-система столкнется с проблемами согласованности данных, потому что нам нужно обеспечить, чтобы данные, гарантированные транзакциями, были разбросаны по разным базам данных, и каждая база данных может только гарантировать, что ее собственные данные могут удовлетворять требованиям ACID для обеспечения строгой согласованности. системы, они могут быть развернуты на разных серверах и могут обмениваться данными только через сеть, поэтому невозможно точно знать выполнение транзакций в других базах данных. См. Рисунок 6-3.
Кроме того, существует не только проблема, которую локальные транзакции не могут решить в вызовах кросс-базы данных, но с реализацией микросервисов каждый сервис имеет свою собственную базу данных, и базы данных являются независимыми и прозрачными. Если служба A необходимо получить данные сервиса B, существует вызов по перекрестному обслуживанию. Если служба находится вниз, или сетевое соединение ненормально, время ожидания вызова синхронизации и другие сценарии приведут к несоответствию данных. Это также распределенный сценарий. Рассмотрим проблемы согласованности данных. См. Рисунок 6-4.
Подводя итог, можно сказать, что при расширении масштаба бизнеса подбиблиотека и бизнес-сервис после внедрения микросервиса вызовут проблему несогласованности распределенных данных. Поскольку локальные транзакции не могут удовлетворить спрос, на сцену выходят распределенные транзакции. Что такое распределенная транзакция? Мы можем просто понять, что это транзакционное решение для обеспечения согласованности данных в разных базах данных. Здесь нам необходимо сначала понять принцип CAP и теорию BASE. Принцип CAP — это аббревиатура от Consistency, Availability и Partition-tolerance, которая является теорией баланса в распределенных системах. В распределенной системе согласованность требует, чтобы все узлы могли гарантировать получение самых последних данных при каждой операции чтения; доступность требует, чтобы службы оставались доступными независимо от любого сбоя; отказоустойчивость секций требует, чтобы секционированные узлы могли нормально предоставлять внешние службы. На самом деле любая система может удовлетворить только два из них одновременно, а не все три. Для распределенных систем отказоустойчивость разделов является фундаментальным требованием. Что ж, если выбрана согласованность и устойчивость к разделам, а доступность утрачена, то сетевые проблемы могут сделать систему недоступной. Если вы выберете доступность и отказоустойчивость разделов и откажетесь от согласованности, данные между разными узлами не смогут синхронизироваться во времени, что приведет к несогласованности данных. См. рис. 6-5.
На данный момент теория BASE предлагает решение для согласованности и доступности.BASE — это аббревиатура от Basic Available (в основном доступный), Soft-state (мягкое состояние) и Ultimate Consistent (состоятельный в конечном итоге), что является теоретической поддержкой согласованности в конечном итоге. . Просто поймите, что в распределенной системе допускается потеря частичной доступности, и есть задержка в процессе синхронизации данных между разными узлами, но после периода ремонта окончательная согласованность данных наконец может быть достигнута. BASE подчеркивает возможную согласованность данных. По сравнению с ACID, BASE повышает доступность, допуская потерю частичной согласованности.
В настоящее время наиболее часто используемые в отрасли решения для распределенных транзакций включают двухфазный протокол фиксации с строгой согласованностью, трехэтапный протокол фиксации, а также надежный режим событий и режим компенсации с окончательной согласованностью, а также режим TCC Али. В следующих главах мы подробно познакомимся с ними и попрактикуемся.
Решение с сильной консистенцией
протокол двухфазной фиксации
В распределенной системе каждая база данных может только гарантировать, что ее собственные данные могут удовлетворять требованиям ACID для обеспечения строгой согласованности, но они могут быть развернуты на разных серверах и могут обмениваться данными только через сеть, поэтому невозможно точно знать транзакции в других базах данных. Выполнение. Следовательно, чтобы решить проблему координации между несколькими узлами, необходимо ввести координатора, ответственного за контроль результатов работы всех узлов, либо все успешно, либо все терпят неудачу. Среди них протокол XA — это протокол распределенных транзакций, который имеет две роли: менеджер транзакций и менеджер ресурсов. Здесь мы можем понимать менеджера транзакций как координатора, а менеджера ресурсов как участника.
Протокол XA гарантирует строгую согласованность благодаря протоколу двухэтапной фиксации.
Протокол двухэтапной фиксации, как следует из названия, состоит из двух фаз: первая фаза — подготовка, а вторая — фиксация. Здесь менеджер транзакций (координатор) в основном отвечает за контроль результатов работы всех узлов, включая процесс подготовки и процесс отправки. На первом этапе диспетчер транзакций (координатор) инициирует инструкцию по подготовке диспетчеру ресурсов (участнику) и спрашивает диспетчера ресурсов (участника), успешно ли выполнена предварительная фиксация. Если менеджер ресурсов (участник) может завершить операцию, он выполнит операцию без отправки и, наконец, выдаст свой собственный результат ответа, независимо от того, прошла ли предварительная отправка успешно или нет. На втором этапе, если все менеджеры ресурсов (участники) отвечают, что предварительная отправка прошла успешно, менеджеры ресурсов (участники) официально отправляют команду. Если один из менеджеров ресурсов (участников) отвечает, что предварительная фиксация не удалась, менеджер транзакций (координатор) выдает команду отката всем менеджерам ресурсов (участникам). Например, теперь у нас есть один менеджер транзакций (координатор) и три менеджера ресурсов (участники), то в этой транзакции нам необходимо обеспечить сильную согласованность данных этих трех участников в процессе транзакции. Во-первых, менеджер транзакций (координатор) инициирует команду подготовки, чтобы предсказать, успешно ли они были предварительно зафиксированы.Если все ответы на предварительную фиксацию успешны, то менеджер транзакций (координатор) формально инициирует команду фиксации для выполнения изменений данных. . См. рис. 6-6.
Обратите внимание, что, хотя протокол двухфазной фиксации предлагает решение для обеспечения строгой согласованности, все же есть некоторые проблемы. Во-первых, менеджер транзакций (координатор) в основном отвечает за контроль результатов работы всех узлов, включая процесс подготовки и процесс отправки, но весь процесс синхронный, поэтому менеджер транзакций (координатор) должен ждать каждого менеджера ресурсов ( участие Следующая операция может быть выполнена только после возврата результата операции. Это очень легко вызвать проблемы с блокировкой синхронизации. Во-вторых, единые точки отказа также требуют серьезного рассмотрения. Оба диспетчера транзакций (координатор) и диспетчер ресурсов (участник) могут быть отключены.Если диспетчер ресурсов (участник) выходит из строя, он не может ответить и все время ждет.Если диспетчер транзакций (координатор) дает сбой, процесс транзакции Контроллер теряется, иными словами, весь процесс будет постоянно блокироваться, и даже в крайних случаях одни администраторы ресурсов (участники) выполняют отправку данных, а некоторые не выполняют, и также будет иметь место несогласованность данных. В этот момент читатели зададут вопросы: все эти проблемы должны быть маловероятными ситуациями и вообще не возникать? Да, но для сценариев распределенных транзакций нам нужно не только учитывать нормальный логический поток, но также необходимо обращать внимание на аномальные сценарии с небольшой вероятностью.Если у нас нет плана обработки аномальных сценариев, может возникнуть несогласованность данных, а затем полагаться на ручной труд на более позднем этапе.Вмешательство и обработка будут очень дорогостоящей задачей.Кроме того, основным звеном транзакции может быть не проблема данных, а более серьезная проблема потери активов.
Протокол трехэтапной фиксации
Есть много проблем с протоколом двухфазной фиксации, поэтому протокол трехфазной фиксации вот-вот выйдет на сцену. Протокол трехфазной фиксации является усовершенствованной версией протокола двухфазной фиксации, отличается от протокола двухфазной фиксации тем, что для решения проблемы блокировки синхронизации введен механизм тайм-аута, а также менеджер ресурсов (участие в ) и завершить транзакцию.Если все менеджеры ресурсов (участники) могут завершить, инициируется второй этап подготовки и третий этап фиксации. В противном случае любой из диспетчеров ресурсов (участников) отвечает на выполнение или ждет тайм-аут, а затем завершает транзакцию. Подводя итог, трехэтапный протокол фиксации включает в себя: подготовку фазы 1, подготовку фазы 2 и фиксацию фазы 2. См. рис. 6-7.
Протокол трехфазной фиксации очень хорошо решает проблемы, связанные с протоколом двухфазной фиксации, и является очень важным решением. Однако несоответствия данных могут возникать в сценариях с крайне малой вероятностью. Поскольку протокол трехэтапной фиксации вводит механизм тайм-аута, если происходит сценарий тайм-аута менеджера ресурсов (участника), фиксация будет успешной по умолчанию, но если она не будет успешно выполнена или если другие менеджеры ресурсов (участники) перевернут обратно, то появится несоответствие данных.
в конечном итоге согласованное решение
режим ТСС
Протокол двухфазной фиксации и протокол трехфазной фиксации очень хорошо решают проблему распределенных транзакций, но в крайних случаях все равно возникают несоответствия данных.Кроме того, это будет дорого стоить системе.Внедрение менеджеров транзакций( координаторы) Впоследствии, скорее всего, возникнут точечные узкие места, а когда масштабы бизнеса продолжат расти, также возникнут проблемы с масштабируемостью системы. Обратите внимание, что это синхронная операция, поэтому после введения транзакции ресурсы не могут быть высвобождены до завершения глобальной транзакции, и производительность может стать большой проблемой. Поэтому он редко используется в сценариях с высокой степенью параллелизма. Поэтому Али предложил другое решение: режим TCC. Обратите внимание, что многие читатели отождествляют двухфазную фиксацию с протоколом двухфазной фиксации.Это недоразумение.На самом деле, режим TCC также является двухфазной фиксацией.
В режиме TCC задача разбивается на три операции: «Попробовать», «Подтвердить» и «Отмена». Если у нас есть метод func(), то в режиме TCC он становится тремя методами: tryFunc(), confirmFunc() и cancelFunc().
tryFunc();
confirmFunc();
cancelFunc();
В режиме TCC основная бизнес-служба отвечает за инициирование процесса, а подчиненная бизнес-служба обеспечивает выполнение трех операций: Try, Confirm и Cancel в режиме TCC. Среди них также есть роль менеджера транзакций, отвечающая за контроль согласованности транзакций. Например, теперь у нас есть три бизнес-сервиса: сервис транзакций, сервис инвентаризации, сервис платежей. Пользователь выбирает продукт, размещает заказ, а затем выбирает способ оплаты. Затем для этого запроса служба транзакций сначала вызывает службу инвентаризации, чтобы вычесть запасы, а затем служба транзакций вызывает службу оплаты для соответствующих платежные операции, а затем платежная служба запросит третью сторону.Платежная платформа создает транзакцию и списывает платеж.Здесь транзакционная служба является основной бизнес-службой, а служба инвентаризации и платежная служба являются вспомогательными бизнес-услугами. См. Рисунок 6-8.
Разберем процесс режима TCC. На первом этапе основная бизнес-служба вызывает все операции Try подчиненных бизнес-служб, а диспетчер транзакций записывает журнал операций. На втором этапе, когда все подчиненные бизнес-сервисы выполнены успешно, выполните операцию Confirm, в противном случае выполните обратную операцию Cancel для отката. См. Рисунок 6-9.
Теперь давайте поговорим об общих идеях бизнес-реализации модели TCC. Сначала служба транзакций (основная бизнес-служба) регистрируется в диспетчере транзакций и запускает транзакцию. Фактически диспетчер транзакций — это концептуальный механизм управления глобальными транзакциями, который может быть бизнес-логикой, встроенной в основной бизнес-сервис, или абстрактной структурой TCC. Фактически он генерирует глобальный идентификатор транзакции для записи всей цепочки транзакций и реализует набор логики обработки для вложенных транзакций. Когда основная бизнес-служба вызывает все операции try подчиненных бизнес-служб, диспетчер транзакций использует локальную транзакцию для записи соответствующего журнала транзакций.В этом случае он записывает запись о вызове службы инвентаризации и запись о вызове платежный сервис и его статус Установите в состояние «предварительно зафиксировано». Здесь вызов операции Try из бизнес-службы является основным бизнес-кодом. Итак, как операция Try связана с соответствующими операциями Confirm и Cancel? На самом деле, мы можем написать файл конфигурации, чтобы установить отношения привязки, или также неплохо добавить два параметра, подтвердить и отменить через аннотации Spring. Когда все подчиненные бизнес-службы работают успешно, диспетчер транзакций выполняет операцию подтверждения через аспект контекста транзакции TCC и устанавливает свой статус в «успешное» состояние; попробуйте. Таким образом, режим TCC гарантирует его окончательную согласованность посредством компенсации.
Существует множество зрелых проектов с открытым исходным кодом для реализации структуры TCC, таких как структура tcc-транзакций. (Подробнее о структуре tcc-транзакций см.:GitHub.com/ часто детали…Фреймворк в основном включает три модуля: tcc-transaction-core, tcc-transaction-api и tcc-transaction-spring. Среди них tcc-transaction-core — базовая реализация tcc-транзакции, tcc-transaction-api — API, используемый tcc-транзакцией, а tcc-transaction-spring — поддержка Spring для tcc-транзакции. tcc-transaction абстрагирует каждую бизнес-операцию от участников транзакции, и каждая транзакция может содержать несколько участников. Участникам необходимо объявить три типа методов: try/confirm/cancel. Здесь мы помечаем метод try аннотацией @Compensable и определяем соответствующие методы подтверждения/отмены.
// try 方法
@Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = MethodTransactionContextEditor.class)
@Transactional
public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {}
// confirm 方法
@Transactional
public void confirmRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {}
// cancel 方法
@Transactional
public void cancelRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {}
Для реализации структуры tcc-транзакций давайте разберемся с некоторыми основными идеями. Фреймворк tcc-транзакции перехватывает через аспект @Compensable, который может прозрачно вызывать метод подтверждения/отмены участника, таким образом реализуя режим TCC. Здесь tcc-транзакция имеет два перехватчика, см. рис. 6-10.
-
org.mengyun.tcctransaction.interceptor.CompensableTransactionInterceptor, компенсируемый перехватчик транзакций.
-
org.mengyun.tcctransaction.interceptor.ResourceCoordinatorInterceptor, перехватчик координатора ресурсов.
Здесь нам нужно обратить особое внимание на контекст транзакции TransactionContext, потому что нам нужно передать транзакцию удаленному участнику в виде параметров при удаленном вызове участника сервиса. В tcc-транзакции транзакцияorg.mengyun.tcctransaction.TransactionМожет иметь несколько участниковorg.mengyun.tcctransaction.ParticipantУчаствуйте в деловых мероприятиях. Среди них номер транзакции TransactionXid используется для уникальной идентификации транзакции, которая генерируется с использованием алгоритма UUID для обеспечения уникальности. Когда участник совершает удаленный вызов, номер транзакции транзакции удаленного филиала равен номеру транзакции участника. С помощью метода подтверждения/отмены ассоциации номера транзакции TCC используйте номер транзакции участника для связи с транзакцией удаленного филиала, чтобы реализовать фиксацию и откат транзакции. Статус транзакции TransactionStatus включает в себя: статус попытки TRYING(1), статус подтверждения CONFIRMING(2), статус отмены CANCELING(3). Кроме того, тип транзакции TransactionType содержит: корневую транзакцию ROOT(1), ответвленную транзакцию BRANCH(2). При вызове TransactionManager#begin() для инициирования корневой транзакции используется тип MethodType.ROOT и вызывается метод попытки транзакции. Вызовите метод TransactionManager#propagationNewBegin(), чтобы распространить транзакцию ветвления. Метод вызывается, когда тип метода — MethodType.PROVIDER, и вызывается метод попытки транзакции. Вызовите метод TransactionManager#commit(), чтобы зафиксировать транзакцию. Этот метод вызывается, когда транзакция находится в методе подтверждения/отмены. Точно так же вызовите метод TransactionManager#rollback(), чтобы отменить транзакцию.
Кроме того, для механизма восстановления транзакций инфраструктура tcc-транзакций реализует планирование на основе Quartz и повторяет транзакцию с определенной частотой до тех пор, пока транзакция не будет завершена или не будет превышено максимальное количество повторных попыток. Если одна транзакция превышает максимальное количество повторных попыток, платформа tcc-транзакций не будет повторять попытку, и в это время требуется ручное вмешательство.
Здесь мы уделяем особое внимание идемпотентности операций. Ядром идемпотентного механизма является обеспечение уникальности ресурсов, например, повторная отправка или многократные повторные попытки на стороне сервера дадут только один результат. Сценарии оплаты, сценарии возврата и транзакции с деньгами не могут иметь несколько вычетов и другие проблемы. На самом деле интерфейс запроса используется для получения ресурсов, потому что он только запрашивает данные и не влияет на изменение ресурсов, поэтому сколько бы раз интерфейс не вызывался, ресурсы не изменятся, поэтому он идемпотентный. Недавно добавленный интерфейс не является идемпотентным, поскольку многократный вызов интерфейса приведет к изменению ресурсов. Поэтому нам нужно быть идемпотентными при наличии повторяющихся коммитов. Итак, как гарантировать идемпотентный механизм? На самом деле у нас много реализаций. Одним из решений является общее создание уникальных индексов. Создание уникального индекса в базе данных для полей ресурсов, которые нам нужно ограничить, может предотвратить вставку повторяющихся данных. Однако в случае подбазы данных и подтаблицы уникальный индекс не так прост в использовании.В настоящее время мы можем запросить базу данных один раз, а затем определить, дублируются ли ограниченные поля ресурсов, а затем выполнить операция вставки, когда нет дубликатов. Обратите внимание, что во избежание параллельных сценариев мы можем обеспечить уникальность данных с помощью механизмов блокировки, таких как пессимистическая блокировка и оптимистическая блокировка. Здесь часто используемой схемой является распределенная блокировка, которая обычно является реализацией пессимистической блокировки. Однако многие люди часто рассматривают пессимистичные блокировки, оптимистичные блокировки и распределенные блокировки как решения для идемпотентных механизмов, что неверно. Кроме того, мы также можем ввести конечный автомат и использовать конечный автомат для выполнения ограничений состояния и переходов между состояниями, чтобы обеспечить выполнение процесса одного и того же бизнеса, тем самым реализуя идемпотентность данных.
Режим компенсации
В предыдущем разделе мы упомянули механизм повторных попыток. На самом деле, это также решение для согласованности в конечном итоге: нам нужно изо всех сил стараться повторять попытки, чтобы гарантировать, что операция базы данных в конечном итоге обеспечит согласованность данных. соответствующие журналы Ручное вмешательство персонала. Обратите внимание, что вызываемый объект должен гарантировать свою идемпотентность. Механизм повтора может быть механизмом синхронизации, например, время ожидания вызова основной бизнес-службы истекло или неаномальный вызов завершился неудачей, и бизнес-вызов необходимо вовремя повторно инициализировать. Механизм повторных попыток можно грубо разделить на стратегии повторных попыток с фиксированным числом и стратегию повторных попыток с фиксированным временем. Кроме того, мы также можем использовать очередь сообщений и механизм задач по времени. Механизм повтора очереди сообщений, то есть, если сообщение не может быть использовано, оно будет доставлено повторно, чтобы избежать отбрасывания сообщения без использования.Например, RocketMQ может разрешить повторную попытку каждого сообщения. до 16 раз по умолчанию, а интервал между каждой повторной попыткой можно настроить. Для механизма повтора задач с заданным временем мы можем создать таблицу выполнения задач и добавить поле «количество повторов». В этой схеме проектирования мы можем получить, находится ли задача в состоянии сбоя выполнения и не превышает ли число повторных попыток при ее регулярном вызове, и если да, то повторить попытку при сбое. Однако, когда выполнение завершается со сбоем и количество повторных попыток превышает количество повторных попыток, это означает, что задача окончательно не удалась, и разработчикам необходимо вручную вмешаться и устранить проблему.
В дополнение к механизму повтора, его также можно исправить при каждом обновлении. Например, для сценариев подсчета, таких как количество лайков, избранных и комментариев социальных взаимодействий, данные могут быть несогласованными в течение определенного периода времени из-за дрожания сети или недоступности связанных сервисов Мы можем исправить это каждый раз она обновляется.Гарантируется, что данные в конечном итоге будут непротиворечивыми после короткого периода самовосстановления и коррекции системы. Следует отметить, что с этим решением, если часть данных несовместима, но не обновляется и не исправляется снова, это всегда будут ненормальные данные.
Регулярная корректура также является очень важным решением, которое гарантируется периодическими корректорскими операциями. Что касается выбора фреймворков для задач по времени, Quartz в автономных сценариях обычно используется в отрасли, а промежуточное ПО для распределенных задач по времени, такое как Elastic-Job, XXL-JOB и SchedulerX, — в распределенных сценариях. Существует два сценария проверки по времени. Один из них — незавершенная повторная попытка по времени. Например, мы используем задачу по времени для сканирования незавершенной вызывающей задачи и исправления ее с помощью механизма компенсации для достижения окончательной согласованности данных. Другой — проверка синхронизации, которая требует, чтобы основная бизнес-служба предоставила соответствующий интерфейс запроса подчиненной бизнес-службе для проверки и запроса на восстановление потерянных бизнес-данных. Теперь давайте представим возврат средств в сценарии электронной коммерции. В этом бизнесе возврата будет базовая служба возврата и служба автоматического возврата. В настоящее время служба автоматического возврата расширяет возможности возврата на основе базовой службы возврата, реализует автоматический возврат на основе нескольких правил и получает информацию о моментальном снимке возврата, отправленную базовой службой возврата через очередь сообщений. Однако несогласованность данных может возникнуть из-за потери сообщений, отправленных базовой службой возврата средств, или активного удаления очередей сообщений после повторяющихся сбоев и повторных попыток. Поэтому особенно важно восстанавливать потерянные бизнес-данные, регулярно проверяя и проверяя базовую услугу на предмет возмещения.
надежный шаблон событий
В распределенных системах очереди сообщений играют очень важную роль в архитектуре на стороне сервера, в основном касаясь таких сценариев, как асинхронная обработка, разделение системы и сглаживание пиков трафика. Если несколько систем взаимодействуют синхронно, легко вызвать блокировку, и в то же время эти системы будут связаны друг с другом. Поэтому вводится очередь сообщений, которая устраняет блокировку, вызванную механизмом синхронной связи, с одной стороны, и разъединяет бизнес через очередь сообщений, с другой стороны. См. Рисунок 6-12.
В режиме надежного события путем введения надежной очереди сообщений, поскольку текущая надежная доставка события гарантируется, а очередь сообщений гарантирует, что событие будет доставлено хотя бы один раз, потребители, подписавшиеся на это событие, могут гарантировать, что событие может быть доставлено. потребляться в рамках собственного бизнеса. Здесь читателям предлагается подумать, можно ли решить проблему, пока введена очередь сообщений? На самом деле, простое введение очереди сообщений не гарантирует ее конечной согласованности, потому что связь основана на сети в среде распределенного развертывания, и в процессе сетевой связи восходящие и нисходящие сообщения могут быть потеряны по разным причинам.
Во-первых, когда основная бизнес-служба отправляет сообщение, может произойти сбой, поскольку очередь сообщений недоступна. В этом случае мы можем позволить основной бизнес-службе (производителю) отправить сообщение, а затем сделать бизнес-звонок для проверки. Общая практика заключается в том, что основная бизнес-служба сохраняет сообщение для отправки в локальную базу данных, устанавливает флаг в состояние «для отправки», а затем отправляет сообщение в очередь сообщений.После того как очередь сообщений получает сообщение, она также сохраняет сообщение в свою службу хранения, не сразу доставляет сообщения ведомой бизнес-службе (потребителю), а сначала возвращает результат ответа очереди сообщений в основную бизнес-службу (производитель), а затем в основную бизнес-службу служба оценивает бизнес-обработку после выполнения результата ответа. Если ответ не получен, последующая бизнес-обработка прекращается, а состояние флага локального постоянного сообщения устанавливается в состояние «конец». В противном случае выполняется последующая бизнес-обработка, и состояние флага локального постоянного сообщения устанавливается в состояние «отправлено».
public void doServer(){
// 发送消息
send();
// 执行业务
exec();
// 更新消息状态
updateMsg();
}
Кроме того, после появления сообщения в очереди сообщений бизнес-служба (потребитель) может выйти из строя и не может быть использована. Большинство промежуточных программ сообщений, таких как RabbitMQ, RocketMQ и т. д., ввели механизм ACK для этой ситуации. Обратите внимание, что по умолчанию используется автоматический ответ, при котором очередь сообщений удалит сообщение из очереди сообщений сразу после отправки сообщения. Поэтому, чтобы обеспечить надежную доставку сообщения, мы используем ручной метод ACK.Если ACK не отправлен от бизнес-службы (потребителя) из-за простоя или по другим причинам, очередь сообщений повторно отправит сообщение, чтобы гарантировать достоверность сообщения. После обработки соответствующего бизнеса из бизнес-службы очередь сообщений уведомляется ручным ACK, и очередь сообщений удаляет постоянное сообщение из очереди сообщений. Затем, если очередь сообщений не может повторить попытку все время и не может быть доставлена, сообщение будет активно отброшено.Как мы можем решить эту проблему? Внимательные читатели могли заметить, что на предыдущем шаге основная бизнес-служба сохранила сообщение для отправки в локальную базу данных. Таким образом, после успешного использования из бизнес-службы он также отправляет уведомление в очередь сообщений, после чего становится производителем сообщений. После того, как основная бизнес-служба (потребитель) получает сообщение, она, наконец, помечает локальный постоянный статус сообщения как «завершенный». При этом читатели должны понимать, что мы используем «механизм прямого и обратного сообщения» для обеспечения надежной доставки событий в очередь сообщений. Конечно, механизмы компенсации также важны. Запланированная задача сканирует базу данных на наличие незавершенных сообщений в течение определенного периода времени и повторно доставляет их. См. Рисунок 6-13.
Обратите внимание, что, поскольку очередь сообщений может не получить результат обработки сообщения из-за тайм-аута обработки сообщения или простоя службы, а сеть может быть получена от бизнес-службы, доставка события надежна, а очередь сообщений гарантирует, что событие доставлен хотя бы один раз. Здесь подчиненный бизнес-сервис (потребитель) должен гарантировать идемпотентность. Если ведомый бизнес-сервис (потребитель) не гарантирует идемпотентность интерфейса, это приведет к аномальным сценариям, таким как повторная отправка. Кроме того, мы также можем независимо развернуть службу сообщений и совместно использовать службу сообщений в соответствии с различными бизнес-сценариями, чтобы снизить стоимость повторной разработки службы.
Теперь, когда мы понимаем методологию «Шаблона надежных событий», давайте рассмотрим реальный случай, чтобы углубить наше понимание. Во-первых, когда пользователь инициирует возврат, служба автоматического возврата получит сообщение о событии возврата. В это время, если возврат соответствует политике автоматического возврата, служба автоматического возврата сначала запишет в локальную базу данных для сохранения после этого возврата снимок, сообщение для выполнения возврата отправляется в очередь сообщений.После того, как очередь сообщений получает сообщение, она возвращает успешный результат ответа.Затем служба автоматического возврата может выполнить последующую бизнес-логику. В то же время очередь сообщений асинхронно доставляет сообщение базовой службе для возврата, а затем базовая служба для возврата выполняет свою собственную бизнес-логику.Неудачное выполнение или нет гарантируется базовой службой для возврата. выполнение выполнено успешно, отправляется возврат выполнения.Успешное сообщение отправляется в очередь сообщений. Наконец, запланированная задача сканирует базу данных на наличие незавершенных сообщений в течение определенного периода времени и повторно доставляет их. Здесь следует отметить, что постоянный моментальный снимок возврата автоматизированного сервиса возврата можно понимать как сообщение, которое необходимо успешно доставить, а «механизм прямого и обратного сообщения» и «временная задача» обеспечивают его успешную доставку. Кроме того, реальная логика учета возврата гарантируется базовой службой возврата, поэтому она должна обеспечивать идемпотентность и сходимость логики учета. Когда выполнение завершается со сбоем, а количество повторных попыток превышает количество повторных попыток, это означает, что задача окончательно не удалась, и разработчикам необходимо вручную вмешаться и устранить проблему. См. Рисунок 6-14.
Подводя итог, можно сказать, что введение очереди сообщений не гарантирует надежной доставки событий. Другими словами, потеря сообщений по разным причинам, таким как сеть, не может гарантировать их окончательную согласованность. используется механизм обратного сообщения. Очереди сообщений обеспечивают надежную доставку событий и используют механизм компенсации для максимально возможной повторной доставки сообщений, которые не были завершены в течение определенного периода времени.
Интерпретация реализации распределенных транзакций в проектах с открытым исходным кодом
Применение распределенных транзакций в проектах с открытым исходным кодом может многому научиться. В этом разделе мы будем интерпретировать его реализацию.
RocketMQ
Apache RocketMQ — это высокопроизводительное промежуточное ПО для распределенных сообщений с высокой пропускной способностью, исходный код которого открыт от Alibaba. Во время Double 11 за последние годы RocketMQ взял на себя весь информационный поток производственной системы Alibaba и имеет стабильную и отличную производительность в основной линии транзакций.Это один из основных базовых продуктов, несущих максимальную ценность транзакций. У RocketMQ также есть коммерческая версия MQ, которую можно приобрести в облаке Alibaba (Woohoo.Alibaba Cloud.com/product/ONS…
Apache RocketMQ версии 4.3 официально поддерживает сообщения распределенных транзакций. Дизайн сообщения транзакции RocketMQ в основном решает проблему атомарности между отправкой сообщения и выполнением локальной транзакции на стороне производителя.Другими словами, если выполнение локальной транзакции не удалось, отправка сообщения MQ не будет выполняться. Тогда у вас могут возникнуть вопросы, если вы умны: мы можем сначала выполнить локальную транзакцию, а затем отправить сообщение MQ после успешного выполнения, чтобы мы могли гарантировать транзакционность? Однако подумайте еще раз, что, если сообщение MQ не будет отправлено успешно? На самом деле, RocketMQ предлагает для этого хорошую идею и решение. RocketMQ сначала отправит сообщение перед выполнением в MQ и выполнит локальную транзакцию после успешной отправки сообщения перед выполнением. Затем он выполняет последующую логику выполнения в соответствии с результатом выполнения локальной транзакции.Если результатом выполнения локальной транзакции является фиксация, то сообщение MQ официально доставляется.Если результатом выполнения локальной транзакции является откат, MQ удаляет предварительное сообщение, доставленное ранее, и не доставляет его. Обратите внимание, что в нештатных ситуациях, таких как простои сервера или тайм-аут во время выполнения локальной транзакции, RocketMQ будет постоянно запрашивать статус у других производителей в той же группе. См. Рисунок 6-15.
Пока что мы поняли идею реализации RocketMQ, Если вас интересует реализация исходного кода, вы можете прочитатьorg.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#sendMessageInTransaction.
ServiceComb
ServiceComb с открытым исходным кодом основан на внутренней структуре CSE (Cloud Service Engine) Huawei.Он обеспечивает набор функций, включая генерацию структуры кода, обнаружение регистрации службы, балансировку нагрузки, надежность службы (отказоустойчивый прерыватель цепи, понижение ограничения тока, отслеживание цепочки вызовов). ) и другие функции фреймворка микросервисов. Среди них ServiceComb Saga — решение для окончательной согласованности данных для микросервисных приложений.
Saga разбивает распределенные транзакции на несколько локальных транзакций, которые затем координируются механизмом Saga. Если весь процесс завершается нормально, дело успешно завершено, если в ходе этого процесса в реализации происходит частичный сбой, движок Saga вызывает операцию компенсации. Saga имеет две стратегии восстановления: прямое восстановление и обратное восстановление. Среди них прямое восстановление делает все возможное, чтобы постоянно повторять попытки отказавшего узла, чтобы гарантировать, что работа базы данных может в конечном итоге обеспечить согласованность данных.Если повторная попытка завершается неудачей несколько раз, разработчик может быть активно уведомлен в соответствии с соответствующим журналом. для ручного вмешательства. Обратное восстановление выполняет операцию отката транзакции на всех предыдущих успешных узлах, чтобы гарантировать, что данные достигают согласованного эффекта.
Разница между Saga и TCC заключается в том, что в Saga на одну операцию Try меньше, чем в TCC. Таким образом, Saga будет отправлена непосредственно в базу данных, а затем, когда произойдет сбой, будет выполнена операция компенсации. Дизайн Saga может привести к более сложным компенсационным действиям в экстремальных сценариях, но для простой бизнес-логики он менее навязчив, легче и сокращает количество коммуникаций, см. рис. 6-16.
ServiceComb Saga расширяет свою теоретическую основу двумя компонентами: альфа и омега. Альфа выступает в роли координатора, в основном ответственного за постоянное хранение событий транзакции и координацию состояния подтранзакций, чтобы оно окончательно согласовывалось с состоянием глобальной транзакции. omega — агент, встроенный в микросервисы, отвечающий за перехват сетевых запросов и сообщение о событиях транзакций в alpha, а также выполнение соответствующих компенсационных операций по инструкциям, выдаваемым alpha в нештатных ситуациях. На этапе предварительной обработки альфа записывает событие начала транзакции, а на этапе постобработки альфа записывает событие завершения транзакции. Следовательно, каждая успешная подтранзакция имеет взаимно однозначно соответствующие начальное и конечное события. На стороне поставщика услуг omega перехватывает связанный с транзакцией идентификатор в запросе для извлечения контекста транзакции. На стороне потребителя услуги omega вставит идентификатор, связанный с транзакцией, в запрос для передачи контекста транзакции. Благодаря этой совместной обработке поставщиков услуг и потребителей услуг подтранзакции могут быть связаны для формирования полной глобальной транзакции. Обратите внимание, что Saga требует, чтобы связанные подтранзакции предоставляли методы обработки транзакций и функции компенсации. Здесь добавьте аннотацию @EnableOmega, чтобы инициализировать конфигурацию омеги и установить соединение с альфой. Добавьте аннотацию @SagaStart к начальной точке глобальной транзакции и добавьте аннотацию @Compensable к подтранзакции, чтобы указать соответствующий метод компенсации. Случаи применения:GitHub.com/Apache/CailV…
@EnableOmega
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SagaStart
public void xxx() { }
@Compensable
public void transfer() { }
Теперь давайте взглянем на его схему бизнес-процессов, см. рис. 6-17.
Еще больше интересных статей в разделе «Серверное мышление»!