1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-EasyTransaction

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
readme-en.md

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

Русский

(Русский не является моим родным языком, может кто-то поможет проверить текст ниже?)

Происхождение

Этот фреймворк вдохновлён презентацией "<Обработка распределённых транзакций в масштабах крупной системы SOA>" авторства Чэнь Ляо, работающего в Alibaba.

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

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

Характеристики:* Фреймворк объединяет все виды транзакционных шаблонов.

  • Можно использовать несколько транзакционных шаблонов одновременно.
  • Высокая производительность.
    • Большинство мобильных систем ограничиваются базами данных,
    • а дополнительное потребление фреймворка к базе данных составляет 25 байт строки (если реализация бесперебойности фреймворка не активирована).
  • Опциональная реализация бесперебойности и гарантии последовательности вызова
    • Это значительно снижает затраты на бизнес-разработку,
    • но при активации записывается строка контроля бесперебойности в базу данных.
  • Может быть полностью декомпозирован в бизнес-коде, без внедрения классов фреймворка в бизнес-код (за исключением конфигурационного кода).
  • Поддерживает вложенные транзакции.
  • Без дополнительной установки координаторов, они находятся внутри сервисного приложения,
    • можно также выделить координаторы из сервисного приложения.
  • Распределённый идентификатор транзакции может быть связан с бизнес-идентификатором, типом бизнеса и идентификатором приложения, что удобно для мониторинга выполнения распределённых транзакций различных услуг.## Распределённые сценарии транзакций и соответствующие реализации фреймворкаРаспределенные бизнес-сценарии*
  • отсутствие распределённых транзакций.
    • наиболее часто используемые
    • первоочередные
  • использование очереди сообщений для достижения со временем согласованности
    • применимо к бизнесу, который может самостоятельно принимать решение о состоянии глобальной транзакции (подтверждение/откат) без участия других сервисов
    • наиболее часто используемые
    • если вы должны взаимодействовать с другими сервисами через интерфейсы записи, приоритет следует отдавать использованию очередей сообщений
    • в зависимости от того, гарантируется ли доставка сообщений подписчикам, различаются надёжная доставка информации и лучшие усилия доставки сообщений
  • шаблон компенсации
    • применим к бизнесу, требующему удалённого выполнения операций для определения состояния глобальной транзакции, и удалённое выполнение позволяет выполнять компенсацию
    • распространённый
    • приоритет отдается транзакциям со временной согласованностью на основе компенсации, если есть нерешаемые проблемы с использованием очередей сообщений
    • в этом фреймворке шаблон компенсации не поддерживает вложенные транзакции, вместо этого можно использовать TCC, когда требуется поддержка вложенных транзакций
  • шаблон TCC * применим к бизнесу, требующему удаленного выполнения операций для определения состояния глобальной транзакции, и удалённое выполнение не позволяет выполнять компенсацию
    • наименее распространенный
    • окончательное решение охватывает все сценарии, которые обязательно должны быть реализованы с помощью 2PC
    • максимальное количество кода и максимальное потребление производительности делают его возможным избежать использования
  • шаблон SAGA
    • аналогичен шаблону компенсации
    • различие между шаблоном компенсации и SAGA заключается в том, что подтранзакции рабочего/глубинного уровня не выполняются во время локальной транзакции основного сервиса
    • SAGA выполняется асинхронно через очередь, тем самым снижая время блокировки записей основной транзакции
    • когда много синхронных вызовов (например, TCC, компенсация) присутствуют в основной транзакции, SAGA может быть использован вместо синхронных вызовов для повышения производительности
    • при использовании SAGA стоит обратить внимание (просто посмотрите демонстрацию), поскольку основная транзакция будет разделена на две части
    • SAGA, реализованный в данном фреймворке, это не традиционный SAGA
    • различие между традиционным SAGA и данным SAGA можно сравнить с отношением между традиционной компенсацией и TCC * и фреймверк использует RPC вместо очередей для реализации SAGA по причинам, которые можно увидеть в демонстрации SAGA-TCC.### Соответствующее реализация и базовые принципы фреймверкаФреймворк реализует все виды транзакционных шаблонов, упомянутых выше, и предоставляет унифицированный и удобный в использовании интерфейс. В следующем разделе представлены основные принципы.

