springboot + dubbo + nacos + seata 实现分布式事务 - Go语言中文社区

springboot + dubbo + nacos + seata 实现分布式事务


1、部署 nacos-server   见https://blog.csdn.net/u013792404/article/details/98479190

2、部署 seata-server 

     下载seata-server ,    https://github.com/seata/seata/releases

      修改配置文件:全部使用file

registry.conf

registry {
    # type配置为file
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "file"
  .................
}

config {
     # type配置为file
  # file、nacos 、apollo、zk、consul、etcd3
  type = "file"
  ................
  file {
    name = "file.conf"
  }
}
file.conf

#其他不用改动

store {
  ## store mode: file、db
  mode = "file"
  ..........

3、数据库

-- ----------------------------
-- Table structure for undo_log
-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
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,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=94 DEFAULT CHARSET=utf8;
 

创建订单表和账户表 , 账户表添加初始数据

 

 

4、java代码

dubbo-common-api 存放domain, dubbo接口,VO等,    dubbo-bussiness 调用  dubbo-account 和 dubbo-order   , 生成订单后,减少账户金额。 

 

5、dubbo-order

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.6.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.mei.dubbo</groupId>
	<artifactId>dubbo-order</artifactId>
	<version>0.0.1</version>
	<name>dubbo-order</name>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>

		<dependency>
			<groupId>com.mei.dubbo</groupId>
			<artifactId>dubbo-common-api</artifactId>
			<version>0.0.1</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>


		<!--jsp支持 -->
		<!-- servlet 依赖. -->
		<!-- <dependency> -->
		<!-- <groupId>javax.servlet</groupId> -->
		<!-- <artifactId>javax.servlet-api</artifactId> -->
		<!-- <scope>provided</scope> -->
		<!-- </dependency> -->
		<!-- <dependency> -->
		<!-- <groupId>javax.servlet</groupId> -->
		<!-- <artifactId>jstl</artifactId> -->
		<!-- </dependency> -->
		<!-- tomcat 的支持. -->
		<!-- <dependency> -->
		<!-- <groupId>org.springframework.boot</groupId> -->
		<!-- <artifactId>spring-boot-starter-tomcat</artifactId> -->
		<!-- </dependency> -->
		<!-- <dependency> -->
		<!-- <groupId>org.apache.tomcat.embed</groupId> -->
		<!-- <artifactId>tomcat-embed-jasper</artifactId> -->
		<!-- <scope>provided</scope> -->
		<!-- </dependency> -->


		<!-- ====================seata=========================== -->
		<dependency>
			<groupId>io.seata</groupId>
			<artifactId>samples-common</artifactId>
			<version>1.0.0-SNAPSHOT</version>
		</dependency>

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

		<!-- ====================dubbo=========================== -->
		<dependency>
			<groupId>com.alibaba.boot</groupId>
			<artifactId>dubbo-spring-boot-starter</artifactId>
			<version>0.2.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>dubbo</artifactId>
			<version>2.6.5</version>
		</dependency>
		<!-- ====================dubbo =========================== -->

		<!-- ====================nacos =========================== -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>dubbo-registry-nacos</artifactId>
			<version>0.0.2</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba.boot</groupId>
			<artifactId>nacos-config-spring-boot-starter</artifactId>
			<version>0.2.1</version>
			<exclusions>
				<exclusion>
					<artifactId>nacos-client</artifactId>
					<groupId>com.alibaba.nacos</groupId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>com.alibaba.nacos</groupId>
			<artifactId>nacos-client</artifactId>
			<version>1.0.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
			<version>0.2.1.RELEASE</version>
			<exclusions>
				<exclusion>
					<artifactId>nacos-client</artifactId>
					<groupId>com.alibaba.nacos</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- ====================nacos =========================== -->

		<!-- 数据库 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.10</version>
		</dependency>

		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>



	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

DubboOrderApplication.java

package com.mei.dubbo.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;

@SpringBootApplication(scanBasePackages = "com.mei.dubbo.order")
@EnableDiscoveryClient
@EnableDubbo(scanBasePackages = "com.mei.dubbo.order")
public class DubboOrderApplication {

	public static void main(String[] args) {
		SpringApplication.run(DubboOrderApplication.class, args);
	}

}

application.properties

server.port=8101
spring.application.name=dubbo-order

#====================================Dubbo config===============================================
dubbo.application.id= dubbo-order
dubbo.application.name= dubbo-order
dubbo.protocol.id=dubbo
dubbo.protocol.name=dubbo
dubbo.registry.id=dubbo-order-registry
dubbo.registry.address=nacos://127.0.0.1:8848
dubbo.protocol.port=20881
dubbo.application.qosEnable=false

#===================================registry config==========================================
#Nacosu6CE8u518Cu4E2Du5FC3
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
management.endpoints.web.exposure.include=*

#====================================mysql =============================================
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/dubbo?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=mysql

#=====================================mybatisu914Du7F6E======================================
mybatis.mapper-locations=classpath*:/mapper/*.xml

file.conf  和 registry.conf   可以从seata-server的conf中复制。

 

SeataAutoConfig.java     seata需用到代理数据源 ,  new GlobalTransactionScanner("dubbo-order", "my_test_tx_group") ,其中txServiceGroup名称需要和seata-server中配置的相同才行。applicationId经测试随便写没问题。

package com.mei.dubbo.order.config;

import com.alibaba.druid.pool.DruidDataSource;

import io.seata.rm.datasource.DataSourceProxy;
import io.seata.spring.annotation.GlobalTransactionScanner;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/**
 * @Author: heshouyou
 * @Description  seata global configuration
 * @Date Created in 2019/1/24 10:28
 */
@Configuration
public class SeataAutoConfig {

    /**
     * autowired datasource config
     */
    @Autowired
    private DataSourceProperties dataSourceProperties;

    /**
     * init durid datasource
     *
     * @Return: druidDataSource  datasource instance
     */
    @Bean
    @Primary
    public DruidDataSource druidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(dataSourceProperties.getUrl());
        druidDataSource.setUsername(dataSourceProperties.getUsername());
        druidDataSource.setPassword(dataSourceProperties.getPassword());
        druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        druidDataSource.setInitialSize(0);
        druidDataSource.setMaxActive(180);
        druidDataSource.setMaxWait(60000);
        druidDataSource.setMinIdle(0);
        druidDataSource.setValidationQuery("Select 1 from DUAL");
        druidDataSource.setTestOnBorrow(false);
        druidDataSource.setTestOnReturn(false);
        druidDataSource.setTestWhileIdle(true);
        druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
        druidDataSource.setMinEvictableIdleTimeMillis(25200000);
        druidDataSource.setRemoveAbandoned(true);
        druidDataSource.setRemoveAbandonedTimeout(1800);
        druidDataSource.setLogAbandoned(true);
        return druidDataSource;
    }

    /**
     * init datasource proxy
     * @Param: druidDataSource  datasource bean instance
     * @Return: DataSourceProxy  datasource proxy
     */
    @Bean
    public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource){
        return new DataSourceProxy(druidDataSource);
    }

    /**
     * init mybatis sqlSessionFactory
     * @Param: dataSourceProxy  datasource proxy
     * @Return: DataSourceProxy  datasource proxy
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSourceProxy);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/mapper/*.xml"));
        factoryBean.setTransactionFactory(new JdbcTransactionFactory());
        return factoryBean.getObject();
    }

    /**
     * init global transaction scanner
     *
     * @Return: GlobalTransactionScanner
     */
    @Bean
    public GlobalTransactionScanner globalTransactionScanner(){//order-gts-fescar-example
        return new GlobalTransactionScanner("dubbo-order", "my_test_tx_group");
    }
}

