Адрес фактического центра электронной коммерции SpringBoot (25k+star):GitHub.com/macro-positive/…
Резюме
Seata – это решение для распределенных транзакций с открытым исходным кодом от Alibaba. Оно стремится предоставлять высокопроизводительные и простые в использовании сервисы распределенных транзакций. В этой статье будет подробно рассказано о его использовании с помощью простого бизнес-сценария размещения заказа.
В чем проблема распределенных транзакций?
Монолитное приложение
В монолитном приложении бизнес-операция должна быть завершена вызовом трех модулей, а согласованность данных гарантируется локальными транзакциями.
Приложение микросервиса
С изменением бизнес-требований монолитное приложение разбивается на микросервисные приложения, а исходные три модуля разбиваются на три независимых приложения, каждое из которых использует независимые источники данных, а для завершения бизнес-операций необходимо вызывать три службы. В настоящее время согласованность данных в каждой службе гарантируется локальными транзакциями, но проблема согласованности глобальных данных не может быть гарантирована.
резюме
В микросервисной архитектуре проблема, заключающаяся в том, что глобальная согласованность данных не может быть гарантирована, связана с проблемой распределенных транзакций. Короче говоря, бизнес-операция должна работать с несколькими источниками данных или совершать удаленные вызовы, что вызовет проблемы с распределенными транзакциями.
Знакомство с Seata
Seata — это решение для распределенных транзакций с открытым исходным кодом, предназначенное для предоставления высокопроизводительных и простых в использовании сервисов распределенных транзакций. Seata предоставит пользователям режимы транзакций AT, TCC, SAGA и XA, создав универсальное распределенное решение для пользователей.
Принцип и дизайн Seata
определить распределенную транзакцию
Мы можем понимать распределенную транзакцию как глобальную транзакцию, которая включает в себя несколько ветвей транзакций.Обязанность глобальной транзакции заключается в координации ветвей транзакций под ее юрисдикцией для достижения соглашения, либо для успешной совместной фиксации, либо для совместного отката после сбоя. Кроме того, обычно транзакция филиала сама по себе является локальной транзакцией, удовлетворяющей требованиям ACID. Это наше основное понимание структуры распределенных транзакций, которое согласуется с XA.
Три компонента процесса распределенной транзакции протокола
- Координатор транзакций (TC): Координатор транзакций, который поддерживает текущее состояние глобальной транзакции, отвечает за координацию и управление фиксацией или откатом глобальной транзакции;
- Диспетчер транзакций (TM): контролирует границы глобальных транзакций, отвечает за открытие глобальной транзакции и, наконец, инициирует глобальную фиксацию или разрешение глобального отката;
- Диспетчер ресурсов (RM): контролирует транзакции филиалов, отвечает за регистрацию филиалов, отчеты о состоянии и получает инструкции от координатора транзакций для управления фиксацией и откатом транзакций филиала (локальных).
Типичный процесс распределенной транзакции
- TM обращается к TC для открытия глобальной транзакции, глобальная транзакция успешно создается и генерируется глобально уникальный XID;
- XID распространяется в контексте цепочки вызовов микросервиса;
- RM регистрирует транзакцию филиала в TC и включает ее в юрисдикцию глобальной транзакции, соответствующей XID;
- TM инициирует глобальную фиксацию или разрешение отката для XID в TC;
- TC планирует все транзакции филиала под юрисдикцией XID для выполнения запроса на фиксацию или откат.
Установка и настройка Seata-сервера
-
Сначала качаем ситу-сервер с официального сайта, вот загрузка
seata-server-0.9.0.zip
,ссылка для скачивания:GitHub.com/цвет ах/цвет ах… -
Здесь мы используем Nacos в качестве центра регистрации.Для установки и использования Nacos, пожалуйста, обратитесь к:Spring Cloud Alibaba: Nacos используется как центр регистрации и настройки;
-
Разархивируйте установочный пакет seata-server в указанную директорию, измените
conf
в каталогеfile.conf
Файл конфигурации, в основном изменяющий имя пользовательской группы транзакций, режим хранения журнала транзакцийdb
и информация о соединении с базой данных;
service {
#vgroup->rgroup
vgroup_mapping.fsp_tx_group = "default" #修改事务组名称为:fsp_tx_group,和客户端自定义的名称对应
#only support single node
default.grouplist = "127.0.0.1:8091"
#degrade current not support
enableDegrade = false
#disable
disable = false
#unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
max.commit.retry.timeout = "-1"
max.rollback.retry.timeout = "-1"
}
## transaction log store
store {
## store mode: file、db
mode = "db" #修改此处将事务信息存储到数据库中
## database store
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "dbcp"
## mysql/oracle/h2/oceanbase etc.
db-type = "mysql"
driver-class-name = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://localhost:3306/seat-server" #修改数据库连接地址
user = "root" #修改数据库用户名
password = "root" #修改数据库密码
min-conn = 1
max-conn = 3
global.table = "global_table"
branch.table = "branch_table"
lock-table = "lock_table"
query-limit = 100
}
}
-
Поскольку мы используем режим db для хранения журналов транзакций, нам нужно создать базу данных места-сервера и создать таблицу sql в месте-сервере.
/conf/db_store.sql
середина; -
Исправлять
conf
в каталогеregistry.conf
Файл конфигурации, указывающий, что реестрnacos
и изменитьnacos
информация о подключении;
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos" #改为nacos
nacos {
serverAddr = "localhost:8848" #改为nacos的连接地址
namespace = ""
cluster = "default"
}
}
- Сначала запустите Nacos, затем используйте Seata-Server.
/bin/seata-server.bat
файл для запуска Seata-сервера.
Подготовка базы данных
Создать бизнес-базу данных
- seat-order: база данных для хранения заказов;
- seat-storage: база данных для хранения инвентаря;
- seat-account: база данных, в которой хранится информация об учетной записи.
Инициализировать бизнес-таблицу
стол заказов
CREATE TABLE `order` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
`product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
`count` int(11) DEFAULT NULL COMMENT '数量',
`money` decimal(11,0) DEFAULT NULL COMMENT '金额',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
ALTER TABLE `order` ADD COLUMN `status` int(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完结' AFTER `money` ;
стол для хранения
CREATE TABLE `storage` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
`total` int(11) DEFAULT NULL COMMENT '总库存',
`used` int(11) DEFAULT NULL COMMENT '已用库存',
`residue` int(11) DEFAULT NULL COMMENT '剩余库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `seat-storage`.`storage` (`id`, `product_id`, `total`, `used`, `residue`) VALUES ('1', '1', '100', '0', '100');
учетная таблица
CREATE TABLE `account` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
`total` decimal(10,0) DEFAULT NULL COMMENT '总额度',
`used` decimal(10,0) DEFAULT NULL COMMENT '已用余额',
`residue` decimal(10,0) DEFAULT '0' COMMENT '剩余可用额度',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `seat-account`.`account` (`id`, `user_id`, `total`, `used`, `residue`) VALUES ('1', '1', '1000', '0', '1000');
Создайте таблицу отката журнала
При использовании Seata также необходимо создать таблицу журналов в каждой базе данных и создать таблицу sql на сервере Seata./conf/db_undo_log.sql
середина.
Схематическая диаграмма полной базы данных
Создайте проблему распределенной транзакции
Здесь мы создадим три службы: службу заказов, службу инвентаризации и службу учетных записей. Когда пользователь размещает заказ, заказ будет создан в службе заказов, а затем запасы заказанного продукта будут вычтены путем удаленного вызова службы инвентаризации, а баланс в учетной записи пользователя будет вычтен путем вызова службы учета. удаленно Наконец, в сервисе заказов изменить статус заказа на Завершен. Операция охватывает три базы данных, имеет два удаленных вызова и, очевидно, имеет проблему с распределенными транзакциями.
Конфигурация клиента
-
Настройте три клиента Seata: Seata-Order-Service, Seata-Storage-Service и Seata-Account-Service. Их конфигурации примерно одинаковы. В качестве примера возьмем конфигурацию Seat-Order-Service.
-
Измените файл application.yml, чтобы настроить имя группы транзакций;
spring:
cloud:
alibaba:
seata:
tx-service-group: fsp_tx_group #自定义事务组名称需要与seata-server中的对应
- Добавьте и измените файл конфигурации file.conf, в основном для изменения имени пользовательской группы транзакций;
service {
#vgroup->rgroup
vgroup_mapping.fsp_tx_group = "default" #修改自定义事务组名称
#only support single node
default.grouplist = "127.0.0.1:8091"
#degrade current not support
enableDegrade = false
#disable
disable = false
#unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
max.commit.retry.timeout = "-1"
max.rollback.retry.timeout = "-1"
disableGlobalTransaction = false
}
- Добавьте и измените файл конфигурации register.conf, в основном, чтобы изменить реестр на nacos;
registry {
# file 、nacos 、eureka、redis、zk
type = "nacos" #修改为nacos
nacos {
serverAddr = "localhost:8848" #修改为nacos的连接地址
namespace = ""
cluster = "default"
}
}
- Отмените автоматическое создание источника данных в классе запуска:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
public class SeataOrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SeataOrderServiceApplication.class, args);
}
}
- Создайте конфигурацию для проксирования источника данных с помощью Seata:
/**
* 使用Seata对数据源进行代理
* Created by macro on 2019/11/11.
*/
@Configuration
public class DataSourceProxyConfig {
@Value("${mybatis.mapperLocations}")
private String mapperLocations;
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(mapperLocations));
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
}
}
- Используйте аннотацию @GlobalTransactional для включения распределенных транзакций:
package com.macro.cloud.service.impl;
import com.macro.cloud.dao.OrderDao;
import com.macro.cloud.domain.Order;
import com.macro.cloud.service.AccountService;
import com.macro.cloud.service.OrderService;
import com.macro.cloud.service.StorageService;
import io.seata.spring.annotation.GlobalTransactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 订单业务实现类
* Created by macro on 2019/11/11.
*/
@Service
public class OrderServiceImpl implements OrderService {
private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class);
@Autowired
private OrderDao orderDao;
@Autowired
private StorageService storageService;
@Autowired
private AccountService accountService;
/**
* 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
*/
@Override
@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
public void create(Order order) {
LOGGER.info("------->下单开始");
//本应用创建订单
orderDao.create(order);
//远程调用库存服务扣减库存
LOGGER.info("------->order-service中扣减库存开始");
storageService.decrease(order.getProductId(),order.getCount());
LOGGER.info("------->order-service中扣减库存结束:{}",order.getId());
//远程调用账户服务扣减余额
LOGGER.info("------->order-service中扣减余额开始");
accountService.decrease(order.getUserId(),order.getMoney());
LOGGER.info("------->order-service中扣减余额结束");
//修改订单状态为已完成
LOGGER.info("------->order-service中修改订单状态开始");
orderDao.update(order.getUserId(),0);
LOGGER.info("------->order-service中修改订单状态结束");
LOGGER.info("------->下单结束");
}
}
Демонстрация функции распределенных транзакций
-
Запустите три службы: seata-order-service, seata-storage-service и seata-account-service;
-
Статус исходной информации базы данных:
- Просмотрите базу данных после вызова интерфейса для оформления заказа:http://localhost:8180/order/create?userId=1&productId=1&count=10&money=100
- После того, как мы создаем исключение тайм-аута в сервисе seata-account-service, мы вызываем интерфейс заказа:
/**
* 账户业务实现类
* Created by macro on 2019/11/11.
*/
@Service
public class AccountServiceImpl implements AccountService {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);
@Autowired
private AccountDao accountDao;
/**
* 扣减账户余额
*/
@Override
public void decrease(Long userId, BigDecimal money) {
LOGGER.info("------->account-service中扣减账户余额开始");
//模拟超时异常,全局事务回滚
try {
Thread.sleep(30*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
accountDao.decrease(userId,money);
LOGGER.info("------->account-service中扣减账户余额结束");
}
}
- На данный момент мы можем обнаружить, что данные базы данных никак не изменились после размещения заказа;
- Мы можем закомментировать @GlobalTransactional в Seata-Order-Service, чтобы увидеть, что происходит с распределенным управлением транзакциями без Seata:
/**
* 订单业务实现类
* Created by macro on 2019/11/11.
*/
@Service
public class OrderServiceImpl implements OrderService {
/**
* 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
*/
@Override
// @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
public void create(Order order) {
LOGGER.info("------->下单开始");
//省略代码...
LOGGER.info("------->下单结束");
}
}
- Из-за тайм-аута службы seata-account-service статус заказа не устанавливается как завершенный после вычета инвентаря и суммы счета, а баланс счета будет вычитаться много раз из-за механизма повторных попыток удаленных вызовов.
использованная литература
Официальная документация Seata:GitHub.com/цвет ах/цвет ах…
используемые модули
springcloud-learning
├── seata-order-service -- 整合了seata的订单服务
├── seata-storage-service -- 整合了seata的库存服务
└── seata-account-service -- 整合了seata的账户服务
Адрес исходного кода проекта
публика
проект торгового центраПолный набор учебных пособий сериализуется,Обратите внимание на общедоступный номерПолучите это прямо сейчас.