Без распределённой транзакции

по аналогии с традиционной локальной транзакцией, EasyTransaction полностью не вмешивается

Другие распределённые транзакции

Основная зависимость фреймворка — это класс TransactionSynchronization библиотеки Spring. EasyTransaction можно использовать, если менеджер транзакций наследуется от AbstractPlatformTransactionManager (в основном все менеджеры транзакций наследуются от этого исполнения). Кроме того, начиная с версии 1.0.0, фреймворк использует возможности конфигурации Spring Boot и функции JDK8, поэтому Spring Boot и JDK8 также являются обязательными компонентами.

Для распределённых транзакций фреймворк внедряет соответствующие операции фреймворка в TransactionSynchronization перед вызовом метода удалённого транзакта, например:

  • при использовании надёжных сообщений, фреймворк отправляет сообщения после подтверждения состояния глобальной транзакции, обеспечивая, что сообщения могут быть видны внешним системам после подтверждения состояния глобальной транзакции, снижая нагрузку на программирование паттерна отправки-подтверждения-детектирования;

  • при использовании TCC, фреймворк вызывает методы Confirm или Cancel в зависимости от состояния глобальной транзакции.Фреймворк имеет фоновые потоки, ответственные за восстановление после аварийного завершения (например, для выполнения подтверждения или отката в TCC) на основе принципов «запись логов до выполнения распределённого вызова службы» и «запись данных транзакции для определения состояния глобальной транзакции».Фреймворк также имеет (опциональную) реализацию идемпотентности, которая гарантирует, что бизнес-методы логически выполняются только один раз (возможно, несколько раз, но повторно выполненные методы будут откатываться, так что бизнес-программы должны контролировать свою идемпотентность при работе с нероллабельными внешними ресурсами). Фреймворк также управляет порядком вызова методов, например, в шаблоне компенсации:

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

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

Результаты всех удалённых вызовов возвращаются в виде объекта Future, что даёт фреймворку пространство для оптимизации производительности, а все логи не будут записываться до попытки получения первого результата. Как только бизнес-программа пытается получить результат выполнения, она пишет логи в больших объёмах и последовательно запускает удалённые методы параллельно.Фреймворк проверяет наличие исключения в удалённых вызовах перед COMMIT. Если удалённый метод выбрасывает исключение, фреймворк откатывает бизнес-операцию до коммита. Это обеспечивает простоту модели программирования и облегчает правильность и понятность кода.

Введение в кодирование

Код

Бизнес-код может использовать EasyTransaction с помощью Maven:

  <dependency>
    <groupId>com.yiqiniu.easytrans</groupId>
    <artifactId>easytrans-starter</artifactId>
    <version>1.1.3</version>
  </dependency>

Этот Starter содержит несколько стандартных реализаций, включая: распределённый журнал транзакций на основе RDBS, реализацию HTTP RPC на основе Netflix-Ribbon, очередь на основе Kafka. Если вы хотите заменить эти реализации, просто исключите их.

Инициатор бизнес-процесса

Для инициаторов бизнес-процесса фреймворк предоставляет только один интерфейс.

public interface EasyTransFacade {
/**
 * Начать простую транзакцию
 * @param busCode appId+busCode+trxId является глобально уникальным идентификатором, независимо от того, будет ли эта транзакция завершена или отменена
 * @param trxId см busCode
 */
void startEasyTrans(String busCode, String trxId);

/**
 * Выполнить удалённую транзакцию
 * @param params
 * @return
 */
<P extends EasyTransRequest<R, E>, E extends EasyTransExecutor, R extends Serializable> Future<R> execute(P params);
}

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

