eureka,沿用 TX-LCN分布式事务-- LCN事务模式(eureka模块)
seata-one 工程结构:
配置文件在最下面:
启动类:
package com.;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class SeataOneApplication {
public static void main(String[] args) {
SpringApplication.run(SeataOneApplication.class, args);
}
@Bean
@LoadBalanced//开启负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
入口 controller:
(一个 AT 模式,一个 TCC 模式)
package com..controller;
import com..service.Rm_One_Interface;
import com..service.Rm_One_Service;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Rm_One_Controller {
@Autowired
private Rm_One_Service rm_one_service;
@Autowired
private Rm_One_Interface rm_one_interface;
/**
* AT 模式
* @return
*/
@RequestMapping("/one_at")
@GlobalTransactional(rollbackFor = Exception.class)
public String at_one(){
String result = rm_one_service.rm1();
if(result.equals("err")){
return "err";
}
return "success_"+result;
}
/**
* TCC模式
* @return
* @throws InterruptedException
*/
@RequestMapping("/one_tcc")
@GlobalTransactional(rollbackFor = Exception.class)
public String oneTcc() throws InterruptedException {
rm_one_interface.rm1(null);
return "success";
}
}
数据库实体类:
package com..entity;
public class TbloneInfo {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
}
mapper:
package com..mapper; import com..entity.TbloneInfo; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Component; import java.util.List; @Mapper @Component(value = "TbloneInfoMapper") public interface TbloneInfoMapper { int deleteByPrimaryKey(String id); int insert(TbloneInfo record); TbloneInfo selectByPrimaryKey(String id); List<TbloneInfo> selectAll(); int updateByPrimaryKey(TbloneInfo record); }
AT 模式 实现类:
package com..service;
import com..entity.TbloneInfo;
import com..mapper.TbloneInfoMapper;
import com..sqlToJava.SnowFlake;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* AT 模式
*/
@Service
public class Rm_One_Service {
@Autowired
private RestTemplate restTemplate;
@Autowired
TbloneInfoMapper tbloneInfoMapper;
public String rm1(){
long id = SnowFlake.nextId();
TbloneInfo tbloneInfo = new TbloneInfo();
tbloneInfo.setId(id+"");
tbloneInfo.setName("Rm_One_"+id);
int insert = tbloneInfoMapper.insert(tbloneInfo);
rm2();
rm3();
if (insert==1){
return id+"";
}
return "err";
}
public String rm2(){
restTemplate.getForObject("http://seata-two/two_at", String.class);
return "";
}
public String rm3(){
restTemplate.getForObject("http://seata-three/three_at", String.class);
return "";
}
}
TCC模式 接口 / 实现类:
接口:
package com..service;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
@LocalTCC
public interface Rm_One_Interface {
@TwoPhaseBusinessAction(name = "rm1TccAction",commitMethod = "rm1Commit",rollbackMethod = "rm1Rollback")
public String rm1(BusinessActionContext businessActionContext);
public boolean rm1Commit(BusinessActionContext businessActionContext);
public boolean rm1Rollback(BusinessActionContext businessActionContext);
}
实现类:
package com..service;
import com..entity.TbloneInfo;
import com..mapper.TbloneInfoMapper;
import com..sqlToJava.SnowFlake;
import io.seata.rm.tcc.api.BusinessActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@Component
public class Rm_One_InterfaceImpl implements Rm_One_Interface {
@Autowired
RestTemplate restTemplate;
@Autowired
TbloneInfoMapper tbloneInfoMapper;
private static ConcurrentMap<String,String> maps = new ConcurrentHashMap<>();
@Override
@Transactional
public String rm1(BusinessActionContext businessActionContext) {
long id = SnowFlake.nextId();
TbloneInfo tbloneInfo = new TbloneInfo();
tbloneInfo.setId(id+"");
tbloneInfo.setName("Rm_One_"+id);
maps.put("id",id+"");
int insert = tbloneInfoMapper.insert(tbloneInfo);
System.out.println("rm1 try....."+insert);
rm2();
rm3();
return null;
}
@Override
@Transactional
public boolean rm1Commit(BusinessActionContext businessActionContext) {
System.out.println("rm1 rm1Commit...");
return true;
}
@Override
@Transactional
public boolean rm1Rollback(BusinessActionContext businessActionContext) {
String id = maps.get("id");
int i = tbloneInfoMapper.deleteByPrimaryKey(id);
System.out.println("rm1 rm1Rollback..."+i);
return true;
}
public String rm2(){
restTemplate.getForObject("http://seata-two/two_tcc", String.class);
return "";
}
public String rm3(){
restTemplate.getForObject("http://seata-three/three_tcc", String.class);
return "";
}
}
一个数据库主键生成工具类(雪花算法):
package com..sqlToJava; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; /** * id自增器(雪花算法) * * @author renjie * @version 1.0.0 */ public class SnowFlake { private final static long twepoch = 12888349746579L; // 机器标识位数 private final static long workerIdBits = 5L; // 数据中心标识位数 private final static long datacenterIdBits = 5L; // 毫秒内自增位数 private final static long sequenceBits = 12L; // 机器ID偏左移12位 private final static long workerIdShift = sequenceBits; // 数据中心ID左移17位 private final static long datacenterIdShift = sequenceBits + workerIdBits; // 时间毫秒左移22位 private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; //sequence掩码,确保sequnce不会超出上限 private final static long sequenceMask = -1L ^ (-1L << sequenceBits); //上次时间戳 private static long lastTimestamp = -1L; //序列 private long sequence = 0L; //服务器ID private long workerId = 1L; private static long workerMask = -1L ^ (-1L << workerIdBits); //进程编码 private long processId = 1L; private static long processMask = -1L ^ (-1L << datacenterIdBits); private static SnowFlake snowFlake = null; static{ snowFlake = new SnowFlake(); } public static synchronized long nextId(){ return snowFlake.getNextId(); } private SnowFlake() { //获取机器编码 this.workerId=this.getMachineNum(); //获取进程编码 RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); this.processId=Long.valueOf(runtimeMXBean.getName().split("@")[0]).longValue(); //避免编码超出最大值 this.workerId=workerId & workerMask; this.processId=processId & processMask; } public synchronized long getNextId() { //获取时间戳 long timestamp = timeGen(); //如果时间戳小于上次时间戳则报错 if (timestamp < lastTimestamp) { try { throw new Exception("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds"); } catch (Exception e) { e.printStackTrace(); } } //如果时间戳与上次时间戳相同 if (lastTimestamp == timestamp) { // 当前毫秒内,则+1,与sequenceMask确保sequence不会超出上限 sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { // 当前毫秒内计数满了,则等待下一秒 timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0; } lastTimestamp = timestamp; // ID偏移组合生成最终的ID,并返回ID long nextId = ((timestamp - twepoch) << timestampLeftShift) | (processId << datacenterIdShift) | (workerId << workerIdShift) | sequence; return nextId; } /** * 再次获取时间戳直到获取的时间戳与现有的不同 * @param lastTimestamp * @return 下一个时间戳 */ private long tilNextMillis(final long lastTimestamp) { long timestamp = this.timeGen(); while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); } return timestamp; } private long timeGen() { return System.currentTimeMillis(); } /** * 获取机器编码 * @return */ private long getMachineNum(){ long machinePiece; StringBuilder sb = new StringBuilder(); Enumeration<NetworkInterface> e = null; try { e = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e1) { e1.printStackTrace(); } while (e.hasMoreElements()) { NetworkInterface ni = e.nextElement(); sb.append(ni.toString()); } machinePiece = sb.toString().hashCode(); return machinePiece; } }
application.yml :
server: port: 8080 #应用名称及验证账号 spring: application: name: seata-one datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3307/seata-rm-one?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: root dbcp2: initial-size: 5 min-idle: 5 max-total: 5 max-wait-millis: 200 validation-query: SELECT 1 test-while-idle: true test-on-borrow: false test-on-return: false mybatis: mapper-locations: - classpath:mapper/*.xml eureka: client: prefer-ip-address: true service-url: defaultZone: http://localhost:7900/eureka/
数据库链接 TbloneInfoMapper.xml:
(项目地址需要根据情况补充)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com..mapper.TbloneInfoMapper"> <resultMap id="BaseResultMap" type="com..entity.TbloneInfo"> <!-- WARNING - @mbg.generated This element is automatically generated by MyBatis Generator, do not modify. This element was generated on Mon May 17 11:02:49 CST 2021. --> <id column="id" jdbcType="VARCHAR" property="id" /> <result column="name" jdbcType="VARCHAR" property="name" /> </resultMap> <delete id="deleteByPrimaryKey" parameterType="java.lang.String"> <!-- WARNING - @mbg.generated This element is automatically generated by MyBatis Generator, do not modify. This element was generated on Mon May 17 11:02:49 CST 2021. --> delete from tbl_one where id = #{id,jdbcType=VARCHAR} </delete> <insert id="insert" parameterType="com..entity.TbloneInfo"> <!-- WARNING - @mbg.generated This element is automatically generated by MyBatis Generator, do not modify. This element was generated on Mon May 17 11:02:49 CST 2021. --> insert into tbl_one (id, name) values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}) </insert> <update id="updateByPrimaryKey" parameterType="com..entity.TbloneInfo"> <!-- WARNING - @mbg.generated This element is automatically generated by MyBatis Generator, do not modify. This element was generated on Mon May 17 11:02:49 CST 2021. --> update tbl_one set name = #{name,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR} </update> <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap"> <!-- WARNING - @mbg.generated This element is automatically generated by MyBatis Generator, do not modify. This element was generated on Mon May 17 11:02:49 CST 2021. --> select id, name from tbl_one where id = #{id,jdbcType=VARCHAR} </select> <select id="selectAll" resultMap="BaseResultMap"> <!-- WARNING - @mbg.generated This element is automatically generated by MyBatis Generator, do not modify. This element was generated on Mon May 17 11:02:49 CST 2021. --> select id, name from tbl_one </select> </mapper>
pom:
(<groupId>com.</groupId> 需要根据情况补充 )
<?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 https://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.4.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.</groupId> <artifactId>seata-one</artifactId> <version>0.0.1-SNAPSHOT</version> <name>seata-one</name> <description>seata-oneBoot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>2020.0.2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- euekea 依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!-- mysql:MyBatis相关依赖--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <!-- 整合MyBatis java类依赖 --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.4.0</version> <type>maven-plugin</type> </dependency> <!-- mysql:mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- mysql:阿里巴巴数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> <!-- JSONObject --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.60</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-seata</artifactId> <version>2.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-commons</artifactId> <version>3.0.2</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>