Авторские права принадлежат автору Для любой формы перепечатки, пожалуйста, свяжитесь с автором для получения разрешения и укажите источник.
Введение в распределенные транзакции
Транзакции, реализованные в распределенных системах, являются распределенными транзакциями Принципы CAP распределенных систем:
- последовательность
- Доступность
- Допуск перегородки
Распределенные транзакции в основном обеспечивают согласованность данных, есть три разных принципа
- сильная консистенция
- слабая консистенция
- возможная согласованность
JTA и ХА
Общая основа:
- Менеджер транзакций
- XA Resource
- двухэтапная фиксация
Другой пример - после успешного списания комиссии сервисом Пользователя он пишет сообщение в очередь тикетов переноса нового заказа.В это время сервис Билет обрабатывается или обработка не проходит.Во время этого процесса пользователь проверяет свой баланс и комиссия была успешно списана, но информация о билете отсутствует.В это время вы можете использовать метод сбоя транзакции и отката для отката один за другим, что называется слабой согласованностью, или вы можете отправить содержимое сбой обработки в очередь ошибок и решить ее путем ручной обработки, которая называется согласованностью в конечном счете.
Реализация распределенной транзакции Spring JTA
- Вы можете использовать диспетчер транзакций JTA, предоставляемый сервером приложений, таким как JBoss.
- Можно использовать менеджеры транзакций JTA, предоставляемые такими библиотеками, как Atomikos и Bitronix.
Реализация распределенной транзакции без Spring JTA
Почему бы не использовать JTA?
Поскольку JTA использует двухэтапный метод подачи документов, первый этап является предварительным, а второй — официальным. Когда при первом подтверждении происходит ошибка, вся транзакция откатывается, и транзакция может занять много времени, поскольку она должна работать с несколькими базами данных и несколькими ресурсами данных, поэтому пропускная способность может быть низкой по производительности.
Не применяйте JTA, отправьте две транзакции по очереди
1.start message transaction
2.receive message
3.start database transaction
4.update database
5.commit database transaction
6.commit message transaction ##当这一步出现错误时,上面的因为已经commit,所以不会rollback
Тогда будет проблема
Метод синхронизации транзакций для нескольких ресурсов
XA и финальная ресурсная игра
1.start message transaction
2.receive message
3.start JTA transaction on DB
4.update database
5.phase-1 commit on DB transaction
6.commit message transaction ##当这一步出现错误时,上面的因为是XA的第一次提交预备状态,所以可以rollback
7.phase-2 commit on DB transaction ##当这一步出现错误时,因为message不是XA方式,commit后无法rollback
Однако по сравнению с отказом от использования JTA возможности ошибок транзакций в значительной степени удалось избежать.
Поделиться ресурсом
- Две источники данных имеют одинаковый базовый ресурс
- Например, ActiveMQ использует БД в качестве базового хранилища ресурсов.
- Используйте менеджер транзакций базы данных базы данных для управления фиксацией транзакций.
- Поддержка источника данных требуется для указания базового метода хранения ресурсов.
сделать все возможное
- Совершайте транзакции последовательно
- может пойти не так
- Прямая синхронизация транзакций через AOP или Listener
Лучшее усилие JMS: одна фиксация + повторная попытка
- Применяется, когда одним из источников данных является MQ и транзакция начинается с чтения сообщений MQ.
- Используйте механизм повтора сообщений MQ
- Дублирующиеся сообщения необходимо учитывать при повторной попытке
1.start message transaction
2.receive message
3.start database transaction
4.update database #数据库操作出错,消息被放回MQ队列,重试重新触发该方法
5.commit database transaction
6.commit message transaction
С вышеперечисленным проблем нет
1.start message transaction
2.receive message
3.start database transaction
4.update database
5.commit database transaction
6.commit message transaction #提交MQ事务出错,消息放回至MQ队列,重试重新触发该方法
Могут возникнуть проблемы: операции с базой данных будут повторяться, так как транзакция базы данных не использует управление транзакциями JTA, поэтому база данных была успешно зафиксирована; как этого избежать, нужно игнорировать сообщения повторной передачи, такие как проверка уникальности и другие средства.
управление цепочкой транзакций
- определить цепочку транзакций
- Несколько транзакций последовательно фиксируются в диспетчере транзакций
- может пойти не так
Как выбрать (согласно требованиям соответствия)
- Строго согласованные транзакции: JTA (наихудшая производительность, только в пределах одного сервиса)
- Слабые, в конечном итоге непротиворечивые транзакции: одна фиксация с максимальной эффективностью, цепочки транзакций (разработайте соответствующие механизмы обработки ошибок)
Как выбрать (по сценарию)
- MQ-DB: лучшая попытка, одна фиксация + повторная попытка
- Несколько БД: управление цепочками транзакций
- Несколько источников данных: связанные транзакции или другие методы синхронизации транзакций.
пример
Пример 1-БД-БД
Существует два источника данных, настроенные в Applications.Properties
# 默认的Datasource配置
# spring.datasource.url = jdbc:mysql://localhost:3307/user
# spring.datasource.username = root
# spring.datasource.password = 123456
# spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.ds_user.url = jdbc:mysql://localhost:3307/js_user
spring.ds_user.username = root
spring.ds_user.password = 123456
spring.ds_user.driver-class-name = com.mysql.jdbc.Driver
spring.ds_order.url = jdbc:mysql://localhost:3307/js_order
spring.ds_order.username = root
spring.ds_order.password = 123456
spring.ds_order.driver-class-name = com.mysql.jdbc.Driver
Пользовательский файл класса конфигурации
@Configuration
public class DBConfiguration{
@Bean
@Primary
@ConfigurationProperties(prefix="spring.ds_user") #设置读取在properties文件内容的前缀
public DataSourceProperties userDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@Primary
public DataSource userDataSource(){
return userDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
@Bean
public JdbcTemplate userJdbcTemplate(@Qualifier("userDataSource") DataSource userDataSource){
return new JdbcTemplate(userDataSource);
}
@Bean
@ConfigurationProperties(prefix="spring.ds_order") #设置读取在properties文件内容的前缀
public DataSourceProperties orderDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public DataSource orderDataSource(){
return userDataSourceProperties().initializeDataSourceBuilder().type(HikariDAtaSource.class).build();
}
@Bean
public JdbcTemplate orderJdbcTemplate(@Qualifier("orderDataSource") DataSource orderDataSource){
return new JdbcTemplate(orderDataSource);
}
}
Объяснение аннотации Spring (@Primary, @Qualifier)
фактический вызывающий класс
public class CustomerService{
@Autowired
@Qualifier("userJdbcTemplate")
private jdbcTemplate userJdbcTemplate;
@Autowired
@Qualifier("orderJdbcTemplate")
private jdbcTemplate orderJdbcTemplate;
private static final String UPDATE_CUSTOMER_SQL;
private static final String INSERT_ORDER_SQL;
@Transactional #事务管理注解
public void createOrder(Order order){
userJdbcTemplate.update(UPDATE_CUSTOMER_SQL, order)
if(order.getTitle().contains("error1")){ #模拟异常出现
throw new RuntimeException("error1")
}
orderJdbcTemplate.update(INSERT_ORDER_SQL, order) #没有使用事务,直接提交
if(order.getTitle().contains("error2")){ #模拟异常出现
throw new RuntimeException("error2")
}
}
}
Подробности о вышеуказанном процессе:
Из-за использования метки @Transactional выполняется в транзакции
Специальное примечание: @Transactional Если настройка не выполняется, будет использоваться DataSource под аннотацией @Primart в классе DBConfiguration, и он будет использоваться для подключения к источнику данных.
spring DataSourceUtilsисходный код
Spring DataSourceUtils использует существующее соединение, но контролирует только выпуск соединения с базой данных, а не транзакцию.
Пример 2-DB-DB.Chained Менеджер транзакций
Цепной менеджер транзакций находится в этой библиотеке.
Добавьте абзац в класс DBConfiguration@Bean
public PlatformTransactionManager transactionManager(){
DataSourceTransactionManager userTM = new DataSourceTransactionManager(userDataSource()) #看似方法调用,实则从spring容器中获取
DataSourceTransactionManager orderTM = new DataSourceTransactionManager(orderDataSource())
# orderTM.setDataSource(orderDataSource()) 如果使用这种方式则不是从容器中去获取了,因为orderTM不是spring容器管理
ChainedTransactionManager tm = new ChainedTransactionManager(userTM, orderTM) ## order先执行,user后执行
return tm;
}
Сцепление менеджеров транзакций
Есть ли проблема с исключениями?
- Используйте работу моделирования Debug Mode, первый порядок после совершения транзакции, когда второе пользовательские службы выполнения транзакций для MySQL остановлены, появляется как исключениеПерезапуск службы msyql, программа продолжает работать, в это время в таблице заказов БД есть еще одна запись, но пользовательская таблица не изменилась, первая транзакция заказа не откатилась, если служба mysql остановился при откате, по сути не имеет никакого эффекта, потому что сам по себе коммит отсутствует, а выполнен ли откат или нет, не имеет никакого значения.
Пример 3-JPA-DB.Chained Transaction Manager
- mysql + mysql
- Связанные транзакции: JpaTransactionManager + DataSourceTransactionMananger
- Не обрабатывать повторы
Продолжайте вносить изменения на основе примеров основного кода, демонстрирующих 1:
Пример 4 - JMS-DB. Best Effort One Commit
- JMS-DB
- ActiveMQ + Mysql
- Лучшее усилие с одной фиксацией: TransactionAwareConnectionFactoryProxy
Уникальность распределенной системы
Что такое идентификатор распределенной системы?
- Глобальный уникальный идентификатор для распределенных систем
- UUID: Спецификация для создания уникальных идентификаторов.
- Используется для уникальной идентификации при работе с повторяющимися сообщениями.
Стратегия генерации уникального идентификатора распределенной системы:
- последовательность автоинкремента базы данных
- UUID: Стандарт уникального идентификатора, 128 бит, несколько методов генерации (время + версия и т. д.)
- ObjectID MongoDB: метка времени + идентификатор машины, идентификатор процесса, серийный номер
- Операция INCR Redis, номер версии узла Zookeeper
Какой метод использовать?
- Самоувеличивающийся идентификатор: необходимо учитывать безопасность, развертывание
- Временной порядок: легко судить о времени создания по ID
- Длина, цифровой ли тип: индексировать ли
Распределенные системы Распределенные объекты
- Redis: библиотека Redisson: такие объекты, как RLock, RMap, RQueue и т. д.
- Zookeeper: библиотека Netflix Curator: такие объекты, как Lock, Queue и т. д.
Режим реализации распределенной транзакции
- Режим управления сообщениями: Управление сообщениями
- Режим поиска событий: Источник событий
- Режим TCC: ПОПРОБОВАТЬ-Подтвердить-Отмена
идемпотентность
- Идемпотентная операция: эффект от любого количества выполнений такой же, как эффект от одного выполнения
- Идемпотентность метода: многократный вызов метода с одними и теми же параметрами дает тот же результат, что и однократный вызов.
- Идемпотентность интерфейса: интерфейс вызывается многократно, а результат один и тот же
Идемпотентность интерфейсов микросервисов
- Важность. Часто требуются повторные попытки для достижения конечной согласованности распределенных транзакций.
- Получить метод не производит побочные эффекты на систему, idempotent
- Метод POST, PUT, DELETE реализован для удовлетворения потребностей идемпотента.
Сервисный метод реализует идемпотентность
public OrderService{
Map disMap; # 用于存放已经处理的id
@Transactional
void ticketOrder(BuyTickerDTO dto){
String uid = createUUID(dto); # 创建并获取数据的唯一id
if(!diMap.contains(uuid){ #disMap还没有处理过这个数据唯一id,则进入创建
Order order = createOrder(dto);
disMap.append(uid) ## 追加Map
}
}
userService.charge(dto); #调用user微服务
}
SQL реализует идемпотентность
#通过调节限定,只有第一次支付的时候才会扣余额,被重复调用的时候就不会重复扣费用,通过paystatus判断
UPDATE customer SET deposit = deposit - ${value}, paystatus = 'PAID' WHERE orderId = ${id} and paystatus = 'UNPAID'