SpringBoot+Dubbo+Seata распределенная битва транзакций

Java

предисловие

SeataЭто промежуточное ПО распределенных транзакций Alibaba с открытым исходным кодом, которое решает проблемы распределенных транзакций, возникающие в сценарии микросервиса, эффективным и ненавязчивым способом.

На самом деле официальныйGitHubбыли даны в различных контекстахSeataПример проекта приложения, адрес:https://github.com/seata/seata-samples.

Есть две основные причины, по которым я хочу переписать его снова:

  • В примере кода официального сайта слишком много зависимостей, и непонятно, какие из них за что отвечают.
  • Про Seata информации меньше.В процессе сборки автор столкнулся с ямами,запишите

1. Подготовка окружающей среды

Программная среда, рассматриваемая в этой статье, выглядит следующим образом:

  • SpringBoot 2.1.6.RELEASE
  • Dubbo 2.7.1
  • Mybatis 3.5.1
  • Seata 0.6.1
  • Zookeeper 3.4.10

1. Бизнес-сценарии

Чтобы упростить процесс, нам нужны только два сервиса Order и Inventory. При создании заказа позвоните в службу инвентаризации для вычета запасов.

Используемые таблицы устроены следующим образом:

CREATE TABLE `t_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_no` varchar(255) DEFAULT NULL,
  `user_id` varchar(255) DEFAULT NULL,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT '0',
  `amount` double(14,2) DEFAULT '0.00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8;

CREATE TABLE `t_storage` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `commodity_code` (`commodity_code`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

Кроме того, требуется таблица журнала отката:

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  `context` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8;

2. Загрузите и установите Seata.

Открытымhttps://github.com/seata/seata/releasesНа данный момент последняя версияv0.6.1.

После загрузки и распаковки перейдите наseata-server-0.6.1\distribution\binможно посмотреть в каталогеseata-server.bat和seata-server.sh, выберите двойной щелчок для выполнения.

Неудивительно, когда вы видите-Server started ...Подождите, пока слова начнутся нормально.

3, Maven полагается

Поскольку это проект Dubbo, впервые введем зависимости, связанные с DUBBO.

<dependency>
	<groupId>org.apache.dubbo</groupId>
	<artifactId>dubbo</artifactId>
	<version>2.7.1</version>
</dependency>
<dependency>
	<groupId>org.apache.dubbo</groupId>
	<artifactId>dubbo-spring-boot-starter</artifactId>
	<version>2.7.1</version>
</dependency>

Сервис Dubbo необходимо зарегистрировать в Zookeeper, а также внедрить кураторский клиент.

<dependency>
	<groupId>org.apache.curator</groupId>
	<artifactId>curator-framework</artifactId>
	<version>2.13.0</version>
</dependency>
<dependency>
	<groupId>org.apache.curator</groupId>
	<artifactId>curator-recipes</artifactId>
	<version>2.13.0</version>
</dependency>

Наконец, представьте Seata.

<dependency>
	<groupId>io.seata</groupId>
	<artifactId>seata-all</artifactId>
	<version>0.6.1</version>
</dependency>

Конечно, есть и другиеMybatis、mysql-connectorКогда он не станет липким, вы можете ввести его самостоятельно.

Во-вторых, конфигурация проекта

1. свойства приложения

Здесь вам нужно только настроить информацию о подключении к базе данных и информацию, связанную с Dubbo.

server.port=8011

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/seata
spring.datasource.username=root
spring.datasource.password=root

dubbo.application.name=order-service
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20881
dubbo.consumer.timeout=9999999
dubbo.consumer.check=false

2. Источник данных

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

@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
	return new DataSourceProxy(dataSource);
}

Обратите внимание, что здесьDataSourceProxyКласс находится вio.seata.rm.datasourceвнутри сумки.

3, конфигурация СЕАТА

Также необходимо настроить глобальный сканер транзакций. Есть два параметра: один — имя приложения, а другой — группа транзакций.