OrderDubboServiceImpl.java

package com.mei.dubbo.order.dubbo;

import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.dubbo.config.annotation.Service;
import com.mei.dubbo.common.order.domain.Order;
import com.mei.dubbo.common.response.ServiceResponse;
import com.mei.dubbo.common.service.OrderDubboService;
import com.mei.dubbo.order.service.OrderService;

import io.seata.core.context.RootContext;

@Service(version = "1.0.0",protocol = "${dubbo.protocol.id}",
        application = "${dubbo.application.id}",registry = "${dubbo.registry.id}",
        timeout = 3000)
public class OrderDubboServiceImpl implements OrderDubboService {

    @Autowired
    private OrderService orderService;

    @Override
    public ServiceResponse createOrder(Order order) {
        System.out.println("全局事务id :" + RootContext.getXID());
        return orderService.createOrder(order);
    }
}

OrderService 使用OrderMapper完成数据相关操作。 

 

 

6、dubbo-account

pom.xml和 dubbo-order相似 。application.properties 内容相似,连接同一个数据库,只需修改应用端口和dubbo协议端口。

Seata配置类和启动主类相似,修改扫描包即可。file.conf  和  registry.conf相同

AccountDubboServiceImpl.java

package com.mei.dubbo.account.dubbo;

import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.dubbo.config.annotation.Service;
import com.mei.dubbo.account.service.AccountService;
import com.mei.dubbo.common.account.domain.Account;
import com.mei.dubbo.common.response.ServiceResponse;
import com.mei.dubbo.common.service.AccountDubboService;

import io.seata.core.context.RootContext;

@Service(version = "1.0.0",protocol = "${dubbo.protocol.id}",
         application = "${dubbo.application.id}",registry = "${dubbo.registry.id}",
         timeout = 3000)