@Transactional
public int покупатьЧто(int userId, long деньги) {
@Transactional
public int покупать_что(int userId, long деньги) {
```    /**
     * завершите локальную транзакцию сначала, чтобы повысить производительность и создание бизнес-идентификатора
     */
    Integer id = saveOrderRecord(jdbcTemplate, userId, деньги);

    /**
     * аннотировать глобальный транзакционный идентификатор, он объединяет appId + business_code + id
     * эта строка кода может быть опущена, тогда фреймворк будет использовать "по умолчанию" как бизнес-код, и сгенерирует идентификатор
     * но это сделает нам труднее связывать глобальную транзакцию с конкретной бизнес-операцией
     */
    transaction.startEasyTrans(BUSINESS_CODE, id);

    /**
     * вызвать удалённый сервис для списания средств, это TCC сервис,
     * фреймворк будет поддерживать конечную согласованность на основе окончательного статуса транзакции метода покупатьЧто
     * если вы считаете введение объекта транзакции (EasyTransFacade) недопустимым связыванием
     * то вы можете обратиться к другому демонстрационному примеру (interfacecall) в директории demos, он покажет вам, как выполнять транзакцию через пользовательски определенный интерфейс
     */
    WalletPayRequestVO списатьЗапрос = new WalletPayRequestVO();
    списатьЗапрос.setUserId(userId);
    списатьЗапрос.setPayAmount(деньги);

    /**
     * вернуть будущее для повышения производительности (пакетное выполнение записи журнала и пакетное выполнение RPC)
     */
    Future<WalletPayResponseVO> списатьБудущее = transaction.execute(списатьЗапрос);```markdown
/**
 * Опубликовать сообщение при подтверждении этой глобальной транзакции,
 * так что другие службы, подписанные на это событие, смогут получить это сообщение.
 */
OrderFinishedMessage заказЗавершенСообщение = new OrderFinishedMessage();
заказЗавершенСообщение.setUserId(userId);
заказЗавершенСообщение.setOrderAmt(деньги);
transaction.execute(заказЗавершенСообщение);

/**
 * Вы можете добавить больше типов транзакций здесь, например SAGA-TCC、Компенсация и т.д.
 * Фреймворк будет поддерживать конечную согласованность.
 */

/**
 * Мы можем получить результат удалённого сервиса, чтобы определить, следует ли подтвердить эту транзакцию.
 *
 * списать_будущее.get();
 */
return id;
}

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

Например, в TCC требуется реализовать метод TccMethod:

@Component
public class WalletPayTccMethod implements TccMethod<WalletPayTccMethodRequest, WalletPayTccMethodResult> {

    @Resource
    private WalletService walletService;

    @Override
    public WalletPayTccMethodResult doTry(WalletPayTccMethodRequest param) {
        return walletService.doTryPay(param);
    }
    
    // Другие методы TccMethod...
}
``````java
@Override
public void doConfirm(WalletPayTccMethodRequest param) {
    walletService.doConfirmPay(param);
}

@Override
public void doCancel(WalletPayTccMethodRequest param) {
    walletService.doCancelPay(param);
}

WalletPayTccMethodRequest — это параметр запроса, который является простым объектом JavaBeans (POJO), наследуемым от класса EasyTransRequest. Для его использования требуется добавить аннотацию BusinessIdentifier, которая указывает фреймворку значения AppID и бизнес-кода для данного запроса:

@BusinessIdentifier(appId = Constant.APPID, busCode = METHOD_NAME)
public class WalletPayTccMethodRequest implements TccTransRequest<WalletPayTccMethodResult> {
    private static final long serialVersionUID = 1L;

    private Integer userId;

    private Long payAmount;

    public Long getPayAmount() {
        return payAmount;
    }

    public void setPayAmount(Long payAmount) {
        this.payAmount = payAmount;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }
}

Приведённый выше пример представляет собой традиционную форму вызова. Форма декомпозиции бизнес-логики выглядит следующим образом. Для более конкретного использования обратитесь к демонстрационному примеру (interfacecall):

@Transactional
public String buySomething(int userId, long money) {

    int id = saveOrderRecord(jdbcTemplate, userId, money);

    // WalletPayRequestVO просто должен реализовать интерфейс Serializable
    WalletPayRequestVO request = new WalletPayRequestVO();
    request.setUserId(userId);
    request.setPayAmount(money);

    // payService — это сгенерированный фреймворком объект пользовательского интерфейса без каких-либо родительских классов
    WalletPayResponseVO pay = payService.pay(request);
}
```    return "id:" + id + " заморожено:" + pay.getFreezeAmount();
}

Дополнительные примеры

Пожалуйста, обратитесь к директории easytrans-demo. Для более полной конфигурации и использования обратитесь к тестовым данным easytrans-starter.

Конфигурация

Каждая база данных предприятия должна добавить две таблицы.

-- Для записи того, находится ли глобальное состояние транзакции
-- Поля начинаются с 'p_', что представляет собой идентификатор родительской транзакции, соответствующий этой транзакции.
-- Когда выполняется select for update, если запись с соответствующим идентификатором транзакции отсутствует, то транзакция должна завершиться ошибкой.
-- Если запись существует, но статус равен 0, это указывает на успешное выполнение транзакции, а значение 1 указывает на её провал (включая родительскую транзакцию и эту транзакцию).
-- Если запись существует, но статус равен 2, это указывает на неопределенное окончательное состояние транзакции.
CREATE TABLE `executed_trans` (
  `app_id` smallint(5) unsigned NOT NULL,
  `bus_code` smallint(5) unsigned NOT NULL,
  `trx_id` bigint(20) unsigned NOT NULL,
  `p_app_id` smallint(5) unsigned DEFAULT NULL,
  `p_bus_code` smallint(5) unsigned DEFAULT NULL,
  `p_trx_id` bigint(20) unsigned DEFAULT NULL,
  `status` tinyint(1) NOT NULL,
  PRIMARY KEY (`app_id`, `bus_code`, `trx_id`),
  KEY `parent` (`p_app_id`, `p_bus_code`, `p_trx_id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_bin;
```CREATE TABLE `idempotent` (
  `src_app_id` smallint(5) unsigned NOT NULL COMMENT 'источник AppID',
  `src_bus_code` smallint(5) unsigned NOT NULL COMMENT 'источник бизнес-кода',
  `src_trx_id` bigint(20) unsigned NOT NULL COMMENT 'источник транзакционного идентификатора',
  `app_id` smallint(5) NOT NULL COMMENT 'вызованный APPID',
  `bus_code` smallint(5) NOT NULL COMMENT 'вызовной бизнес-код',
  `call_seq` smallint(5) NOT NULL COMMENT 'последовательность вызова одного и того же бизнес-кода в рамках одной глобальной транзакции',
  `handler` smallint(5) NOT NULL COMMENT 'идентификатор приложения обработчика',
  `called_methods` varchar(64) NOT NULL COMMENT 'вызванные методы',
  `md5` binary(16) NOT NULL COMMENT 'MD5 параметров запроса',
  `sync_method_result` blob COMMENT 'результат вызова бизнес-логики',
  `create_time` datetime NOT NULL COMMENT 'время выполнения',
  `update_time` datetime NOT NULL,
  `lock_version` smallint(32) NOT NULL COMMENT 'версия блока',
  PRIMARY KEY (`src_app_id`, `src_bus_code`, `src_trx_id`, `app_id`, `bus_code`, `call_seq`, `handler`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;(Лог распределённой транзакции на основе RDBMS; если вы используете Redis, то это не обязательно.) Вам потребуется база данных RDBMS для записи логов транзакций и создание двух таблиц для неё. Каждый бизнес-сервис должен иметь базу данных логов транзакций. Несколько сервисов могут использовать одну базу данных логов транзакций. Создание таблицы `trans_log_detail`:

```sql
CREATE TABLE `trans_log_detail` (
  `log_detail_id` int(11) NOT NULL AUTO_INCREMENT,
  `trans_log_id` binary(12) NOT NULL,
  `log_detail` blob,
  `create_time` datetime NOT NULL,
  PRIMARY KEY (`log_detail_id`),
  KEY `app_id` (`trans_log_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

Создание таблицы trans_log_unfinished:

CREATE TABLE `trans_log_unfinished` (
  `trans_log_id` binary(12) NOT NULL,
  `create_time` datetime NOT NULL,
  PRIMARY KEY (`trans_log_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Запрос данных из таблицы:

SELECT * FROM translog.trans_log_detail;

Расширение

Фреймворк использует интерфейсы для соединения каждого модуля, что делает его гибко расширяемым. В следующих модулях рекомендовано расширяться:* Реализация RPC * Поддержка Alibaba DUBBO, SPRING CLOUD RIBBON/EUREKA в настоящий момент. * Добро пожаловать к дополнительной реализации.

  • Реализация очереди сообщений
    • Поддержка Alibaba ONS в настоящий момент (используется непотоковый режим при создании) и KAFKA.
    • Добро пожаловать к дополнительной реализации и требуются два основных подхода.
  • Реализация сериализации
    • Сериализация, предоставляемая Spring-core.
    • Добро пожаловать к дополнительной реализации для повышения эффективности.
  • Реализация фильтров
    • Есть атомарные фильтры, фильтры установки метаданных и фильтры помощи с вложенным транзактом.
    • Новый фильтр может быть добавлен, если есть дополнительные требования.
  • Реализация выборщика источников данных
    • В настоящий момент предоставляется выборщик одного источника данных.
    • Если сервис имеет несколько источников данных, ему требуется реализовать бизнес-зависимый выборщик источника данных и выбирать источник данных в соответствии с запросом.
  • Реализация журналов выполнения транзакций
    • Реализация баз данных отношений и REDIS доступна в настоящий момент.
    • Для повышения эффективности можно реализовать журналы транзакций на основе других форм, таких как файловые системы, HBASE и т. д. Добро пожаловать к предложениям.
  • Реализация выборщика главного и вторичного сервера * Реализация выборщика главного и вторичного сервера на основе ZK.
    • Если вы не хотите использовать ZK, вы можете заменить его самостоятельно.## Лучшие практики

Журналы транзакций на основе базы данных

  • Используйте различные серверы для базы данных журнала транзакций и базы данных бизнеса.

Параметры и значения возврата

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

Часто задаваемые вопросы

  1. Как определить, был ли глобальный транзакт завершен после сбоев?

    • При вызове метода startEasyTrans() фреймворк вносит запись в таблицу executed_trans.
    • Отдалённый транзакт может быть выполнен только после того, как будет вызван метод startEasyTrans().
    • Инициатор бизнес-транзакта (мастер-транзакт) удерживает блокировку записи executed_trans до тех пор, пока мастер-транзакт не откатится или не завершится.
    • Поэтому при восстановлении после сбоев процесс CRASH использует select for update, чтобы выбрать запись executed_trans. Это гарантирует получение точной информации о том, был ли транзакт завершен (если мастер-транзакт всё ещё выполняется, то select for update будет ждать).
    • Используйте select for update, чтобы избежать ошибочного запроса состояния глобального транзакта в модели многопользовательской версионной системы управления базами данных (MVCC).

Другое

Публичный аккаунт WeChat автора

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

Электронная почта: skyes.xu@qq.com

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Описание недоступно Развернуть Свернуть
Apache-2.0
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/mirrors-EasyTransaction.git
git@api.gitlife.ru:oschina-mirror/mirrors-EasyTransaction.git
oschina-mirror
mirrors-EasyTransaction
mirrors-EasyTransaction
master