@Bean
public GlobalTransactionScanner globalTransactionScanner() {
	return new GlobalTransactionScanner("springboot-order", "my_test_tx_group");
}

Фактически, здесь выполняется ряд работ по инициализации транзакций Seata.

4. Настройте реестр

SeataНекоторые элементы конфигурации необходимы при подключении к серверу.registry.confФайл может указать, что такое реестр и файл конфигурации.

Здесь есть много вариантов, напримерfile、nacos 、apollo、zk、consul.

Последние четыре являются зрелыми продуктами реестра конфигурации в отрасли, зачем файл?

Официальное первоначальное намерение состоит в том, чтобы быстро интегрировать тестирование, не полагаясь на сторонний реестр конфигурации.seataфункция, ноfileСам тип не имеет функций динамического обнаружения и динамической настройки реестра.

registry.confСодержимое файла следующее:

registry {
  type = "file"
  file {
    name = "file.conf"
  }
}
config {
  # file、nacos 、apollo、zk、consul
  type = "file"
  file {
    name = "file.conf"
  }
}

если вы выберетеfileТип, заданный атрибутом имениfile.conf, который указывает информацию о конфигурации клиента или сервера. Например, протокол передачи, адрес сервера и т. д.

service {
  #vgroup->rgroup
  vgroup_mapping.my_test_tx_group = "default"
  #only support single node
  default.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
}

3. Бизнес-код

1. Служба инвентаризации

В сервисе инвентаризации получите код товара и общее количество покупок, а затем вычтите его.

<update id="decreaseStorage">
	update t_storage set count = count-${count} where commodity_code = #{commodityCode}
</update>

Затем используйте Dubbo, чтобы открыть интерфейс вычета службы инвентаризации.

2. Заказать услугу

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

@GlobalTransactional
public void createOrder(OrderDTO orderDTO) {

	System.out.println("开始全局事务。XID="+RootContext.getXID());
	StorageDTO storageDTO = new StorageDTO();
	storageDTO.setCount(orderDTO.getCount());
	storageDTO.setCommodityCode(orderDTO.getCommodityCode());
	
	//1、扣减库存
	storageDubboService.decreaseStorage(storageDTO);
	
	//2、创建订单
	orderDTO.setId(order_id.incrementAndGet());
	orderDTO.setOrderNo(UUID.randomUUID().toString());
	Order order = new Order();
	BeanUtils.copyProperties(orderDTO,order);
	orderMapper.createOrder(order);

	throw new RuntimeException("分布式事务异常..."+orderDTO.getOrderNo());
}

Стоит отметить, что на способе запуска операции обслуживания заказа необходимо отметить@GlobalTransactional. Кроме того, в методе службы инвентаризации эта аннотация не требуется, и транзакция будет распространяться через Dubbo.

В-четвертых, вопросы, требующие внимания

1. Источник данных

Помните, что Seata реализует ветвление транзакций через прокси-источники данных, и вы не должны забывать настраивать прокси-источники данных.

2. Первичный ключ увеличивается

В базе данных поле идентификатора первичного ключа в таблице самоувеличивается. Если ваше поле не является самоинкрементным, то в Mybatisinsert SQL, завершите имена столбцов.

Например, мы можем написать SQL следующим образом:

INSERT INTO table_name VALUES (值1, 值2,....)

Тогда это должно быть записано так:

INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)

3. Проблема сериализации

В форме заказа,amountТип поляdouble. существуетseata0.6.1версия, метод сериализации по умолчаниюfastjson, но он будет сериализовать поле какbigdecimalтипа, что приведет к несоответствию следующих типов.

Но в последующемseata0.7.0В версии (еще не выпущенной) метод сериализации по умолчанию был изменен наjackson.

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

4. Код этой статьи

Пример кода для этой статьи находится по адресу:https://github.com/taoxun/springboot-dubbo-zookeeper-seata.

5. Другие

Приветствуем любые вопросы, чтобы общаться вовремя ~