public class AccountDubboServiceImpl implements AccountDubboService {

    @Autowired
    private AccountService accountService;

    @Override
    public ServiceResponse decreaseAccount(Account account) {
        System.out.println("全局事务id :" + RootContext.getXID());
        return accountService.decreaseAccount(account);
    }
}

 

7、dubbo-bussiness

pom.xml和 dubbo-order相似 ,不需要连接数据库,去掉数据库相关依赖即可。application.properties 内容相似,去掉数据库相关配置,需修改应用端口和dubbo协议端口。file.conf  和  registry.conf相同

Seata配置类和启动主类相似,修改扫描包即可。

BussinessService.java   , 经测试  @GlobalTransactional(timeoutMills = 300000) 其中没有加上name也可以 

package com.mei.dubbo.bussiness.service;

import java.util.UUID;

import org.springframework.stereotype.Service;

import com.alibaba.dubbo.config.annotation.Reference;
import com.mei.dubbo.common.account.domain.Account;
import com.mei.dubbo.common.order.domain.Order;
import com.mei.dubbo.common.response.ServiceResponse;
import com.mei.dubbo.common.service.AccountDubboService;
import com.mei.dubbo.common.service.OrderDubboService;

import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;

@Service
public class BussinessService {

	@Reference(version = "1.0.0")
	private AccountDubboService accountDubboService;

	@Reference(version = "1.0.0")
	private OrderDubboService orderDubboService;

	@GlobalTransactional(timeoutMills = 300000, name = "dubbo-seata-example")
	public void buy(String str) {
		System.out.println("开始全局事务,XID = " + RootContext.getXID());
		
		
		Order order = new Order();
		order.setOrderId(UUID.randomUUID().toString().replace("-", ""));
		order.setGoodsName("商品1");
		order.setNum(2);
		order.setPrice("60");
		order.setAccountId("1001");

		String amount = "120";// 2*60
		Account account = new Account();
		account.setAccountId("1001");
		account.setName("张三");
		account.setAmount(amount);
		
		ServiceResponse orderResponse = orderDubboService.createOrder(order);
		ServiceResponse amountResponse = accountDubboService.decreaseAccount(account);
		
		if(ServiceResponse.SUCCESS.equals(orderResponse.getCode())  &&  ServiceResponse.SUCCESS.equals(amountResponse.getCode())) {
			System.out.println("事务成功。。。。。");
			
		} else {
			System.out.println("事务回滚1。。。。。");
			throw new RuntimeException("事务回滚1。。。。。") ;
		}
		if("1".equals(str)) {
			System.out.println("事务回滚2。。。。。");
			throw new RuntimeException("事务回滚2。。。。。") ;
		}

	}

}

BussinessController.java

package com.mei.dubbo.bussiness.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.mei.dubbo.bussiness.service.BussinessService;

@Controller
public class BussinessController {

	@Autowired
	private BussinessService bussinessService ;
	
	@ResponseBody
	@RequestMapping("/buy")
	public String test(String str) {
		try {
			bussinessService.buy(str) ;
			return "OK" ;
			
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return "false" ;
	}
}
server.port=8104
spring.application.name=dubbo-business

#============================dubbo config==============================================
dubbo.application.id=dubbo-business-example
dubbo.application.name=dubbo-business-example
dubbo.protocol.id=dubbo
dubbo.protocol.name=dubbo
dubbo.protocol.port=20884
dubbo.registry.id=dubbo-business-example-registry
dubbo.registry.address=nacos://127.0.0.1:8848
dubbo.provider.version=1.0.0
dubbo.application.qosEnable=false

#==================================nacos==============================================
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
management.endpoints.web.exposure.include=*

8、测试

根据dubbo依赖关系,先启动dubbo-account ,dubbo-order ; 在启动dubbo-bussiness . 

访问dubbo-bussiness : http://localhost:8104/buy?str=0   , 返回“OK”

开始全局事务,XID = 192.168.5.167:8091:2018715902
事务成功。。。。。
2019-08-05 15:55:27.367  INFO 8332 --- [nio-8104-exec-1] i.seata.tm.api.DefaultGlobalTransaction  : [192.168.5.167:8091:2018715902] commit status:Committed
 

 

访问: http://localhost:8104/buy?str=1    返回“false”

事务回滚2。。。。。
2019-08-05 15:57:36.056  INFO 8332 --- [nio-8104-exec-4] i.seata.tm.api.DefaultGlobalTransaction  : [192.168.5.167:8091:2018715905] rollback status:Rollbacked
事务回滚2。。。。。

没有生成新的订单, 账户金额也没有减

 

 

 

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/u013792404/article/details/98493788
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-01 22:08:58
  • 阅读 ( 1134 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