├── .gitignore ├── micro-tcc-tc ├── src │ └── main │ │ ├── java │ │ └── org │ │ │ └── micro │ │ │ └── tcc │ │ │ └── tc │ │ │ ├── dubbo │ │ │ ├── init │ │ │ └── filter │ │ │ │ ├── DubboConsumerContextFilter.java │ │ │ │ └── DubboProviderContextFilter.java │ │ │ ├── util │ │ │ ├── TransactionUtils.java │ │ │ ├── ReflectionUtils.java │ │ │ └── TransactionSerializer.java │ │ │ ├── interceptor │ │ │ ├── FeignHeadConfiguration.java │ │ │ ├── TccTransactionAspect.java │ │ │ ├── TccCoordinatorAspect.java │ │ │ ├── TccMethodContext.java │ │ │ ├── TccCoordinatorInterceptor.java │ │ │ ├── WebControllerAspect.java │ │ │ ├── TccTransactionInterceptor.java │ │ │ └── FeignHystrixConcurrencyStrategy.java │ │ │ ├── recover │ │ │ ├── RecoverConfig.java │ │ │ ├── TransactionRecoverConfig.java │ │ │ ├── RecoverScheduledZookeeperJob.java │ │ │ ├── RecoverScheduledJob.java │ │ │ └── TransactionRecovery.java │ │ │ ├── annotation │ │ │ ├── TccIdentity.java │ │ │ ├── EnableMicroTccTransaction.java │ │ │ └── TccTransaction.java │ │ │ ├── component │ │ │ ├── MVCConfig.java │ │ │ ├── LogAspect.java │ │ │ ├── MicroTccSpringSelector.java │ │ │ ├── RedisConfig.java │ │ │ ├── SpringContextAware.java │ │ │ ├── MicroTccSpringConfig.java │ │ │ ├── CoordinatorWatcher.java │ │ │ └── CoordinatorTreeCacheWatcher.java │ │ │ ├── http │ │ │ ├── HttpAutoConfiguration.java │ │ │ ├── HttpClientRequestInterceptor.java │ │ │ └── RestTemplateInterceptor.java │ │ │ └── repository │ │ │ └── JdbcTransactionRepository.java │ │ └── resources │ │ ├── META-INF │ │ ├── spring.factories │ │ └── dubbo │ │ │ └── com.alibaba.dubbo.rpc.Filter │ │ └── application.properties ├── pom.xml └── micro-tcc-tc.iml ├── micro-tcc-common ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring.factories │ │ └── java │ │ └── org │ │ └── micro │ │ └── tcc │ │ └── common │ │ ├── exception │ │ ├── ConfirmException.java │ │ ├── CancelException.java │ │ ├── NoExistedTransactionException.java │ │ ├── TccSystemErrorException.java │ │ └── TransactionIOStreamException.java │ │ ├── constant │ │ ├── MethodType.java │ │ ├── Model.java │ │ ├── Propagation.java │ │ ├── TransactionType.java │ │ ├── TransactionStatus.java │ │ └── Constant.java │ │ ├── support │ │ ├── BeanFactory.java │ │ ├── SpringBeanFactory.java │ │ └── BeanFactoryBuilder.java │ │ ├── util │ │ ├── CoordinatorUtils.java │ │ ├── ByteUtils.java │ │ └── SnowFlake.java │ │ ├── serializer │ │ ├── ObjectSerializer.java │ │ ├── JdkSerializationSerializer.java │ │ └── KryoPoolSerializer.java │ │ └── core │ │ ├── FixSizeLinkedList.java │ │ ├── Invocation.java │ │ ├── TccTransactionContext.java │ │ ├── MethodReflect.java │ │ ├── TransactionMember.java │ │ ├── TransactionRepository.java │ │ ├── TransactionXid.java │ │ └── Transaction.java ├── pom.xml └── micro-tcc-common.iml ├── package.bat ├── micro-tcc.iml ├── README.md ├── pom.xml └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/dubbo/init: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | mvn clean package 4 | 5 | 6 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.micro.tcc.tc.http.HttpAutoConfiguration -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | dubbo.consumer.filter=org.micro.tcc.tc.dubbo.filter.DubboConsumerContextFilter 2 | dubbo.provider.filter=org.micro.tcc.tc.dubbo.filter.DubboProviderContextFilter -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/util/TransactionUtils.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.util; 2 | 3 | 4 | /** 5 | *@author jeff.liu 6 | * 7 | * date 2019/7/31 8 | */ 9 | public class TransactionUtils { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter: -------------------------------------------------------------------------------- 1 | dubboProviderContextFilter=org.micro.tcc.tc.dubbo.filter.DubboProviderContextFilter 2 | dubboConsumerContextFilter=org.micro.tcc.tc.dubbo.filter.DubboConsumerContextFilter -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/exception/ConfirmException.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.exception; 2 | 3 | 4 | public class ConfirmException extends RuntimeException { 5 | 6 | public ConfirmException(Throwable cause) { 7 | super(cause); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/exception/CancelException.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.exception; 2 | 3 | 4 | public class CancelException extends RuntimeException { 5 | 6 | 7 | public CancelException(Throwable cause) { 8 | super(cause); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/constant/MethodType.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.constant; 2 | 3 | 4 | /** 5 | *@author jeff.liu 6 | *@desc 方法类型 7 | *@date 2019/8/27 8 | */ 9 | public enum MethodType { 10 | 11 | ROOT, 12 | 13 | CONSUMER, 14 | 15 | PROVIDER, 16 | 17 | NORMAL; 18 | } 19 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/support/BeanFactory.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.support; 2 | 3 | /** 4 | *@author jeff.liu 5 | * 描述 bean 工厂接口 6 | * date 2019/7/31 7 | */ 8 | public interface BeanFactory { 9 | T getBean(Class val); 10 | 11 | boolean isFactoryBean(Class clazz); 12 | } 13 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/interceptor/FeignHeadConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.interceptor; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | /** 7 | * 自定义的请求头处理类,处理服务发送时的请求头; 8 | */ 9 | @Configuration 10 | @Slf4j 11 | public class FeignHeadConfiguration { 12 | 13 | 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/exception/NoExistedTransactionException.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.exception; 2 | 3 | 4 | public class NoExistedTransactionException extends Exception { 5 | private static final long serialVersionUID = 1031919168789207713L; 6 | 7 | public NoExistedTransactionException(String msg){ 8 | super(msg); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/recover/RecoverConfig.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.recover; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | *@author jeff.liu 7 | * 事务恢复配置接口 8 | * date 2019/7/31 9 | */ 10 | public interface RecoverConfig { 11 | 12 | public int getMaxRetryCount(); 13 | 14 | public int getRecoverDuration(); 15 | 16 | public String getCronExpression(); 17 | 18 | 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/annotation/TccIdentity.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | 9 | /** 10 | *@author jeff.liu 11 | *@desc 自增主键注解 12 | *@date 2019/8/27 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target({ElementType.PARAMETER}) 16 | public @interface TccIdentity { 17 | } 18 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/exception/TccSystemErrorException.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.exception; 2 | 3 | 4 | public class TccSystemErrorException extends RuntimeException { 5 | 6 | public TccSystemErrorException(String message) { 7 | super(message); 8 | } 9 | 10 | public TccSystemErrorException(Throwable e) { 11 | super(e); 12 | } 13 | 14 | public TccSystemErrorException(String message, Throwable e) { 15 | super(message, e); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/annotation/EnableMicroTccTransaction.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.annotation; 2 | 3 | import org.micro.tcc.tc.component.MicroTccSpringSelector; 4 | import org.springframework.context.annotation.Import; 5 | 6 | import java.lang.annotation.*; 7 | 8 | 9 | /** 10 | *@author jeff.liu 11 | *@desc 启用tcc 分布式事务注解 12 | *@date 2019/8/27 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | @Target(ElementType.TYPE) 17 | @Import(MicroTccSpringSelector.class) 18 | public @interface EnableMicroTccTransaction { 19 | } -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/exception/TransactionIOStreamException.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.exception; 2 | 3 | /** 4 | *@author jeff.liu 5 | * 描述 6 | * date 2019/7/31 7 | */ 8 | public class TransactionIOStreamException extends RuntimeException { 9 | 10 | private static final long serialVersionUID = 6508064607297986329L; 11 | 12 | public TransactionIOStreamException(String message) { 13 | super(message); 14 | } 15 | 16 | public TransactionIOStreamException(Throwable e) { 17 | super(e); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/constant/Model.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.constant; 2 | 3 | 4 | /** 5 | *@author jeff.liu 6 | *@desc 事务模式 7 | *@date 2019/8/27 8 | */ 9 | public enum Model { 10 | 11 | /** 12 | * confirm发生异常自动rollback 13 | */ 14 | AT(0), 15 | 16 | /** 17 | * confirm发生异常不会自动rollback 18 | */ 19 | MT(1); 20 | 21 | private final int value; 22 | 23 | private Model(int value) { 24 | this.value = value; 25 | } 26 | 27 | public int value() { 28 | return this.value; 29 | } 30 | } -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/constant/Propagation.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.constant; 2 | 3 | 4 | /** 5 | *@author jeff.liu 6 | *@desc 事务类型 7 | *@date 2019/8/27 8 | */ 9 | public enum Propagation { 10 | /** 11 | * 新建事务 12 | */ 13 | REQUIRED(0), 14 | /** 15 | * 加入事务 16 | */ 17 | SUPPORTS(1), 18 | MANDATORY(2), 19 | REQUIRES_NEW(3); 20 | 21 | private final int value; 22 | 23 | private Propagation(int value) { 24 | this.value = value; 25 | } 26 | 27 | public int value() { 28 | return this.value; 29 | } 30 | } -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/util/CoordinatorUtils.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.util; 2 | 3 | import org.micro.tcc.common.constant.Constant; 4 | 5 | /** 6 | *@author jeff.liu 7 | *@desc 协调工具 8 | *@date 2019/8/27 9 | */ 10 | public class CoordinatorUtils { 11 | 12 | public static String getNodeGroupId(String transactionPath){ 13 | String[] path=transactionPath.split("\\"+Constant.DELIMIT); 14 | if(path.length==2){ 15 | return path[0]; 16 | } 17 | return ""; 18 | } 19 | 20 | public static String getAppName(String transactionPath){ 21 | 22 | return getNodeGroupId(transactionPath); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/util/ByteUtils.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.util; 2 | 3 | /** 4 | *@author jeff.liu 5 | * 描述 字节工具 6 | * date 2019/7/31 7 | */ 8 | public class ByteUtils { 9 | 10 | public static byte[] longToBytes(long num) { 11 | return String.valueOf(num).getBytes(); 12 | } 13 | 14 | public static long bytesToLong(byte[] bytes) { 15 | return Long.valueOf(new String(bytes)); 16 | } 17 | 18 | public static byte[] intToBytes(int num) { 19 | return String.valueOf(num).getBytes(); 20 | } 21 | 22 | public static int bytesToInt(byte[] bytes) { 23 | return Integer.valueOf(new String(bytes)); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /micro-tcc.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/serializer/ObjectSerializer.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.serializer; 2 | 3 | /** 4 | *@author jeff.liu 5 | * 描述 序列号接口 6 | * date 2019/7/31 7 | */ 8 | public interface ObjectSerializer { 9 | 10 | /** 11 | * Serialize the given object to binary data. 12 | * 13 | * @param t object to serialize 14 | * the equivalent binary data 15 | */ 16 | byte[] serialize(T t); 17 | 18 | /** 19 | * Deserialize an object from the given binary data. 20 | * 21 | * @param bytes object binary representation 22 | * the equivalent object instance 23 | */ 24 | T deserialize(byte[] bytes); 25 | 26 | 27 | T clone(T object); 28 | } 29 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/constant/TransactionType.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | package org.micro.tcc.common.constant; 4 | 5 | /** 6 | *@author jeff.liu 7 | *@desc 事务类型 8 | *@date 2019/8/27 9 | */ 10 | public enum TransactionType { 11 | 12 | /** 13 | * 发起者 14 | */ 15 | ROOT(1), 16 | /** 17 | * 参与者 18 | */ 19 | BRANCH(2); 20 | int id; 21 | 22 | 23 | public int value(){ 24 | return id; 25 | } 26 | TransactionType(int id) { 27 | this.id = id; 28 | } 29 | 30 | 31 | public static TransactionType valueOf(int id) { 32 | switch (id) { 33 | case 1: 34 | return ROOT; 35 | case 2: 36 | return BRANCH; 37 | default: 38 | return null; 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/serializer/JdkSerializationSerializer.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.serializer; 2 | 3 | import org.apache.commons.lang3.SerializationUtils; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | *@author jeff.liu 9 | * 描述 jdk 序列化 10 | * date 2019/7/31 11 | */ 12 | public class JdkSerializationSerializer implements ObjectSerializer { 13 | 14 | @Override 15 | public byte[] serialize(T object) { 16 | return SerializationUtils.serialize(object); 17 | } 18 | 19 | @Override 20 | public T deserialize(byte[] bytes) { 21 | if (bytes == null) { 22 | return null; 23 | } else { 24 | return (T) SerializationUtils.deserialize(bytes); 25 | } 26 | } 27 | 28 | @Override 29 | public T clone(T object) { 30 | return SerializationUtils.clone(object); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/constant/TransactionStatus.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.constant; 2 | 3 | /** 4 | *@author jeff.liu 5 | *@desc 事务状态 6 | *@date 2019/8/27 7 | */ 8 | public enum TransactionStatus { 9 | 10 | /** 11 | * 尝试发起 12 | */ 13 | TRY(1), 14 | /** 15 | * 事务确认 16 | */ 17 | CONFIRM(2), 18 | /** 19 | * 事务取消 20 | */ 21 | CANCEL(3); 22 | 23 | private int id; 24 | 25 | TransactionStatus(int id) { 26 | this.id = id; 27 | } 28 | 29 | public int value() { 30 | return id; 31 | } 32 | public void setId(int id) { 33 | this.id= id; 34 | } 35 | public static TransactionStatus valueOf(int id) { 36 | 37 | switch (id) { 38 | case 1: 39 | return TRY; 40 | case 2: 41 | return CONFIRM; 42 | default: 43 | return CANCEL; 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/annotation/TccTransaction.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.annotation; 2 | 3 | import org.micro.tcc.common.constant.Model; 4 | import org.micro.tcc.common.constant.Propagation; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | 12 | /** 13 | *@author jeff.liu 14 | *@desc tcc 声明事务注解 15 | *@date 2019/8/27 16 | */ 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Target({ElementType.METHOD}) 19 | public @interface TccTransaction { 20 | 21 | public Propagation propagation() default Propagation.REQUIRED; 22 | 23 | public String confirmMethod() default ""; 24 | 25 | public String cancelMethod() default ""; 26 | 27 | public Model model() default Model.AT; 28 | 29 | public boolean asyncConfirm() default false; 30 | 31 | public boolean asyncCancel() default false; 32 | 33 | Class[] rollbackFor() default {}; 34 | 35 | } -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/component/MVCConfig.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.component; 2 | 3 | import org.micro.tcc.tc.interceptor.WebControllerAspect; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 8 | 9 | /** 10 | *@author jeff.liu 11 | *@desc spring mvc 配置 12 | *@date 2019/8/27 13 | */ 14 | @Configuration 15 | public class MVCConfig extends WebMvcConfigurationSupport { 16 | @Bean 17 | public WebControllerAspect securityInterceptor() { 18 | return new WebControllerAspect(); 19 | } 20 | 21 | @Override 22 | public void addInterceptors(InterceptorRegistry registry) { 23 | registry.addInterceptor(securityInterceptor()).excludePathPatterns("/static/*") 24 | .excludePathPatterns("/error").addPathPatterns("/**"); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/http/HttpAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.http; 2 | 3 | import org.apache.http.HttpRequestInterceptor; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.core.env.ConfigurableEnvironment; 10 | import org.springframework.http.client.ClientHttpRequestInterceptor; 11 | import org.springframework.web.client.RestTemplate; 12 | 13 | import javax.annotation.PostConstruct; 14 | import java.util.Collections; 15 | 16 | /** 17 | * @author jeff.liu 18 | * HTTP 配置 19 | * date 2019/8/6 10:26 20 | */ 21 | @Configuration 22 | public class HttpAutoConfiguration { 23 | 24 | 25 | @Autowired 26 | private ConfigurableEnvironment configurableEnvironment; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/core/FixSizeLinkedList.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.core; 2 | 3 | import java.util.LinkedList; 4 | 5 | import com.alibaba.fastjson.JSON; 6 | 7 | /** 8 | * 固定长度List 9 | * 如果List里面的元素个数大于了缓存最大容量,则删除链表的顶端元素 10 | */ 11 | public class FixSizeLinkedList extends LinkedList { 12 | private static final long serialVersionUID = 3292612616231532364L; 13 | // 定义缓存的容量 14 | private int capacity; 15 | 16 | public FixSizeLinkedList(int capacity) { 17 | super(); 18 | this.capacity = capacity; 19 | } 20 | 21 | @Override 22 | public boolean add(T e) { 23 | // 超过长度,移除最后一个 24 | if (size() + 1 > capacity) { 25 | super.removeFirst(); 26 | } 27 | return super.add(e); 28 | } 29 | 30 | public static void main(String[] args) { 31 | FixSizeLinkedList list = new FixSizeLinkedList<>(3); 32 | list.add("1234"); 33 | list.add("234"); 34 | list.add("34"); 35 | list.add("4"); 36 | System.out.println(JSON.toJSONString(list)); 37 | } 38 | } -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/support/SpringBeanFactory.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.support; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | *@author jeff.liu 12 | * 描述 spring bean 工厂 13 | * date 2019/7/31 14 | */ 15 | @Component 16 | public class SpringBeanFactory implements BeanFactory, ApplicationContextAware { 17 | 18 | private ApplicationContext applicationContext; 19 | 20 | @Override 21 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 22 | this.applicationContext = applicationContext; 23 | BeanFactoryBuilder.registerBeanFactory(this); 24 | } 25 | 26 | @Override 27 | public boolean isFactoryBean(Class clazz) { 28 | Map map = this.applicationContext.getBeansOfType(clazz); 29 | return map.size() > 0; 30 | } 31 | 32 | @Override 33 | public T getBean(Class val) { 34 | return this.applicationContext.getBean(val); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/core/Invocation.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.core; 2 | 3 | import java.io.Serializable; 4 | 5 | 6 | /** 7 | *@author jeff.liu 8 | *@desc 调用管理者 9 | *@date 2019/8/27 10 | */ 11 | public class Invocation implements Serializable { 12 | 13 | 14 | private static final long serialVersionUID = -1921572071621055035L; 15 | /** 16 | * 目标类 17 | */ 18 | private Class targetClass; 19 | 20 | /** 21 | * 方法 22 | */ 23 | private String methodName; 24 | 25 | /** 26 | * 参数 27 | */ 28 | private Class[] parameterTypes; 29 | 30 | private Object[] args; 31 | 32 | public Invocation() { 33 | 34 | } 35 | 36 | public Invocation(Class targetClass, String methodName, Class[] parameterTypes, Object... args) { 37 | this.methodName = methodName; 38 | this.parameterTypes = parameterTypes; 39 | this.targetClass = targetClass; 40 | this.args = args; 41 | } 42 | 43 | public Object[] getArgs() { 44 | return args; 45 | } 46 | 47 | public Class getTargetClass() { 48 | return targetClass; 49 | } 50 | 51 | public String getMethodName() { 52 | return methodName; 53 | } 54 | 55 | public Class[] getParameterTypes() { 56 | return parameterTypes; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/interceptor/TccTransactionAspect.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.interceptor; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.ProceedingJoinPoint; 5 | import org.aspectj.lang.annotation.Around; 6 | import org.aspectj.lang.annotation.Aspect; 7 | import org.aspectj.lang.annotation.Pointcut; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.core.annotation.Order; 10 | import org.springframework.stereotype.Component; 11 | 12 | 13 | /** 14 | *@author jeff.liu 15 | * tcc 事务拦截器,主要拦截器 16 | * date 2019/7/31 17 | */ 18 | @Component 19 | @Aspect 20 | @Order(1) 21 | @Slf4j 22 | public class TccTransactionAspect { 23 | 24 | @Autowired 25 | private TccTransactionInterceptor tccTransactionInterceptor; 26 | 27 | public void setTccTransactionInterceptor(TccTransactionInterceptor tccTransactionInterceptor) { 28 | this.tccTransactionInterceptor = tccTransactionInterceptor; 29 | } 30 | 31 | @Pointcut("@annotation(org.micro.tcc.tc.annotation.TccTransaction)") 32 | public void transactionMethod() { 33 | 34 | } 35 | 36 | @Around("transactionMethod()") 37 | public Object interceptTransactionMethod(ProceedingJoinPoint pjp) throws Throwable { 38 | log.debug("TCC:TransactionAspect "); 39 | return tccTransactionInterceptor.interceptTransactionMethod(pjp); 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/interceptor/TccCoordinatorAspect.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.interceptor; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.ProceedingJoinPoint; 5 | import org.aspectj.lang.annotation.Around; 6 | import org.aspectj.lang.annotation.Aspect; 7 | import org.aspectj.lang.annotation.Pointcut; 8 | import org.springframework.core.annotation.Order; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | *@author jeff.liu 13 | * 参与者拦截器 14 | * date 2019/7/31 15 | */ 16 | @Component 17 | @Aspect 18 | @Order(2) 19 | @Slf4j 20 | public class TccCoordinatorAspect { 21 | 22 | private TccCoordinatorInterceptor tccCoordinatorInterceptor; 23 | 24 | @Pointcut("@annotation(org.micro.tcc.tc.annotation.TccTransaction)") 25 | public void transactionContextCall() { 26 | 27 | } 28 | 29 | @Around("transactionContextCall()") 30 | public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable { 31 | log.debug("TCC:CoordinatorAspect "); 32 | if(null== tccCoordinatorInterceptor) tccCoordinatorInterceptor =new TccCoordinatorInterceptor(); 33 | return tccCoordinatorInterceptor.interceptTransactionContextMethod(pjp); 34 | } 35 | 36 | public void setTccCoordinatorInterceptor(TccCoordinatorInterceptor tccCoordinatorInterceptor) { 37 | this.tccCoordinatorInterceptor = tccCoordinatorInterceptor; 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/core/TccTransactionContext.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.core; 2 | 3 | import java.io.Serializable; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | *@author jeff.liu 9 | *@desc 事务内容 10 | *@date 2019/8/27 11 | */ 12 | public class TccTransactionContext implements Serializable { 13 | 14 | 15 | private static final long serialVersionUID = -5483706013898190522L; 16 | private TransactionXid xid; 17 | 18 | private int status; 19 | 20 | private Map attachments = new ConcurrentHashMap(); 21 | 22 | public TccTransactionContext() { 23 | 24 | } 25 | 26 | public TccTransactionContext(TransactionXid xid, int status) { 27 | this.xid = xid; 28 | this.status = status; 29 | } 30 | 31 | public void setXid(TransactionXid xid) { 32 | this.xid = xid; 33 | } 34 | 35 | public TransactionXid getXid() { 36 | return xid; 37 | } 38 | 39 | public void setAttachments(Map attachments) { 40 | if (attachments != null && !attachments.isEmpty()) { 41 | this.attachments.putAll(attachments); 42 | } 43 | } 44 | 45 | public Map getAttachments() { 46 | return attachments; 47 | } 48 | 49 | public void setStatus(int status) { 50 | this.status = status; 51 | } 52 | 53 | public int getStatus() { 54 | return status; 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/http/HttpClientRequestInterceptor.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.http; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.http.HttpException; 5 | import org.apache.http.HttpHost; 6 | import org.apache.http.HttpRequest; 7 | import org.apache.http.HttpRequestInterceptor; 8 | import org.apache.http.protocol.HttpContext; 9 | import org.apache.http.protocol.HttpCoreContext; 10 | import org.micro.tcc.common.constant.Constant; 11 | import org.micro.tcc.common.core.Transaction; 12 | import org.micro.tcc.tc.component.TransactionManager; 13 | 14 | import java.io.IOException; 15 | 16 | /** 17 | * @author jeff.liu 18 | * rest 拦截器 ,传递group id 19 | * date 2019/8/6 10:18 20 | */ 21 | @Slf4j 22 | public class HttpClientRequestInterceptor implements HttpRequestInterceptor { 23 | 24 | 25 | @Override 26 | public void process(HttpRequest httpRequest, HttpContext httpContext) throws HttpException, IOException { 27 | log.debug("TCC:HttpClientRequestInterceptor "); 28 | HttpCoreContext adapt = HttpCoreContext.adapt(httpContext); 29 | HttpHost targetHost = adapt.getTargetHost(); 30 | Transaction transaction = TransactionManager.getInstance().getCurrentTransaction(); 31 | if(null==transaction)return ; 32 | String groupId = transaction.getTransactionXid().getGlobalTccTransactionId(); 33 | 34 | httpRequest.addHeader(Constant.GLOBAL_TCCTRANSACTION_ID, groupId); 35 | httpRequest.addHeader(Constant.TCCTRANSACTION_STATUS, String.valueOf(transaction.getStatus().value())); 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/dubbo/filter/DubboConsumerContextFilter.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.dubbo.filter; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import com.alibaba.dubbo.common.extension.Activate; 6 | import com.alibaba.dubbo.rpc.Filter; 7 | import com.alibaba.dubbo.rpc.Invocation; 8 | import com.alibaba.dubbo.rpc.Invoker; 9 | import com.alibaba.dubbo.rpc.Result; 10 | import com.alibaba.dubbo.rpc.RpcContext; 11 | import com.alibaba.dubbo.rpc.RpcException; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.micro.tcc.common.constant.Constant; 14 | import org.micro.tcc.common.core.Transaction; 15 | import org.micro.tcc.tc.component.TransactionManager; 16 | 17 | 18 | /** 19 | *@author jeff.liu 20 | * dubbo 消费者拦截器 21 | * date 2019/8/5 22 | */ 23 | @Activate 24 | @Slf4j 25 | public class DubboConsumerContextFilter implements Filter { 26 | 27 | @Override 28 | public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { 29 | log.debug("TCC: Consumer filter "); 30 | Map context = new HashMap<>(); 31 | //处理group id 32 | Transaction transaction= TransactionManager.getInstance().getCurrentTransaction(); 33 | if(null==transaction)return invoker.invoke(invocation); 34 | 35 | String groupId=transaction.getTransactionXid().getGlobalTccTransactionId(); 36 | context.put(Constant.GLOBAL_TCCTRANSACTION_ID, groupId); 37 | context.put(Constant.TCCTRANSACTION_STATUS,String.valueOf(transaction.getStatus().value())); 38 | RpcContext.getContext().setAttachments(context); 39 | return invoker.invoke(invocation); 40 | } 41 | } 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/core/MethodReflect.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.core; 2 | 3 | 4 | import com.esotericsoftware.reflectasm.MethodAccess; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang.StringUtils; 7 | import org.micro.tcc.common.exception.TccSystemErrorException; 8 | import org.micro.tcc.common.support.BeanFactoryBuilder; 9 | 10 | import java.io.Serializable; 11 | import java.lang.reflect.Method; 12 | 13 | /** 14 | *@author jeff.liu 15 | *@desc 反射类 16 | *@date 2019/8/20 17 | */ 18 | @Slf4j 19 | public class MethodReflect implements Serializable { 20 | 21 | private static final long serialVersionUID = -164958655471605778L; 22 | 23 | 24 | public MethodReflect() { 25 | 26 | } 27 | 28 | public Object invoke(TccTransactionContext transactionContext, Invocation invocation) { 29 | 30 | if (StringUtils.isNotEmpty(invocation.getMethodName())) { 31 | try { 32 | Object target = BeanFactoryBuilder.factoryBean(invocation.getTargetClass()).getInstance(); 33 | Method method = null; 34 | //生成字节码的方式创建MethodAccess 35 | MethodAccess access = MethodAccess.get(target.getClass()); 36 | Object oo=access.invoke(target,invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArgs()); 37 | // method = target.getClass().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); 38 | // Object o= method.invoke(target, invocation.getArgs()); 39 | return oo; 40 | 41 | } catch (Exception e) { 42 | throw new TccSystemErrorException(e); 43 | } 44 | } 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /micro-tcc-tc/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | com.github.mytcctransaction 7 | micro-tcc 8 | 1.5.1 9 | 10 | 11 | 4.0.0 12 | 13 | micro-tcc-tc 14 | 15 | micro-tcc-tc 16 | jar 17 | 18 | 19 | 20 | 21 | com.github.mytcctransaction 22 | micro-tcc-common 23 | 1.5.1 24 | 25 | 26 | com.alibaba.boot 27 | dubbo-spring-boot-starter 28 | 0.2.0 29 | 30 | 31 | 32 | com.alibaba.boot 33 | dubbo-spring-boot-actuator 34 | 0.2.0 35 | 36 | 37 | org.apache.httpcomponents 38 | httpclient 39 | 40 | 41 | 42 | org.quartz-scheduler 43 | quartz 44 | 2.2.1 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/dubbo/filter/DubboProviderContextFilter.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.dubbo.filter; 2 | 3 | import com.alibaba.dubbo.common.extension.Activate; 4 | import com.alibaba.dubbo.rpc.Filter; 5 | import com.alibaba.dubbo.rpc.Invocation; 6 | import com.alibaba.dubbo.rpc.Invoker; 7 | import com.alibaba.dubbo.rpc.Result; 8 | import com.alibaba.dubbo.rpc.RpcContext; 9 | import com.alibaba.dubbo.rpc.RpcException; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.micro.tcc.common.constant.Constant; 13 | import org.micro.tcc.common.constant.TransactionType; 14 | import org.micro.tcc.common.core.Transaction; 15 | import org.micro.tcc.tc.component.TransactionManager; 16 | 17 | /** 18 | *@author jeff.liu 19 | * dubbo提供者拦截器 20 | * date 2019/8/5 21 | */ 22 | @Activate 23 | @Slf4j 24 | public class DubboProviderContextFilter implements Filter { 25 | @Override 26 | public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { 27 | // 处理group id 28 | log.debug("TCC:Dubbo provider filter "); 29 | String groupId = RpcContext.getContext().getAttachment(Constant.GLOBAL_TCCTRANSACTION_ID); 30 | String status = RpcContext.getContext().getAttachment(Constant.TCCTRANSACTION_STATUS); 31 | if(StringUtils.isNotEmpty(groupId)&&StringUtils.isNotEmpty(status)){ 32 | 33 | Transaction transaction=new Transaction(TransactionType.BRANCH); 34 | transaction.getTransactionXid().setGlobalTccTransactionId(groupId); 35 | transaction.getStatus().setId(Integer.parseInt(status)); 36 | TransactionManager.getInstance().registerTransactionTrace(transaction); 37 | } 38 | return invoker.invoke(invocation); 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/util/ReflectionUtils.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.util; 2 | 3 | import org.aspectj.lang.ProceedingJoinPoint; 4 | import org.aspectj.lang.reflect.MethodSignature; 5 | import org.micro.tcc.tc.annotation.TccTransaction; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | *@author jeff.liu 11 | * 反射工具 12 | * date 2019/7/31 13 | */ 14 | public class ReflectionUtils { 15 | 16 | 17 | public static Method getTccTransactionMethod(ProceedingJoinPoint pjp) { 18 | Method method = ((MethodSignature) (pjp.getSignature())).getMethod(); 19 | 20 | if (method.getAnnotation(TccTransaction.class) == null) { 21 | try { 22 | method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes()); 23 | } catch (NoSuchMethodException e) { 24 | return null; 25 | } 26 | } 27 | return method; 28 | } 29 | 30 | 31 | public static Class getDeclaringType(Class aClass, String methodName, Class[] parameterTypes) { 32 | 33 | Method method = null; 34 | 35 | Class findClass = aClass; 36 | 37 | do { 38 | Class[] clazzes = findClass.getInterfaces(); 39 | 40 | for (Class clazz : clazzes) { 41 | 42 | try { 43 | method = clazz.getDeclaredMethod(methodName, parameterTypes); 44 | } catch (NoSuchMethodException e) { 45 | method = null; 46 | } 47 | 48 | if (method != null) { 49 | return clazz; 50 | } 51 | } 52 | 53 | findClass = findClass.getSuperclass(); 54 | 55 | } while (!findClass.equals(Object.class)); 56 | 57 | return aClass; 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/component/LogAspect.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.component; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.aspectj.lang.JoinPoint; 6 | import org.aspectj.lang.ProceedingJoinPoint; 7 | import org.aspectj.lang.Signature; 8 | import org.aspectj.lang.annotation.After; 9 | import org.aspectj.lang.annotation.Around; 10 | import org.aspectj.lang.annotation.Aspect; 11 | import org.aspectj.lang.annotation.Before; 12 | import org.aspectj.lang.reflect.MethodSignature; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.lang.reflect.Method; 17 | import java.util.Date; 18 | import java.util.HashMap; 19 | 20 | /** 21 | *@author jeff.liu 22 | *@desc 记录日志 23 | *@date 2019/8/14 24 | */ 25 | @Slf4j 26 | public class LogAspect { 27 | 28 | 29 | 30 | /*@Before("execution(* *..car.service..*.*(..))") 31 | public void doBeforeInServiceLayer(JoinPoint joinPoint) { 32 | log.debug("doBeforeInServiceLayer"); 33 | } 34 | 35 | @After("execution(* *..car.service..*.*(..))") 36 | public void doAfterInServiceLayer(JoinPoint joinPoint) { 37 | log.debug("doAfterInServiceLayer"); 38 | }*/ 39 | 40 | @Around("execution(* *..tcc.tc.interceptor..*.*(..))") 41 | public Object doAround(ProceedingJoinPoint pjp) throws Throwable { 42 | // 开始时间 43 | long startTime = 0L; 44 | // 结束时间 45 | long endTime = 0L; 46 | startTime = System.currentTimeMillis(); 47 | Signature signature = pjp.getSignature(); 48 | MethodSignature methodSignature = (MethodSignature) signature; 49 | Method method = methodSignature.getMethod(); 50 | Object result = pjp.proceed(); 51 | endTime = System.currentTimeMillis(); 52 | log.error("doAround>>>method={},result={},耗时2:{}", method.getName(),result, endTime - startTime); 53 | return result; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/component/MicroTccSpringSelector.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.component; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.micro.tcc.tc.annotation.EnableMicroTccTransaction; 5 | import org.springframework.beans.BeansException; 6 | import org.springframework.beans.factory.BeanFactory; 7 | import org.springframework.beans.factory.BeanFactoryAware; 8 | import org.springframework.context.annotation.ImportSelector; 9 | import org.springframework.core.type.AnnotationMetadata; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | *@author jeff.liu 16 | * spring boot 启动管理 17 | * date 2019/8/6 18 | */ 19 | @Slf4j 20 | public class MicroTccSpringSelector implements ImportSelector, BeanFactoryAware { 21 | private BeanFactory beanFactory; 22 | 23 | @Override 24 | public String[] selectImports(AnnotationMetadata importingClassMetadata) { 25 | 26 | boolean isEnable = importingClassMetadata.getAnnotationAttributes(EnableMicroTccTransaction.class.getName()).isEmpty(); 27 | 28 | List importClasses = new ArrayList<>(); 29 | if(isEnable){ 30 | importClasses.add(MicroTccSpringConfig.class.getName()); 31 | String microTcc=" _ _ \n" + 32 | " _ __ ___ (_) ___ _ __ ___ | |_ ___ ___ \n" + 33 | "| '_ ` _ \\| |/ __| '__/ _ \\ _____| __/ __/ __|\n" + 34 | "| | | | | | | (__| | | (_) |_____| || (_| (__ \n" + 35 | "|_| |_| |_|_|\\___|_| \\___/ \\__\\___\\___|\n" + 36 | "** Boot Startup ** \n"; 37 | System.out.println(microTcc); 38 | } 39 | 40 | return importClasses.toArray(new String[0]); 41 | } 42 | 43 | @Override 44 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 45 | this.beanFactory = beanFactory; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/http/RestTemplateInterceptor.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.http; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.micro.tcc.common.constant.Constant; 5 | import org.micro.tcc.common.core.Transaction; 6 | import org.micro.tcc.tc.component.TransactionManager; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 9 | import org.springframework.core.annotation.Order; 10 | import org.springframework.http.HttpHeaders; 11 | import org.springframework.http.HttpRequest; 12 | import org.springframework.http.client.ClientHttpRequestExecution; 13 | import org.springframework.http.client.ClientHttpRequestInterceptor; 14 | import org.springframework.http.client.ClientHttpResponse; 15 | import org.springframework.stereotype.Component; 16 | import org.springframework.web.client.RestTemplate; 17 | 18 | import java.io.IOException; 19 | import java.util.List; 20 | import java.util.Objects; 21 | 22 | /** 23 | * @author jeff.liu 24 | * RestTemplate 拦截器,传递group id 25 | * date 2019/8/6 10:25 26 | */ 27 | @Slf4j 28 | public class RestTemplateInterceptor implements ClientHttpRequestInterceptor { 29 | 30 | 31 | @Override 32 | public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { 33 | log.debug("TCC: RestTemplateIntercetor "); 34 | Transaction transaction = TransactionManager.getInstance().getCurrentTransaction(); 35 | if(null==transaction)return clientHttpRequestExecution.execute(httpRequest,bytes); 36 | String groupId = transaction.getTransactionXid().getGlobalTccTransactionId(); 37 | HttpHeaders headers = httpRequest.getHeaders(); 38 | headers.add(Constant.GLOBAL_TCCTRANSACTION_ID, groupId); 39 | headers.add(Constant.TCCTRANSACTION_STATUS, String.valueOf(transaction.getStatus().value())); 40 | return clientHttpRequestExecution.execute(httpRequest,bytes); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/recover/TransactionRecoverConfig.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.recover; 2 | 3 | 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | *@author jeff.liu 9 | *@desc 事务恢复配置 10 | *@date 2019/8/27 11 | */ 12 | @Component 13 | public class TransactionRecoverConfig implements RecoverConfig { 14 | 15 | public static final TransactionRecoverConfig INSTANCE = new TransactionRecoverConfig(); 16 | 17 | @Value("${micro.tcc.transaction.recover.maxRetryCount:30}") 18 | private int maxRetryCount = 30; 19 | 20 | //160 秒 21 | @Value("${micro.tcc.transaction.recover.recoverDuration:160}") 22 | private int recoverDuration = 160; 23 | @Value("${micro.tcc.transaction.recover.cronExpression:}") 24 | private String cronExpression = ""; 25 | 26 | @Value("${micro.tcc.transaction.recover.zk.cronExpression:}") 27 | private String cronZkExpression = ""; 28 | 29 | public static TransactionRecoverConfig getInstance(){ 30 | return INSTANCE; 31 | } 32 | 33 | 34 | 35 | public TransactionRecoverConfig() { 36 | 37 | } 38 | 39 | public String getCronZkExpression() { 40 | return cronZkExpression; 41 | } 42 | 43 | public void setCronZkExpression(String cronZkExpression) { 44 | this.cronZkExpression = cronZkExpression; 45 | } 46 | 47 | @Override 48 | public int getMaxRetryCount() { 49 | return maxRetryCount; 50 | } 51 | 52 | @Override 53 | public int getRecoverDuration() { 54 | return recoverDuration; 55 | } 56 | 57 | @Override 58 | public String getCronExpression() { 59 | return cronExpression; 60 | } 61 | 62 | 63 | public void setMaxRetryCount(int maxRetryCount) { 64 | this.maxRetryCount = maxRetryCount; 65 | } 66 | 67 | public void setRecoverDuration(int recoverDuration) { 68 | this.recoverDuration = recoverDuration; 69 | } 70 | 71 | public void setCronExpression(String cronExpression) { 72 | this.cronExpression = cronExpression; 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/core/TransactionMember.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.core; 2 | 3 | 4 | import org.micro.tcc.common.constant.TransactionStatus; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | *@author jeff.liu 10 | *@desc 事务参与者 11 | *@date 2019/8/27 12 | */ 13 | public class TransactionMember implements Serializable { 14 | 15 | 16 | private static final long serialVersionUID = 3284377143420086541L; 17 | 18 | /** 19 | * 全局id 20 | */ 21 | private TransactionXid xid; 22 | 23 | /** 24 | * 确认方法调用者 25 | */ 26 | private Invocation confirmInvocation; 27 | 28 | /** 29 | * 取消方法调用者 30 | */ 31 | private Invocation cancelInvocation; 32 | 33 | private MethodReflect methodReflect = new MethodReflect(); 34 | 35 | 36 | public TransactionMember() { 37 | 38 | } 39 | 40 | public TransactionMember(TransactionXid xid, Invocation confirmInvocation, Invocation cancelInvocation) { 41 | this.xid = xid; 42 | this.confirmInvocation = confirmInvocation; 43 | this.cancelInvocation = cancelInvocation; 44 | 45 | } 46 | 47 | public TransactionMember(Invocation confirmInvocation, Invocation cancelInvocation) { 48 | this.confirmInvocation = confirmInvocation; 49 | this.cancelInvocation = cancelInvocation; 50 | 51 | } 52 | 53 | public void setXid(TransactionXid xid) { 54 | this.xid = xid; 55 | } 56 | 57 | public void rollback() { 58 | methodReflect.invoke(new TccTransactionContext(xid, TransactionStatus.CANCEL.value()), cancelInvocation); 59 | } 60 | 61 | public void commit() { 62 | methodReflect.invoke(new TccTransactionContext(xid, TransactionStatus.CONFIRM.value()), confirmInvocation); 63 | } 64 | 65 | public MethodReflect getMethodReflect() { 66 | return methodReflect; 67 | } 68 | 69 | public TransactionXid getXid() { 70 | return xid; 71 | } 72 | 73 | public Invocation getConfirmInvocation() { 74 | return confirmInvocation; 75 | } 76 | 77 | public Invocation getCancelInvocation() { 78 | return cancelInvocation; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/constant/Constant.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.constant; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | *@author jeff.liu 8 | *@desc 静态变量 9 | *@date 2019/8/15 10 | */ 11 | @Component 12 | public class Constant { 13 | 14 | 15 | public static String transactionMapKey; 16 | public static String prefix="TCC:"; 17 | public static String ROOT=":ROOT"; 18 | public static String BRANCH=":BRANCH"; 19 | public static String TRY="TRY"; 20 | public static String CONFIRM="CONFIRM"; 21 | public static String CANCEL="CANCEL"; 22 | public final static String GLOBAL_TCCTRANSACTION_ID="globalTccTransactionId"; 23 | public final static String TCCTRANSACTION_STATUS="tccTransactionStatus"; 24 | 25 | public final static String TRANSACTION_GROUP="TRANSACTION_GROUP:"; 26 | 27 | public final static String TRANSACTION_LOG="TRANSACTION_LOG"; 28 | 29 | public static String datacenterId; 30 | 31 | public static String machineId; 32 | 33 | public static String getTransactionMapKey(){ 34 | return prefix+transactionMapKey; 35 | } 36 | 37 | public final static String DELIMIT="#"; 38 | 39 | public static String IDEMPOTENT; 40 | 41 | public static String IDEMPOTENT_TRUE="true"; 42 | 43 | public static long DELAY=50; 44 | 45 | public static long PERIOD=10; 46 | 47 | public static String getAppName(){ 48 | return transactionMapKey; 49 | } 50 | 51 | @Value("${micro.tcc.datacenterId:1}") 52 | public void setDatacenterId(String datacenterId){ 53 | Constant.datacenterId=datacenterId; 54 | } 55 | 56 | @Value("${micro.tcc.machineId:1}") 57 | public void setMachineId(String machineId){ 58 | Constant.machineId=machineId; 59 | } 60 | 61 | @Value("${spring.application.name}") 62 | public void setTransactionMapKey(String transactionMapKey) { 63 | Constant.transactionMapKey = transactionMapKey; 64 | } 65 | 66 | @Value("${micro.tcc.idempotent:true}") 67 | public void setIdempotent(String idempotent) { 68 | Constant.IDEMPOTENT = idempotent; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/component/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.component; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.cache.annotation.CachingConfigurerSupport; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.data.redis.connection.RedisConnectionFactory; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 12 | import org.springframework.data.redis.serializer.StringRedisSerializer; 13 | 14 | 15 | /** 16 | *@author jeff.liu 17 | *@desc redis 配置 18 | *@date 2019/8/27 19 | */ 20 | @Configuration 21 | public class RedisConfig extends CachingConfigurerSupport { 22 | 23 | // @Bean 24 | // JedisConnectionFactory jedisConnectionFactory() { 25 | // return new JedisConnectionFactory(); 26 | // } 27 | 28 | @Bean(value = "redisTemplate") 29 | public RedisTemplate redisTemplate(RedisConnectionFactory factory) { 30 | RedisTemplate redisTemplate = new RedisTemplate(); 31 | redisTemplate.setConnectionFactory(factory); 32 | StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); 33 | Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); 34 | ObjectMapper om = new ObjectMapper(); 35 | om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 36 | om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 37 | jackson2JsonRedisSerializer.setObjectMapper(om); 38 | redisTemplate.setKeySerializer(stringRedisSerializer); 39 | redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); 40 | /**hash也同样存对象数组*/ 41 | redisTemplate.setHashKeySerializer(stringRedisSerializer); 42 | //redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); 43 | // 开启事务 44 | redisTemplate.setEnableTransactionSupport(true); 45 | redisTemplate.afterPropertiesSet(); 46 | return redisTemplate; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/component/SpringContextAware.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.component; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.core.annotation.Order; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | * spring资源文件读取工具 13 | * 14 | * @author jeff.liu 15 | * date 2016年10月15日 16 | */ 17 | @Configuration 18 | @Order(0) 19 | public class SpringContextAware implements ApplicationContextAware { 20 | 21 | private static ApplicationContext context = null; 22 | 23 | public SpringContextAware() { 24 | super(); 25 | } 26 | 27 | @Override 28 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 29 | context = applicationContext; 30 | } 31 | 32 | /** 33 | * 根据名称获取bean 34 | * @param beanName 35 | * 36 | */ 37 | public static Object getBean(String beanName) { 38 | if(context==null)return null; 39 | return context.getBean(beanName); 40 | } 41 | 42 | /** 43 | * 根据bean名称获取指定类型bean 44 | * @param beanName bean名称 45 | * @param clazz 返回的bean类型,若类型不匹配,将抛出异常 46 | */ 47 | public static T getBean(String beanName, Class clazz) { 48 | if(context==null)return null; 49 | return context.getBean(beanName, clazz); 50 | } 51 | /** 52 | * 根据类型获取bean 53 | * @param clazz 54 | * 55 | */ 56 | public static T getBean(Class clazz) { 57 | if(context==null)return null; 58 | T t = null; 59 | Map map = context.getBeansOfType(clazz); 60 | for (Map.Entry entry : map.entrySet()) { 61 | t = entry.getValue(); 62 | } 63 | return t; 64 | } 65 | 66 | /** 67 | * 是否包含bean 68 | * @param beanName 69 | * 70 | */ 71 | public static boolean containsBean(String beanName) { 72 | if(context==null)return false; 73 | return context.containsBean(beanName); 74 | } 75 | 76 | /** 77 | * 是否是单例 78 | * @param beanName 79 | * 80 | */ 81 | public static boolean isSingleton(String beanName) { 82 | return context.isSingleton(beanName); 83 | } 84 | 85 | /** 86 | * bean的类型 87 | * @param beanName 88 | * 89 | */ 90 | public static Class getType(String beanName) { 91 | return context.getType(beanName); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/core/TransactionRepository.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.core; 2 | 3 | 4 | import java.util.Date; 5 | import java.util.List; 6 | 7 | /** 8 | *@author jeff.liu 9 | *@desc 事务日志接口 10 | *@date 2019/8/27 11 | */ 12 | public interface TransactionRepository { 13 | 14 | 15 | /** 16 | * 创建事务组 17 | * @param transaction 18 | * @return 19 | */ 20 | boolean createGroupMemberForClientCancel(Transaction transaction); 21 | 22 | 23 | boolean createRollbackOrder(Transaction transaction); 24 | 25 | /** 26 | * 创建事务组 27 | * @param transaction 28 | * @return 29 | */ 30 | boolean createGroupMember(Transaction transaction); 31 | 32 | /** 33 | * 事务是否在取消中 34 | * @param transaction 35 | * @return 36 | */ 37 | boolean isCanceling(Transaction transaction); 38 | 39 | boolean createConfirmOrder(Transaction transaction); 40 | 41 | boolean update(Transaction transaction); 42 | 43 | /** 44 | * 删除事务 45 | * @param transaction 46 | * @return 47 | */ 48 | boolean delete(Transaction transaction); 49 | 50 | /** 51 | * 判断幂等性 52 | * @param transaction 53 | * @return 54 | */ 55 | boolean judgeIdempotent(Transaction transaction); 56 | 57 | 58 | 59 | /** 60 | * 查找事务by id 61 | * @param xid 62 | * @return 63 | */ 64 | Transaction findByGroupId(TransactionXid xid); 65 | 66 | /** 67 | * 分布式锁加锁 68 | * @param lockKey 69 | * @param requestId 70 | * @param expireTime 71 | * @return 72 | */ 73 | boolean tryGetDistributedLock(String lockKey, String requestId, long expireTime); 74 | 75 | /** 76 | * 释放分布式锁 77 | * @param lockKey 78 | * @param requestId 79 | * @return 80 | */ 81 | boolean releaseDistributedLock(String lockKey, String requestId); 82 | 83 | /** 84 | * 查找事务 85 | * @param gid 86 | * @return 87 | */ 88 | Transaction findByGroupId(String gid); 89 | 90 | /** 91 | * 查找所有事务 92 | * @return 93 | */ 94 | List findTransactionGroupAll(); 95 | 96 | /** 97 | * 查找所有事务 98 | * @param date 99 | * @return 100 | */ 101 | List findAll(Date date); 102 | } 103 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/core/TransactionXid.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.core; 2 | 3 | 4 | import org.micro.tcc.common.util.SnowFlake; 5 | 6 | import javax.transaction.xa.Xid; 7 | import java.io.Serializable; 8 | import java.nio.ByteBuffer; 9 | import java.util.Arrays; 10 | import java.util.UUID; 11 | 12 | /** 13 | *@author jeff.liu 14 | *@desc 全局id 15 | *@date 2019/8/27 16 | */ 17 | public class TransactionXid implements Serializable { 18 | 19 | 20 | private static final long serialVersionUID = 5549076002296308508L; 21 | 22 | private String globalTccTransactionId; 23 | 24 | 25 | /** 26 | * 生成全局唯一id 27 | */ 28 | public TransactionXid() { 29 | String groupId= SnowFlake.get().nextGroupId(); 30 | globalTccTransactionId=groupId; 31 | 32 | } 33 | public String getGlobalTccTransactionId() { 34 | return globalTccTransactionId; 35 | } 36 | 37 | public void setGlobalTccTransactionId(String globalTccTransactionId) { 38 | this.globalTccTransactionId = globalTccTransactionId; 39 | } 40 | 41 | 42 | public TransactionXid(Object uniqueIdentity) { 43 | UUID branchUuid = null; 44 | if (uniqueIdentity instanceof UUID) { 45 | branchUuid = (UUID) uniqueIdentity; 46 | } else { 47 | try { 48 | branchUuid = UUID.fromString(uniqueIdentity.toString()); 49 | } catch (IllegalArgumentException e) { 50 | byte[] bytes = uniqueIdentity.toString().getBytes(); 51 | if (bytes.length > 16) { 52 | throw new IllegalArgumentException("UniqueIdentify is illegal, the value is :" + uniqueIdentity.toString()); 53 | } 54 | branchUuid = UUID.nameUUIDFromBytes(bytes); 55 | } 56 | } 57 | 58 | 59 | } 60 | 61 | 62 | public TransactionXid(String globalTransactionId) { 63 | this.globalTccTransactionId = globalTransactionId; 64 | 65 | } 66 | public TransactionXid(String globalTransactionId, byte[] branchQualifier) { 67 | this.globalTccTransactionId = globalTransactionId; 68 | 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | StringBuilder stringBuilder = new StringBuilder(); 74 | stringBuilder.append(globalTccTransactionId); 75 | return stringBuilder.toString(); 76 | } 77 | 78 | } 79 | 80 | 81 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/recover/RecoverScheduledZookeeperJob.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.recover; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.lang.StringUtils; 5 | import org.micro.tcc.tc.component.CoordinatorWatcher; 6 | import org.micro.tcc.tc.component.SpringContextAware; 7 | import org.quartz.Scheduler; 8 | import org.quartz.SchedulerFactory; 9 | import org.quartz.impl.StdSchedulerFactory; 10 | import org.springframework.boot.ApplicationArguments; 11 | import org.springframework.boot.ApplicationRunner; 12 | import org.springframework.scheduling.quartz.CronTriggerFactoryBean; 13 | import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean; 14 | 15 | 16 | /** 17 | *@author jeff.liu 18 | *@desc zk事件调用 19 | *@date 2019/8/19 20 | */ 21 | @Slf4j 22 | public class RecoverScheduledZookeeperJob implements ApplicationRunner { 23 | 24 | 25 | private Scheduler scheduler; 26 | 27 | public void init() { 28 | 29 | TransactionRecoverConfig transactionConfigurator= SpringContextAware.getBean(TransactionRecoverConfig.class); 30 | 31 | try { 32 | if(StringUtils.isEmpty(transactionConfigurator.getCronZkExpression())){ 33 | return; 34 | } 35 | 36 | CoordinatorWatcher coordinatorWatcher= SpringContextAware.getBean(CoordinatorWatcher.class); 37 | MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean(); 38 | jobDetail.setTargetObject(coordinatorWatcher); 39 | jobDetail.setTargetMethod("processTransactionStart"); 40 | jobDetail.setName("transactionRecoveryZKJob"); 41 | jobDetail.setConcurrent(false); 42 | jobDetail.afterPropertiesSet(); 43 | 44 | CronTriggerFactoryBean cronTrigger = new CronTriggerFactoryBean(); 45 | cronTrigger.setBeanName("transactionRecoveryZKCronTrigger"); 46 | cronTrigger.setCronExpression(transactionConfigurator.getCronZkExpression()); 47 | cronTrigger.setJobDetail(jobDetail.getObject()); 48 | cronTrigger.afterPropertiesSet(); 49 | SchedulerFactory schedulerfactory = new StdSchedulerFactory(); 50 | scheduler=schedulerfactory.getScheduler(); 51 | scheduler.scheduleJob(jobDetail.getObject(), cronTrigger.getObject()); 52 | 53 | scheduler.start(); 54 | log.debug("TCC:开启处理Zookeeper事件定时任务"); 55 | 56 | } catch (Exception e) { 57 | log.error("TCC:处理Zookeeper事件定时任务发生异常!",e); 58 | } 59 | } 60 | 61 | 62 | 63 | 64 | 65 | @Override 66 | public void run(ApplicationArguments args) throws Exception { 67 | init(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/recover/RecoverScheduledJob.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.recover; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.lang.StringUtils; 5 | import org.micro.tcc.tc.component.SpringContextAware; 6 | import org.quartz.Scheduler; 7 | import org.quartz.SchedulerFactory; 8 | import org.quartz.impl.StdSchedulerFactory; 9 | import org.springframework.boot.ApplicationArguments; 10 | import org.springframework.boot.ApplicationRunner; 11 | import org.springframework.scheduling.quartz.CronTriggerFactoryBean; 12 | import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean; 13 | 14 | 15 | /** 16 | *@author jeff.liu 17 | *@desc 事务恢复定时任务 18 | *@date 2019/8/27 19 | */ 20 | @Slf4j 21 | public class RecoverScheduledJob implements ApplicationRunner { 22 | 23 | private TransactionRecovery transactionRecovery=new TransactionRecovery(); 24 | 25 | 26 | private Scheduler scheduler; 27 | 28 | public void init() { 29 | 30 | try { 31 | 32 | 33 | TransactionRecoverConfig transactionConfigurator= SpringContextAware.getBean(TransactionRecoverConfig.class); 34 | if(StringUtils.isEmpty(transactionConfigurator.getCronExpression())){ 35 | return; 36 | } 37 | MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean(); 38 | jobDetail.setTargetObject(transactionRecovery); 39 | jobDetail.setTargetMethod("beginRecover"); 40 | jobDetail.setName("transactionRecoveryJob"); 41 | jobDetail.setConcurrent(false); 42 | jobDetail.afterPropertiesSet(); 43 | 44 | CronTriggerFactoryBean cronTrigger = new CronTriggerFactoryBean(); 45 | cronTrigger.setBeanName("transactionRecoveryCronTrigger"); 46 | cronTrigger.setCronExpression(transactionConfigurator.getCronExpression()); 47 | cronTrigger.setJobDetail(jobDetail.getObject()); 48 | cronTrigger.afterPropertiesSet(); 49 | SchedulerFactory schedulerfactory = new StdSchedulerFactory(); 50 | scheduler=schedulerfactory.getScheduler(); 51 | scheduler.scheduleJob(jobDetail.getObject(), cronTrigger.getObject()); 52 | scheduler.start(); 53 | log.debug("TCC:开启分布式事务定时恢复任务"); 54 | 55 | } catch (Exception e) { 56 | log.error("TCC:定时commit/cancel事务发生异常!",e); 57 | } 58 | } 59 | 60 | public void setTransactionRecovery(TransactionRecovery transactionRecovery) { 61 | this.transactionRecovery = transactionRecovery; 62 | } 63 | 64 | 65 | 66 | @Override 67 | public void run(ApplicationArguments args) throws Exception { 68 | init(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/support/BeanFactoryBuilder.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.support; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | *@author jeff.liu 9 | * 描述 bean工厂 10 | * date 2019/7/31 11 | */ 12 | public final class BeanFactoryBuilder { 13 | 14 | 15 | private BeanFactoryBuilder() { 16 | 17 | } 18 | 19 | private static List beanFactories = new ArrayList(); 20 | 21 | private static ConcurrentHashMap classFactoryMap = new ConcurrentHashMap(); 22 | 23 | public static SingeltonFactory factoryBean(Class clazz) { 24 | 25 | if (!classFactoryMap.containsKey(clazz)) { 26 | 27 | for (BeanFactory beanFactory : beanFactories) { 28 | if (beanFactory.isFactoryBean(clazz)) { 29 | //从spring bean容器取出 30 | classFactoryMap.putIfAbsent(clazz, new SingeltonFactory(clazz, beanFactory.getBean(clazz))); 31 | } 32 | } 33 | //如果spring 容器没有,则从classloader实例化一个对象 34 | if (!classFactoryMap.containsKey(clazz)) { 35 | classFactoryMap.putIfAbsent(clazz, new SingeltonFactory(clazz)); 36 | } 37 | } 38 | 39 | return classFactoryMap.get(clazz); 40 | } 41 | 42 | public static void registerBeanFactory(BeanFactory beanFactory) { 43 | beanFactories.add(beanFactory); 44 | } 45 | 46 | public static class SingeltonFactory { 47 | 48 | private volatile T instance = null; 49 | 50 | private String className; 51 | 52 | public SingeltonFactory(Class clazz, T instance) { 53 | this.className = clazz.getName(); 54 | this.instance = instance; 55 | } 56 | 57 | public SingeltonFactory(Class clazz) { 58 | this.className = clazz.getName(); 59 | } 60 | 61 | public T getInstance() { 62 | //如果sping容器为null,则从classloader 实例化一个对象 63 | if (instance == null) { 64 | synchronized (SingeltonFactory.class) { 65 | if (instance == null) { 66 | try { 67 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); 68 | 69 | Class clazz = loader.loadClass(className); 70 | 71 | instance = (T) clazz.newInstance(); 72 | } catch (Exception e) { 73 | throw new RuntimeException("Failed to create an instance of " + className, e); 74 | } 75 | } 76 | } 77 | } 78 | 79 | return instance; 80 | } 81 | 82 | } 83 | } -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/interceptor/TccMethodContext.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.interceptor; 2 | 3 | import org.aspectj.lang.ProceedingJoinPoint; 4 | 5 | import org.micro.tcc.common.constant.Propagation; 6 | 7 | import org.micro.tcc.common.core.TccTransactionContext; 8 | 9 | import org.micro.tcc.common.constant.MethodType; 10 | import org.micro.tcc.tc.util.ReflectionUtils; 11 | import org.micro.tcc.tc.annotation.TccIdentity; 12 | import org.micro.tcc.tc.annotation.TccTransaction; 13 | 14 | import java.lang.annotation.Annotation; 15 | import java.lang.reflect.Method; 16 | 17 | /** 18 | *@author jeff.liu 19 | * tcc 方法内容 20 | * date 2019/7/31 21 | */ 22 | public class TccMethodContext { 23 | 24 | ProceedingJoinPoint pjp = null; 25 | 26 | Method method = null; 27 | 28 | TccTransaction tccTransaction = null; 29 | 30 | Propagation propagation = null; 31 | 32 | TccTransactionContext transactionContext = null; 33 | 34 | public TccMethodContext(ProceedingJoinPoint pjp) { 35 | this.pjp = pjp; 36 | this.method = ReflectionUtils.getTccTransactionMethod(pjp); 37 | this.tccTransaction = method.getAnnotation(TccTransaction.class); 38 | this.propagation = tccTransaction.propagation(); 39 | 40 | } 41 | 42 | public TccTransaction getAnnotation() { 43 | return tccTransaction; 44 | } 45 | 46 | public Propagation getPropagation() { 47 | return propagation; 48 | } 49 | 50 | public TccTransactionContext getTransactionContext() { 51 | return transactionContext; 52 | } 53 | 54 | public Method getMethod() { 55 | return method; 56 | } 57 | 58 | public Object getUniqueIdentity() { 59 | Annotation[][] annotations = this.getMethod().getParameterAnnotations(); 60 | 61 | for (int i = 0; i < annotations.length; i++) { 62 | for (Annotation annotation : annotations[i]) { 63 | if (annotation.annotationType().equals(TccIdentity.class)) { 64 | 65 | Object[] params = pjp.getArgs(); 66 | Object unqiueIdentity = params[i]; 67 | 68 | return unqiueIdentity; 69 | } 70 | } 71 | } 72 | 73 | return null; 74 | } 75 | 76 | 77 | 78 | 79 | public MethodType getMethodRole(boolean isTransactionActive) { 80 | if ((propagation.equals(Propagation.REQUIRED) ) || 81 | propagation.equals(Propagation.REQUIRES_NEW)) { 82 | return MethodType.ROOT; 83 | } else if ((propagation.equals(Propagation.SUPPORTS) || propagation.equals(Propagation.MANDATORY))&& isTransactionActive ) { 84 | return MethodType.PROVIDER; 85 | } else { 86 | return MethodType.NORMAL; 87 | } 88 | } 89 | 90 | public Object proceed() throws Throwable { 91 | return this.pjp.proceed(); 92 | } 93 | } -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/serializer/KryoPoolSerializer.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.serializer; 2 | 3 | import com.esotericsoftware.kryo.Kryo; 4 | import com.esotericsoftware.kryo.io.Input; 5 | import com.esotericsoftware.kryo.io.Output; 6 | import com.esotericsoftware.kryo.pool.KryoCallback; 7 | import com.esotericsoftware.kryo.pool.KryoFactory; 8 | import com.esotericsoftware.kryo.pool.KryoPool; 9 | import org.objenesis.strategy.StdInstantiatorStrategy; 10 | 11 | import java.io.ByteArrayInputStream; 12 | import java.io.ByteArrayOutputStream; 13 | 14 | /** 15 | *@author jeff.liu 16 | * 描述 kryo 序列化 17 | * date 2019/7/31 18 | */ 19 | public class KryoPoolSerializer implements ObjectSerializer { 20 | 21 | 22 | static KryoFactory factory = new KryoFactory() { 23 | public Kryo create() { 24 | Kryo kryo = new Kryo(); 25 | kryo.setReferences(true); 26 | kryo.setRegistrationRequired(false); 27 | //Fix the NPE bug when deserializing Collections. 28 | ((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy()) 29 | .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy()); 30 | 31 | return kryo; 32 | } 33 | }; 34 | 35 | 36 | KryoPool pool = new KryoPool.Builder(factory).softReferences().build(); 37 | 38 | private int initPoolSize = 300; 39 | 40 | public KryoPoolSerializer() { 41 | init(); 42 | } 43 | 44 | public KryoPoolSerializer(int initPoolSize) { 45 | this.initPoolSize = initPoolSize; 46 | init(); 47 | } 48 | 49 | private void init() { 50 | 51 | for (int i = 0; i < initPoolSize; i++) { 52 | Kryo kryo = pool.borrow(); 53 | pool.release(kryo); 54 | } 55 | } 56 | 57 | @Override 58 | public byte[] serialize(final T object) { 59 | 60 | return pool.run(new KryoCallback() { 61 | public byte[] execute(Kryo kryo) { 62 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 63 | Output output = new Output(byteArrayOutputStream); 64 | 65 | kryo.writeClassAndObject(output, object); 66 | output.flush(); 67 | 68 | return byteArrayOutputStream.toByteArray(); 69 | } 70 | }); 71 | } 72 | 73 | @Override 74 | public T deserialize(final byte[] bytes) { 75 | 76 | return pool.run(new KryoCallback() { 77 | public T execute(Kryo kryo) { 78 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); 79 | Input input = new Input(byteArrayInputStream); 80 | 81 | return (T) kryo.readClassAndObject(input); 82 | } 83 | }); 84 | } 85 | 86 | @Override 87 | public T clone(final T object) { 88 | return pool.run(new KryoCallback() { 89 | public T execute(Kryo kryo) { 90 | return kryo.copy(object); 91 | } 92 | }); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/interceptor/TccCoordinatorInterceptor.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.interceptor; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.ProceedingJoinPoint; 5 | import org.micro.tcc.tc.annotation.TccTransaction; 6 | import org.micro.tcc.common.core.TransactionXid; 7 | import org.micro.tcc.common.core.Invocation; 8 | import org.micro.tcc.common.core.TransactionMember; 9 | import org.micro.tcc.common.core.Transaction; 10 | import org.micro.tcc.tc.util.ReflectionUtils; 11 | import org.micro.tcc.tc.component.TransactionManager; 12 | 13 | 14 | import java.lang.reflect.Method; 15 | 16 | /** 17 | *@author jeff.liu 18 | * 参与者拦截后具体实现 19 | * date 2019/7/31 20 | */ 21 | @Slf4j 22 | public class TccCoordinatorInterceptor { 23 | 24 | private TransactionManager transactionManager= TransactionManager.getInstance(); 25 | 26 | 27 | public void setTransactionManager(TransactionManager transactionManager) { 28 | this.transactionManager = transactionManager; 29 | } 30 | 31 | public Object interceptTransactionContextMethod(ProceedingJoinPoint pjp) throws Throwable { 32 | 33 | Transaction transaction = transactionManager.getCurrentTransaction(); 34 | 35 | if (transaction != null) { 36 | 37 | switch (transaction.getStatus()) { 38 | case TRY: 39 | addingParticipants(pjp); 40 | break; 41 | case CONFIRM: 42 | break; 43 | case CANCEL: 44 | break; 45 | default: 46 | break; 47 | } 48 | } 49 | 50 | 51 | return pjp.proceed(pjp.getArgs()); 52 | } 53 | 54 | private void addingParticipants(ProceedingJoinPoint pjp) throws IllegalAccessException, InstantiationException { 55 | 56 | Method method = ReflectionUtils.getTccTransactionMethod(pjp); 57 | if (method == null) { 58 | throw new RuntimeException(String.format("join point not found method, point is : %s", pjp.getSignature().getName())); 59 | } 60 | TccTransaction tccTransaction = method.getAnnotation(TccTransaction.class); 61 | 62 | String confirmMethodName = tccTransaction.confirmMethod(); 63 | String cancelMethodName = tccTransaction.cancelMethod(); 64 | 65 | Transaction transaction = transactionManager.getCurrentTransaction(); 66 | TransactionXid xid = new TransactionXid(transaction.getTransactionXid().getGlobalTccTransactionId()); 67 | 68 | Class targetClass = ReflectionUtils.getDeclaringType(pjp.getTarget().getClass(), method.getName(), method.getParameterTypes()); 69 | Invocation confirmInvocation = new Invocation(targetClass, confirmMethodName, method.getParameterTypes(), pjp.getArgs()); 70 | Invocation cancelInvocation = new Invocation(targetClass, cancelMethodName, method.getParameterTypes(), pjp.getArgs()); 71 | TransactionMember participant = new TransactionMember(xid, confirmInvocation, cancelInvocation); 72 | transactionManager.addingParticipants(participant); 73 | 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/util/TransactionSerializer.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.util; 2 | 3 | 4 | import com.alibaba.fastjson.JSON; 5 | import org.apache.commons.lang3.time.DateFormatUtils; 6 | import org.apache.commons.lang3.time.DateUtils; 7 | import org.micro.tcc.common.core.Transaction; 8 | import org.micro.tcc.common.constant.TransactionStatus; 9 | import org.micro.tcc.common.exception.TccSystemErrorException; 10 | import org.micro.tcc.common.serializer.ObjectSerializer; 11 | import org.micro.tcc.common.util.ByteUtils; 12 | 13 | import java.text.ParseException; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | /** 18 | *@author jeff.liu 19 | * 事务序列化工具 20 | * date 2019/7/31 21 | */ 22 | public class TransactionSerializer { 23 | 24 | public static Map serialize(ObjectSerializer serializer, Transaction transaction) { 25 | 26 | Map map = new HashMap(); 27 | 28 | map.put("global_transaction_id".getBytes(), transaction.getTransactionXid().getGlobalTccTransactionId().getBytes()); 29 | /* map.put("ASYNC_CONFIRM".getBytes(), transaction.getAsyncConfirm().getBytes()); 30 | map.put("ASYNC_CANCEL".getBytes(), transaction.getAsyncCancel().getBytes());*/ 31 | map.put("status".getBytes(), ByteUtils.intToBytes(transaction.getStatus().value())); 32 | map.put("transaction_type".getBytes(), ByteUtils.intToBytes(transaction.getTransactionType().value())); 33 | map.put("retry_count".getBytes(), ByteUtils.intToBytes(transaction.getRetriedCount())); 34 | map.put("create_time".getBytes(), DateFormatUtils.format(transaction.getCreateTime(), "yyyy-MM-dd HH:mm:ss").getBytes()); 35 | map.put("update_time".getBytes(), DateFormatUtils.format(transaction.getLastUpdateTime(), "yyyy-MM-dd HH:mm:ss").getBytes()); 36 | map.put("version".getBytes(), ByteUtils.longToBytes(transaction.getVersion())); 37 | map.put("content".getBytes(), serializer.serialize(transaction)); 38 | map.put("content_text".getBytes(), JSON.toJSONString(transaction).getBytes()); 39 | return map; 40 | } 41 | 42 | public static Transaction deserialize(ObjectSerializer serializer, Map map1) { 43 | 44 | Map propertyMap = new HashMap(); 45 | 46 | for (Map.Entry entry : map1.entrySet()) { 47 | propertyMap.put(new String(entry.getKey()), entry.getValue()); 48 | } 49 | 50 | byte[] content = propertyMap.get("content"); 51 | Transaction transaction = (Transaction) serializer.deserialize(content); 52 | transaction.changeStatus(TransactionStatus.valueOf(ByteUtils.bytesToInt(propertyMap.get("status")))); 53 | transaction.resetRetriedCount(ByteUtils.bytesToInt(propertyMap.get("retry_count"))); 54 | 55 | try { 56 | transaction.setLastUpdateTime(DateUtils.parseDate(new String(propertyMap.get("update_time")), "yyyy-MM-dd HH:mm:ss")); 57 | } catch (ParseException e) { 58 | throw new TccSystemErrorException(e); 59 | } 60 | transaction.setVersion(ByteUtils.bytesToLong(propertyMap.get("version"))); 61 | return transaction; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/interceptor/WebControllerAspect.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.interceptor; 2 | 3 | import org.micro.tcc.common.constant.Constant; 4 | import org.micro.tcc.common.core.Transaction; 5 | import org.micro.tcc.common.constant.TransactionType; 6 | import org.micro.tcc.tc.component.TransactionManager; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.web.servlet.ModelAndView; 10 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.util.Enumeration; 15 | 16 | 17 | /** 18 | *@author jeff.liu 19 | * web Controller 拦截器 20 | * date 2019/7/31 21 | */ 22 | public class WebControllerAspect extends HandlerInterceptorAdapter { 23 | 24 | private static Logger log = LoggerFactory.getLogger(WebControllerAspect.class); 25 | 26 | 27 | 28 | @Override 29 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 30 | try{ 31 | log.debug("TCC:WebControllerAspect "); 32 | Enumeration headerNames = request.getHeaderNames(); 33 | Transaction transaction=new Transaction(TransactionType.BRANCH); 34 | int count=0; 35 | if (headerNames != null) { 36 | while (headerNames.hasMoreElements()) { 37 | String name = headerNames.nextElement(); 38 | String headerValue = request.getHeader(name); 39 | /** 遍历请求头里面的属性字段,将group id添加到新的请求头中转发到下游服务 */ 40 | if (Constant.GLOBAL_TCCTRANSACTION_ID.equalsIgnoreCase(name)) { 41 | log.debug("TCC:添加自定义请求头key:" + name + ",value:" + headerValue); 42 | transaction.getTransactionXid().setGlobalTccTransactionId(headerValue); 43 | count++; 44 | } 45 | if (Constant.TCCTRANSACTION_STATUS.equalsIgnoreCase(name)) { 46 | log.debug("TCC:添加自定义请求头key:" + name + ",value:" + headerValue); 47 | transaction.getStatus().setId(Integer.parseInt(headerValue)); 48 | count++; 49 | } 50 | if(count>=2){ 51 | break; 52 | } 53 | } 54 | } 55 | if(count==2){ 56 | TransactionManager.getInstance().registerTransactionTrace(transaction); 57 | } 58 | 59 | }catch(Exception e){ 60 | log.warn("globalTccTransactionId interceptor fail!",e); 61 | } 62 | return true; 63 | } 64 | 65 | @Override 66 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 67 | super.postHandle(request, response, handler, modelAndView); 68 | } 69 | 70 | @Override 71 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 72 | super.afterCompletion(request, response, handler, ex); 73 | } 74 | 75 | @Override 76 | public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 77 | super.afterConcurrentHandlingStarted(request, response, handler); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/component/MicroTccSpringConfig.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.component; 2 | 3 | import feign.RequestInterceptor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.apache.http.HttpRequestInterceptor; 6 | import org.micro.tcc.common.constant.Constant; 7 | import org.micro.tcc.common.core.Transaction; 8 | import org.micro.tcc.tc.http.HttpClientRequestInterceptor; 9 | import org.micro.tcc.tc.http.RestTemplateInterceptor; 10 | import org.micro.tcc.tc.recover.RecoverScheduledJob; 11 | import org.micro.tcc.tc.recover.RecoverScheduledZookeeperJob; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 14 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 15 | import org.springframework.context.annotation.Bean; 16 | import org.springframework.context.annotation.ComponentScan; 17 | import org.springframework.http.client.ClientHttpRequestInterceptor; 18 | import org.springframework.web.client.RestTemplate; 19 | 20 | import java.util.Collections; 21 | 22 | /** 23 | * @author jeff.liu 24 | * 25 | * date 2019/8/6 15:01 26 | */ 27 | // @Configuration 28 | @ComponentScan(basePackages = {"org.micro.tcc.**"}) 29 | @Slf4j 30 | public class MicroTccSpringConfig { 31 | 32 | 33 | @Bean 34 | public CoordinatorWatcher coordinatorWatcher() throws Exception { 35 | System.setProperty("jute.maxbuffer", String.valueOf(4096 * 1024 * 3)); 36 | CoordinatorWatcher coordinatorWatcher=new CoordinatorWatcher(); 37 | return coordinatorWatcher; 38 | } 39 | 40 | @Bean 41 | public RecoverScheduledJob recoverScheduledJob(){ 42 | RecoverScheduledJob recoverScheduledJob=new RecoverScheduledJob(); 43 | return recoverScheduledJob; 44 | } 45 | 46 | @Bean 47 | @ConditionalOnClass(CoordinatorWatcher.class) 48 | public RecoverScheduledZookeeperJob recoverScheduledZookeeperJob(){ 49 | RecoverScheduledZookeeperJob recoverScheduledJob=new RecoverScheduledZookeeperJob(); 50 | return recoverScheduledJob; 51 | } 52 | 53 | @Bean 54 | @ConditionalOnClass(HttpRequestInterceptor.class) 55 | public HttpRequestInterceptor httpClientRequestInterceptor(){ 56 | return new HttpClientRequestInterceptor(); 57 | } 58 | 59 | @Bean 60 | @ConditionalOnClass(ClientHttpRequestInterceptor.class) 61 | public ClientHttpRequestInterceptor clientHttpRequestInterceptor(){ 62 | return new RestTemplateInterceptor(); 63 | } 64 | 65 | /*@Bean 66 | //@ConditionalOnClass 67 | @ConditionalOnBean(ClientHttpRequestInterceptor.class) 68 | public RestTemplate restTemplate(ClientHttpRequestInterceptor clientHttpRequestInterceptor){ 69 | RestTemplate restTemplate = new RestTemplate(); 70 | restTemplate.setInterceptors(Collections.singletonList(clientHttpRequestInterceptor)); 71 | return restTemplate; 72 | }*/ 73 | 74 | @Bean 75 | public RequestInterceptor requestInterceptor() { 76 | return requestTemplate -> { 77 | log.debug("TCC:Feign interceptor "); 78 | Transaction transaction = TransactionManager.getInstance().getCurrentTransaction(); 79 | if(null==transaction){ 80 | return; 81 | } 82 | String gid = transaction.getTransactionXid().getGlobalTccTransactionId(); 83 | requestTemplate.header(Constant.GLOBAL_TCCTRANSACTION_ID, gid); 84 | requestTemplate.header(Constant.TCCTRANSACTION_STATUS, String.valueOf(transaction.getStatus().value())); 85 | }; 86 | } 87 | 88 | 89 | } -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/recover/TransactionRecovery.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.recover; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.apache.commons.lang.exception.ExceptionUtils; 6 | 7 | import org.micro.tcc.common.constant.TransactionStatus; 8 | import org.micro.tcc.common.constant.TransactionType; 9 | import org.micro.tcc.common.core.Transaction; 10 | import org.micro.tcc.common.core.TransactionRepository; 11 | import org.micro.tcc.tc.repository.RedisSpringTransactionRepository; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | 16 | import java.util.Calendar; 17 | import java.util.Date; 18 | import java.util.List; 19 | 20 | 21 | /** 22 | *@author jeff.liu 23 | *@desc 事务恢复具体实现 24 | *@date 2019/8/27 25 | */ 26 | @Slf4j 27 | public class TransactionRecovery { 28 | 29 | 30 | public void beginRecover() { 31 | log.debug("TCC:准备进行事务恢复"); 32 | List transactions = loadExceptionTransactions(); 33 | if(null!=transactions) recoverExceptionTransactions(transactions); 34 | cleanZookeeperNodeData(); 35 | log.debug("TCC:事务恢复成功结束"); 36 | } 37 | 38 | private void cleanZookeeperNodeData(){ 39 | 40 | } 41 | private List loadExceptionTransactions() { 42 | long currentTimeInMillis = Calendar.getInstance().getTimeInMillis(); 43 | 44 | TransactionRepository transactionRepository = RedisSpringTransactionRepository.getInstance(); 45 | 46 | return transactionRepository.findAll(new Date(currentTimeInMillis)); 47 | 48 | } 49 | 50 | private void recoverExceptionTransactions(List transactions) { 51 | 52 | TransactionRepository transactionRepository = RedisSpringTransactionRepository.getInstance(); 53 | for (Transaction transaction : transactions) { 54 | 55 | if (transaction.getRetriedCount() > TransactionRecoverConfig.getInstance().getMaxRetryCount()) { 56 | 57 | log.error(String.format("TCC:recover failed with max retry count,will not try again. txid:%s, status:%s,retried count:%d,transaction content:%s", transaction.getTransactionXid(), transaction.getStatus().value(), transaction.getRetriedCount(), JSON.toJSONString(transaction))); 58 | continue; 59 | } 60 | if (TransactionRecoverConfig.getInstance().getMaxRetryCount()>5 && (transaction.getCreateTime().getTime() + 61 | TransactionRecoverConfig.getInstance().getMaxRetryCount() * 62 | TransactionRecoverConfig.getInstance().getRecoverDuration() > System.currentTimeMillis())){ 63 | continue; 64 | } 65 | 66 | try { 67 | transaction.addRetriedCount(); 68 | if (transaction.getStatus().equals(TransactionStatus.CONFIRM)) { 69 | //transaction.changeStatus(TransactionStatus.CONFIRM); 70 | //transactionRepository.update(transaction); 71 | transaction.commit(); 72 | transactionRepository.delete(transaction); 73 | 74 | } else if (transaction.getStatus().equals(TransactionStatus.CANCEL)) { 75 | //transaction.changeStatus(TransactionStatus.CANCEL); 76 | //transactionRepository.update(transaction); 77 | transaction.rollback(); 78 | transactionRepository.delete(transaction); 79 | } 80 | 81 | } catch (Throwable throwable) { 82 | log.error(String.format("TCC:recover failed, txid:%s, status:%s,retried count:%d,transaction content:%s", transaction.getTransactionXid(), transaction.getStatus().value(), transaction.getRetriedCount(), JSON.toJSONString(transaction)), throwable); 83 | 84 | } 85 | } 86 | } 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /micro-tcc-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.github.mytcctransaction 8 | micro-tcc 9 | 1.5.1 10 | 11 | 12 | 13 | micro-tcc-common 14 | micro-tcc-common 15 | jar 16 | micro-tcc-common project for Spring Boot 17 | 18 | 19 | 20 | 21 | 22 | com.alibaba 23 | druid 24 | 1.1.12 25 | 26 | 27 | mysql 28 | mysql-connector-java 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-actuator 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-starter-openfeign 44 | 45 | 46 | 47 | org.springframework.cloud 48 | spring-cloud-starter-netflix-hystrix 49 | 50 | 51 | 52 | com.google.guava 53 | guava 54 | 28.0-jre 55 | 56 | 57 | 58 | org.mybatis.spring.boot 59 | mybatis-spring-boot-starter 60 | 1.3.2 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.curator 73 | curator-framework 74 | 4.0.1 75 | 76 | 77 | org.apache.curator 78 | curator-recipes 79 | 4.0.1 80 | 81 | 82 | 83 | org.apache.cxf 84 | cxf-api 85 | 2.6.1 86 | 87 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-data-redis 95 | 96 | 97 | org.apache.commons 98 | commons-lang3 99 | 3.4 100 | 101 | 102 | com.esotericsoftware 103 | kryo-shaded 104 | 4.0.0 105 | 106 | 107 | com.alibaba 108 | fastjson 109 | 1.2.58 110 | 111 | 112 | com.esotericsoftware 113 | reflectasm 114 | 1.11.0 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/util/SnowFlake.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.util; 2 | 3 | import org.micro.tcc.common.constant.Constant; 4 | 5 | import java.text.ParseException; 6 | import java.util.Date; 7 | 8 | /** 9 | *@author jeff.liu 10 | *@desc 分布式全局id 11 | *@date 2019/8/28 12 | */ 13 | public class SnowFlake { 14 | 15 | /** 16 | * 起始的时间戳 17 | */ 18 | private final static long START_STMP = 1480166465631L; 19 | 20 | /** 21 | * 每一部分占用的位数 22 | */ 23 | private final static long SEQUENCE_BIT = 12; //序列号占用的位数 24 | private final static long MACHINE_BIT = 5; //机器标识占用的位数 25 | private final static long DATACENTER_BIT = 5;//数据中心占用的位数 26 | 27 | /** 28 | * 每一部分的最大值 29 | */ 30 | private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); 31 | private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); 32 | private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); 33 | 34 | /** 35 | * 每一部分向左的位移 36 | */ 37 | private final static long MACHINE_LEFT = SEQUENCE_BIT; 38 | private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; 39 | private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; 40 | 41 | private long datacenterId; //数据中心 42 | private long machineId; //机器标识 43 | private long sequence = 0L; //序列号 44 | private long lastStmp = -1L;//上一次时间戳 45 | 46 | private volatile static SnowFlake snowFlake=null; 47 | 48 | public static SnowFlake get() { 49 | if (null == snowFlake) { 50 | synchronized (SnowFlake.class) { 51 | if (null == snowFlake) { 52 | long datacenterId=1; 53 | long machineId=1; 54 | try{ 55 | datacenterId= Long.parseLong(Constant.datacenterId); 56 | machineId=Long.parseLong(Constant.machineId); 57 | }catch (Exception e){} 58 | snowFlake = new SnowFlake(datacenterId,machineId); 59 | } 60 | } 61 | } 62 | return snowFlake; 63 | } 64 | 65 | 66 | public SnowFlake(long datacenterId, long machineId) { 67 | if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { 68 | throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); 69 | } 70 | if (machineId > MAX_MACHINE_NUM || machineId < 0) { 71 | throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); 72 | } 73 | this.datacenterId = datacenterId; 74 | this.machineId = machineId; 75 | } 76 | 77 | public String nextGroupId(){ 78 | long id=snowFlake.nextId(); 79 | String dateStr= ""; 80 | try { 81 | dateStr = TimeUtils.convertToString(); 82 | } catch (ParseException e) { 83 | 84 | } 85 | return dateStr+id; 86 | } 87 | /** 88 | * 产生下一个ID 89 | * 90 | * @return 91 | */ 92 | public synchronized long nextId() { 93 | long currStmp = getNewstmp(); 94 | if (currStmp < lastStmp) { 95 | throw new RuntimeException("Clock moved backwards. Refusing to generate id"); 96 | } 97 | 98 | if (currStmp == lastStmp) { 99 | //相同毫秒内,序列号自增 100 | sequence = (sequence + 1) & MAX_SEQUENCE; 101 | //同一毫秒的序列数已经达到最大 102 | if (sequence == 0L) { 103 | currStmp = getNextMill(); 104 | } 105 | } else { 106 | //不同毫秒内,序列号置为0 107 | sequence = 0L; 108 | } 109 | 110 | lastStmp = currStmp; 111 | 112 | return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分 113 | | datacenterId << DATACENTER_LEFT //数据中心部分 114 | | machineId << MACHINE_LEFT //机器标识部分 115 | | sequence; //序列号部分 116 | } 117 | 118 | private long getNextMill() { 119 | long mill = getNewstmp(); 120 | while (mill <= lastStmp) { 121 | mill = getNewstmp(); 122 | } 123 | return mill; 124 | } 125 | 126 | private long getNewstmp() { 127 | return System.currentTimeMillis(); 128 | } 129 | 130 | public static void main(String[] args) { 131 | SnowFlake snowFlake = new SnowFlake(1, 1); 132 | 133 | long start = System.currentTimeMillis(); 134 | for (int i = 0; i < 100; i++) { 135 | System.out.println(snowFlake.nextId()); 136 | } 137 | 138 | System.out.println(System.currentTimeMillis() - start); 139 | 140 | 141 | } 142 | } -------------------------------------------------------------------------------- /micro-tcc-common/src/main/java/org/micro/tcc/common/core/Transaction.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.common.core; 2 | 3 | 4 | 5 | import org.micro.tcc.common.constant.Model; 6 | import org.micro.tcc.common.constant.TransactionStatus; 7 | import org.micro.tcc.common.constant.TransactionType; 8 | 9 | import java.io.Serializable; 10 | import java.util.ArrayList; 11 | import java.util.Date; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | 17 | /** 18 | *@author jeff.liu 19 | *@desc 分布式事务 20 | *@date 2019/8/27 21 | */ 22 | public class Transaction implements Serializable { 23 | 24 | private static final long serialVersionUID = 776032890445193092L; 25 | 26 | private boolean asyncConfirm =false; 27 | 28 | private boolean asyncCancel=false; 29 | 30 | private int model= Model.AT.value(); 31 | 32 | private Class[] rollbackFor= new Class[]{Throwable.class}; 33 | 34 | 35 | /** 36 | * 全局id 37 | */ 38 | private TransactionXid xid; 39 | 40 | /** 41 | * 事务状态 42 | */ 43 | private TransactionStatus status; 44 | 45 | /** 46 | * 事务类型 47 | */ 48 | private TransactionType transactionType; 49 | 50 | /** 51 | * 重试次数 52 | */ 53 | private volatile int retriedCount = 0; 54 | 55 | private Date createTime = new Date(); 56 | 57 | private Date lastUpdateTime = new Date(); 58 | 59 | private long version = 1; 60 | 61 | /** 62 | * 参与者 63 | */ 64 | private List participants = new ArrayList(); 65 | 66 | private Map attachments = new ConcurrentHashMap(); 67 | 68 | public Transaction() { 69 | 70 | } 71 | 72 | public Transaction(TransactionXid xid,TransactionStatus status) { 73 | this.xid=xid; 74 | this.status=status; 75 | } 76 | 77 | 78 | public Transaction(TccTransactionContext transactionContext) { 79 | this.xid = transactionContext.getXid(); 80 | this.status = TransactionStatus.TRY; 81 | this.transactionType = TransactionType.BRANCH; 82 | } 83 | 84 | public Transaction(TransactionType transactionType) { 85 | this.xid = new TransactionXid(); 86 | this.status = TransactionStatus.TRY; 87 | this.transactionType = transactionType; 88 | } 89 | 90 | public Transaction(Object uniqueIdentity,TransactionType transactionType) { 91 | 92 | this.xid = new TransactionXid(uniqueIdentity); 93 | this.status = TransactionStatus.TRY; 94 | this.transactionType = transactionType; 95 | } 96 | 97 | public void addingParticipants(TransactionMember participant) { 98 | participants.add(participant); 99 | } 100 | 101 | public int getModel() { 102 | return model; 103 | } 104 | 105 | 106 | public Class[] getRollbackFor() { 107 | return rollbackFor; 108 | } 109 | 110 | public void setRollbackFor(Class[] rollbackFor) { 111 | this.rollbackFor = rollbackFor; 112 | } 113 | public void setModel(int model) { 114 | this.model = model; 115 | } 116 | public TransactionXid getTransactionXid() { 117 | return xid; 118 | } 119 | public TransactionStatus getStatus() { 120 | return status; 121 | } 122 | 123 | public boolean getAsyncConfirm() { 124 | return asyncConfirm; 125 | } 126 | 127 | public void setAsyncConfirm(boolean asyncConfirm) { 128 | this.asyncConfirm = asyncConfirm; 129 | } 130 | 131 | public boolean getAsyncCancel() { 132 | return asyncCancel; 133 | } 134 | 135 | public void setAsyncCancel(boolean asyncCancel) { 136 | this.asyncCancel = asyncCancel; 137 | } 138 | 139 | public List getParticipants() { 140 | return participants; 141 | } 142 | 143 | public TransactionType getTransactionType() { 144 | return transactionType; 145 | } 146 | 147 | public void changeStatus(TransactionStatus status) { 148 | this.status = status; 149 | } 150 | 151 | /** 152 | * 提交事务 153 | */ 154 | public void commit() { 155 | for (TransactionMember participant : participants) { 156 | participant.commit(); 157 | } 158 | } 159 | 160 | /** 161 | * 回滚事务 162 | */ 163 | public void rollback() { 164 | for (TransactionMember participant : participants) { 165 | participant.rollback(); 166 | } 167 | } 168 | 169 | public int getRetriedCount() { 170 | return retriedCount; 171 | } 172 | 173 | public void addRetriedCount() { 174 | this.retriedCount++; 175 | } 176 | 177 | public void resetRetriedCount(int retriedCount) { 178 | this.retriedCount = retriedCount; 179 | } 180 | 181 | public Map getAttachments() { 182 | return attachments; 183 | } 184 | 185 | public long getVersion() { 186 | return version; 187 | } 188 | 189 | public void updateVersion() { 190 | this.version++; 191 | } 192 | 193 | public void setVersion(long version) { 194 | this.version = version; 195 | } 196 | 197 | public Date getLastUpdateTime() { 198 | return lastUpdateTime; 199 | } 200 | 201 | public void setLastUpdateTime(Date date) { 202 | this.lastUpdateTime = date; 203 | } 204 | 205 | public Date getCreateTime() { 206 | return createTime; 207 | } 208 | 209 | public void updateTime() { 210 | this.lastUpdateTime = new Date(); 211 | } 212 | 213 | 214 | } 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Distributed TCC Transaction Framework - micro-tcc (1.3.5.RELEASE) 2 | 3 | [![Maven](https://img.shields.io/badge/endpoint.svg?url=https://github.com/mytcctransaction/micro-tcc)](https://github.com/mytcctransaction/micro-tcc) 4 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/mytcctransaction/micro-tcc/master/LICENSE) 5 | 6 | # Project 7 | **micro-tcc** 项目链接 (框架核心源码) 8 | 9 | https://github.com/mytcctransaction/micro-tcc 10 | 11 | _Modules_ 12 | 13 | 1. micro-tcc-tc: *Distributed Transaction Client* 14 | 2. micro-tcc-common: *Commons* 15 | 16 | **micro-tcc-demo** 项目链接 (框架使用例子) 17 | 18 | https://github.com/mytcctransaction/micro-tcc-demo 19 | 20 | _Modules_ 21 | 22 | 1. micro-tcc-demo-common: *Distributed Transaction demo common* 23 | 2. micro-tcc-dubbo-*: *Dubbo demo* 24 | 3. micro-tcc-springcloud-*: *SpringCloud demo* 25 | 4. micro-tcc-ek:*Eureka Server* 26 | 27 | ## Summary 28 | micro-tcc 是基于Zookeeper(协调者)+Redis 分布式事务中间件,支持SpringCloud 、Dubbo、RestTemplate 29 | 30 | micro-tcc 支持事务同步和异步调用方式,发生异常的事务会定时自动恢复,如果超过最大恢复次数,建议手动恢复 31 | 32 | Zookeeper 作为分布式事务协调者,它负责协调各个子系统的事务状态和事务确认、提交、回滚 33 | 34 | redis 作为事务日志的存储方式 35 | 36 | 代码完全开源,欢迎大家start! 37 | 38 | ## Start 39 | ### 主要流程 40 | 41 | 设计流程图: 42 | 43 | ![](https://github.com/mytcctransaction/micro-tcc-demo/blob/master/micro-tcc-demo-common/src/main/resources/img/tcc-flow.jpg) 44 | 45 | 拦截器会拦截所有业务方法,把参与同一个事务的微服务加入事务组 46 | 47 | 全局事务ID 基于雪花算法生成,通过拦截器会在所有微服务中传播 48 | 49 | 事务在Try 阶段没有发生异常,Zookeeper通知事务参与者执行Confirm 方法 50 | 51 | 事务在Confirm 阶段没有异常,则这个事务结束 52 | 53 | 事务在Confirm阶段发生异常,Zookeeper 通知事务参与者执行Rollback 方法 54 | 55 | 事务Rollback 阶段发生异常,不做任何操作,防止进入死循环里,后续异常事务通过定时任务恢复 56 | 57 | ### 项目配置 58 | 59 | 1,首先在pom 文件添加以下依赖包: 60 | 61 | 62 | 63 | com.github.mytcctransaction 64 | 65 | micro-tcc-tc 66 | 67 | 1.3.5 68 | 69 | 70 | 71 | 72 | 其次在各个项目的resources目录下,配置数据库连接,本项目采用mysql数据库 73 | 74 | spring.datasource.driver-class-name=com.mysql.jdbc.Driver 75 | 76 | spring.datasource.url=jdbc:mysql://127.0.0.1:3306/micro-tcc?characterEncoding=UTF-8&serverTimezone=UTC 77 | 78 | spring.datasource.username=abc1 79 | 80 | spring.datasource.password=123456 81 | 82 | 2,数据库建表脚本 83 | 84 | 在common项目的resources目录下micro-tcc.sql文件,新建一个micro-tcc数据库,并执行它 85 | 86 | 3,配置zookeeper、redis连接 87 | 88 | micro.tcc.coordinator.ip=127.0.0.1:2181 89 | 90 | dubbo项目还需要配置 91 | 92 | dubbo.registry.address=127.0.0.1:2181 93 | 94 | dubbo.scan.basePackages=org.micro.tcc.demo.dubbo 95 | 96 | dubbo.consumer.filter=dubboConsumerContextFilter 97 | 98 | dubbo.provider.filter=dubboProviderContextFilter 99 | 100 | 配置redis连接 101 | 102 | spring.redis.host: 127.0.0.1 103 | 104 | spring.redis.port: 6379 105 | 106 | spring.redis.timeout: 10000 107 | 108 | 4,micro-tcc 事务使用配置 109 | 110 | 4.1 在SpringBoot 启动类加上@EnableMicroTccTransaction 注解,表示使用micro-tcc管理分布式事务,如下面: 111 | 112 | @EnableMicroTccTransaction 113 | 114 | public class SpringServiceAApplication { 115 | 116 | public static void main(String[] args) throws Exception { 117 | 118 | SpringApplication.run(SpringServiceAApplication.class, args); 119 | 120 | } 121 | 122 | } 123 | 124 | 4.2 在service 要进行事务管理的方法上加上 125 | 126 | @TccTransaction(confirmMethod = "confirmMethod",cancelMethod = "cancelMethod") 127 | 128 | @TccTransaction :表示开启tcc事务 129 | 130 | confirmMethod :确认方法 131 | 132 | cancelMethod :取消方法 133 | 134 | 那么在该service类里应该加上confirmMethod、cancelMethod,如下面: 135 | 136 | public void confirmMethod(args...){} 137 | 138 | ### 事务恢复配置 139 | 140 | 最大重试次数 141 | 142 | micro.tcc.transaction.recover.maxRetryCount=15 143 | 144 | 重试间隔 145 | 146 | micro.tcc.transaction.recover.recoverDuration=100 147 | 148 | 重试时间=重试间隔*重试次数 149 | 150 | job cron表达式 151 | 152 | micro.tcc.transaction.recover.cronExpression=0 */2 * * * ? 153 | 154 | ### 使用 155 | 156 | 1,分别启动dubbo或者SpringCloud a、b、c 三个项目,SpringCloud还需要首先启动micro-tcc-ek 项目 157 | 158 | 启动类类似:DubboServiceAApplication... 159 | 160 | 2,打开浏览器,输入如下地址: 161 | 162 | http://127.0.0.1:8881/micro_tcc?value=1 163 | 164 | 查看数据库,会查看到有三条记录 165 | 166 | 3,模拟异常情况,浏览器打开如下地址 167 | 168 | http://127.0.0.1:8881/micro_tcc?value=1&ex=1 169 | 170 | 查看数据库,发现没有一条记录 171 | 172 | 恭喜,测试成功!欢迎大家加群交流 173 | 174 | ## 分布式事务最终一致性机制 175 | 176 | Micro-tcc 在执行Confirm、Rollback方法前都会预先在redis记录预存事务日志,并把事务进行序列化保存 177 | 178 | 执行完 Confirm、Rollback方法成功后会执行在redis删除预存事务日志 ,不成功不会删除事务日志 179 | 180 | 如果Confirm、Rollback方法发生异常,定时器会查找没有删除的事务日志,并反序列化事务对象,执行Confirm、Rollback方法进行恢复事务 181 | 182 | 如果恢复超过一定次数(次数可配置),则建议人工手动恢复,中间件不会再执行恢复 183 | 184 | ## 性能测试 185 | 186 | 机器配置: 187 | 188 | cpu:Inter Core i5-8600 @ 3.10GHZ 189 | 190 | 内存: 16 GB 191 | 192 | 设备有限只有一台机子做测试,这台机子安装了mysql、zookeeper、redis、JMeter 193 | 194 | 测试里所有服务器也是运行在这台机子上 195 | 196 | JMeter 线程设置:并发20个线程,持续运行2分钟,观察TPS 197 | 198 | 1,完全不启用事务的情况下,关闭log日志,测试结果 199 | 200 | ![](https://github.com/mytcctransaction/micro-tcc-demo/blob/master/micro-tcc-demo-common/src/main/resources/img/ori.png) 201 | 202 | 发现事务TPS在210 左右 203 | 204 | 2,使用micro-tcc 进行分布式事务管理,关闭log日志,测试结果 205 | 206 | ![](https://github.com/mytcctransaction/micro-tcc-demo/blob/master/micro-tcc-demo-common/src/main/resources/img/new.png) 207 | 208 | 发现事务TPS在110 左右 209 | 210 | 3,结论:未启用分布式事务,也没有做任何事务补偿情况下TPS是210,如果手动做了事务补偿之类操作,这个值会更低,估计TPS值在170左右 211 | 212 | 下降率公式:下降率=(未启tcc事务TPS - 启用tcc事务TPS)/ 未启tcc事务TPS * 100% 213 | 214 | 得出总体下降率在40%左右 215 | 216 | ## 注意要点 217 | 218 | 1,每个方法在加上@TccTransaction 同时一般也要加上@Transaction 219 | 220 | 2,方法都要抛出异常,不建议捕获异常,抛出异常tcc事务才能拦截到 221 | 222 | 3,confirmMethod、cancelMethod 必须在业务上保证幂等性,框架暂不实现幂等 223 | 224 | 4,confirmMethod 是异步执行,建议前端做异步处理。如果要同步处理结果,那可以不用confirmMethod方法,即是这个方法不做任何业务处理 225 | 226 | 5,方法参数最好有一个全局唯一id,方便业务做幂等、查找数据等操作 227 | 228 | 6,zookeeper 生产环境必须做集群,而且起码3个节点以上 229 | 230 | ## The Authority 231 | Website: [https://github.com/mytcctransaction/micro-tcc](https://github.com/mytcctransaction/micro-tcc) 232 | Statistics: [Leave your company messages](https://github.com/mytcctransaction/micro-tcc) 233 | QQ 群:246539015 (Hot) 234 | 作者 QQ:306750639 -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/repository/JdbcTransactionRepository.java: -------------------------------------------------------------------------------- 1 | //package org.micro.tcc.tc.repository; 2 | // 3 | // 4 | // 5 | //import org.apache.commons.lang3.StringUtils; 6 | //import org.micro.tcc.common.constant.TransactionStatus; 7 | //import org.micro.tcc.common.core.Transaction; 8 | //import org.micro.tcc.common.core.TransactionRepository; 9 | //import org.micro.tcc.common.core.TransactionXid; 10 | //import org.micro.tcc.common.exception.TransactionIOStreamException; 11 | //import org.micro.tcc.common.serializer.KryoPoolSerializer; 12 | //import org.micro.tcc.common.serializer.ObjectSerializer; 13 | // 14 | //import javax.sql.DataSource; 15 | //import javax.transaction.xa.Xid; 16 | //import java.sql.*; 17 | //import java.util.Date; 18 | //import java.util.List; 19 | // 20 | ///** 21 | // *@author jeff.liu 22 | // * jdbc 日志保存 23 | // * date 2019/7/31 24 | // */ 25 | //public class JdbcTransactionRepository implements TransactionRepository { 26 | // 27 | // private String domain; 28 | // 29 | // private String tbSuffix; 30 | // 31 | // private DataSource dataSource; 32 | // 33 | // private ObjectSerializer serializer = new KryoPoolSerializer(); 34 | // 35 | // public String getDomain() { 36 | // return domain; 37 | // } 38 | // 39 | // public void setDomain(String domain) { 40 | // this.domain = domain; 41 | // } 42 | // 43 | // public String getTbSuffix() { 44 | // return tbSuffix; 45 | // } 46 | // 47 | // public void setTbSuffix(String tbSuffix) { 48 | // this.tbSuffix = tbSuffix; 49 | // } 50 | // 51 | // public void setSerializer(ObjectSerializer serializer) { 52 | // this.serializer = serializer; 53 | // } 54 | // 55 | // public void setDataSource(DataSource dataSource) { 56 | // this.dataSource = dataSource; 57 | // } 58 | // 59 | // public DataSource getDataSource() { 60 | // return dataSource; 61 | // } 62 | // 63 | // protected int doCreate(Transaction transaction) { 64 | // 65 | // return 1; 66 | // } 67 | // 68 | // protected int doUpdate(Transaction transaction) { 69 | // return 1; 70 | // } 71 | // 72 | // protected int doDelete(Transaction transaction) { 73 | // return 1; 74 | // } 75 | // 76 | // protected Transaction doFindOne(Xid xid) { 77 | // 78 | // 79 | // return null; 80 | // } 81 | // 82 | // 83 | // protected List doFindAllUnmodifiedSince(java.util.Date date) { 84 | // return null; 85 | // 86 | // } 87 | // 88 | // protected List doFind(List xids) { 89 | // return null; 90 | // } 91 | // 92 | // protected void constructTransactions(ResultSet resultSet, List transactions) throws SQLException { 93 | // while (resultSet.next()) { 94 | // byte[] transactionBytes = resultSet.getBytes(3); 95 | // Transaction transaction = (Transaction) serializer.deserialize(transactionBytes); 96 | // transaction.changeStatus(TransactionStatus.valueOf(resultSet.getInt(4))); 97 | // transaction.setLastUpdateTime(resultSet.getDate(7)); 98 | // transaction.setVersion(resultSet.getLong(9)); 99 | // transaction.resetRetriedCount(resultSet.getInt(8)); 100 | // transactions.add(transaction); 101 | // } 102 | // } 103 | // 104 | // 105 | // protected Connection getConnection() { 106 | // try { 107 | // return this.dataSource.getConnection(); 108 | // } catch (SQLException e) { 109 | // throw new TransactionIOStreamException(e); 110 | // } 111 | // } 112 | // 113 | // protected void releaseConnection(Connection con) { 114 | // try { 115 | // if (con != null && !con.isClosed()) { 116 | // con.close(); 117 | // } 118 | // } catch (SQLException e) { 119 | // throw new TransactionIOStreamException(e); 120 | // } 121 | // } 122 | // 123 | // private void closeStatement(Statement stmt) { 124 | // try { 125 | // if (stmt != null && !stmt.isClosed()) { 126 | // stmt.close(); 127 | // } 128 | // } catch (Exception ex) { 129 | // throw new TransactionIOStreamException(ex); 130 | // } 131 | // } 132 | // 133 | // private String getTableName() { 134 | // return StringUtils.isNotEmpty(tbSuffix) ? "TCC_TRANSACTION" + tbSuffix : "TCC_TRANSACTION"; 135 | // } 136 | // 137 | // 138 | // 139 | // @Override 140 | // public boolean createGroupMemberForClientCancel(Transaction transaction) { 141 | // return false; 142 | // } 143 | // 144 | // 145 | // 146 | // @Override 147 | // public boolean createGroupMember(Transaction transaction) { 148 | // return true; 149 | // } 150 | // 151 | // @Override 152 | // public boolean isCanceling(Transaction transaction) { 153 | // return false; 154 | // } 155 | // 156 | // @Override 157 | // public boolean update(Transaction transaction) { 158 | // return true; 159 | // } 160 | // 161 | // 162 | // 163 | // @Override 164 | // public boolean delete(Transaction transaction) { 165 | // return true; 166 | // } 167 | // 168 | // @Override 169 | // public boolean judgeIdempotent(Transaction transaction) { 170 | // return false; 171 | // } 172 | // 173 | // 174 | // @Override 175 | // public Transaction findByGroupId(TransactionXid xid) { 176 | // return null; 177 | // } 178 | // 179 | // @Override 180 | // public boolean tryGetDistributedLock(String lockKey, String requestId, long expireTime) { 181 | // return false; 182 | // } 183 | // 184 | // @Override 185 | // public boolean releaseDistributedLock(String lockKey, String requestId) { 186 | // return false; 187 | // } 188 | // 189 | // @Override 190 | // public Transaction findByGroupId(String gid) { 191 | // return null; 192 | // } 193 | // 194 | // @Override 195 | // public List findTransactionGroupAll() { 196 | // return null; 197 | // } 198 | // 199 | // @Override 200 | // public List findAll(Date date) { 201 | // return null; 202 | // } 203 | //} 204 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/interceptor/TccTransactionInterceptor.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.interceptor; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.ProceedingJoinPoint; 5 | import org.micro.tcc.common.core.TccTransactionContext; 6 | import org.micro.tcc.common.core.Transaction; 7 | import org.micro.tcc.common.constant.TransactionStatus; 8 | import org.micro.tcc.common.exception.CancelException; 9 | import org.micro.tcc.tc.component.TransactionManager; 10 | import org.springframework.stereotype.Component; 11 | 12 | /** 13 | *@author jeff.liu 14 | * tcc 事务拦截器,主要拦截器 15 | * date 2019/7/31 16 | */ 17 | @Component 18 | @Slf4j 19 | public class TccTransactionInterceptor { 20 | 21 | private TransactionManager transactionManager= TransactionManager.getInstance(); 22 | 23 | 24 | public void setTransactionManager(TransactionManager transactionManager) { 25 | this.transactionManager = transactionManager; 26 | } 27 | 28 | /** 29 | * 拦截业务方法 30 | * 判断是初始化事务还是加入事务 31 | * @param pjp 32 | * @return 33 | * @throws Throwable 34 | */ 35 | public Object interceptTransactionMethod(ProceedingJoinPoint pjp) throws Throwable { 36 | TccMethodContext tccMethodContext = new TccMethodContext(pjp); 37 | //获取feign传输过来的gid 38 | Transaction transaction= TransactionManager.getInstance().getCurrentTransaction(); 39 | boolean isTransactionActive=false; 40 | if(null!=transaction){ 41 | isTransactionActive=true; 42 | } 43 | switch (tccMethodContext.getMethodRole(isTransactionActive)) { 44 | case ROOT: 45 | return processPropagationRequired(tccMethodContext); 46 | case PROVIDER: 47 | TccTransactionContext transactionContext=new TccTransactionContext(transaction.getTransactionXid(),transaction.getStatus().value()); 48 | tccMethodContext.transactionContext=transactionContext; 49 | return processPropagationSupports(tccMethodContext); 50 | default: 51 | return pjp.proceed(); 52 | } 53 | } 54 | 55 | /** 56 | * 判断抛出异常,如果是自定义异常,则回滚 57 | * @param transaction 58 | * @param tryingException 59 | * @throws Throwable 60 | */ 61 | private void checkThrowsException(Transaction transaction,Throwable tryingException) throws Throwable{ 62 | Class[] rollbackFor=transaction.getRollbackFor(); 63 | for(Class c:rollbackFor){ 64 | //抛出的异常继承rollbackFor 65 | if(c.isAssignableFrom(tryingException.getClass())){ 66 | //主调用方失败,修改状态为cancel,次调用方需要全部回滚 67 | //创建cancel事务组 68 | transactionManager.createGroupMemberForCancel(transaction); 69 | //通知事务组成员执行取消 70 | transactionManager.createAndSendCancelOrderToMember(transaction); 71 | throw new CancelException(tryingException); 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * 处理初始化事务 78 | * @param tccMethodContext 79 | * @return 80 | * @throws Throwable 81 | */ 82 | private Object processPropagationRequired(TccMethodContext tccMethodContext) throws Throwable { 83 | Object returnValue = null; 84 | Transaction transaction = null; 85 | try { 86 | transaction = transactionManager.propagationRequiredBegin(tccMethodContext); 87 | try { 88 | returnValue = tccMethodContext.proceed(); 89 | } catch (Throwable tryingException) { 90 | if(tryingException instanceof CancelException){ 91 | throw tryingException; 92 | } 93 | checkThrowsException(transaction,tryingException); 94 | } 95 | try{ 96 | //发出提交指令,所有子系统执行提交 97 | transactionManager.sendConfirmOrderToMember(transaction); 98 | }catch (Throwable t){ 99 | checkThrowsException(transaction,t); 100 | } 101 | } finally { 102 | transactionManager.cleanAfterCompletion(transaction); 103 | } 104 | return returnValue; 105 | } 106 | 107 | /** 108 | * 处理成员事务,即是加入事务 109 | * @param tccMethodContext 110 | * @return 111 | * @throws Throwable 112 | */ 113 | private Object processPropagationSupports(TccMethodContext tccMethodContext) throws Throwable { 114 | Transaction transaction = null; 115 | Object returnObj=null; 116 | try { 117 | switch (TransactionStatus.valueOf(tccMethodContext.getTransactionContext().getStatus())) { 118 | case TRY: 119 | transaction = transactionManager.propagationSupportsBegin(tccMethodContext); 120 | try { 121 | returnObj= tccMethodContext.proceed(); 122 | }catch (Throwable t){ 123 | if(t instanceof CancelException){ 124 | throw t; 125 | } 126 | Class[] rollbackFor=transaction.getRollbackFor(); 127 | for(Class c:rollbackFor){ 128 | //抛出的异常继承rollbackFor 129 | if(c.isAssignableFrom(t.getClass())){ 130 | //创建cancel事务组 131 | transactionManager.createGroupMemberForCancel(transaction); 132 | //次调用方失败,修改日志状态为cancel 133 | //transactionManager.createCancelOrderToMember(transaction); 134 | throw new CancelException(t); 135 | } 136 | } 137 | } 138 | break; 139 | case CONFIRM: 140 | break; 141 | case CANCEL: 142 | break; 143 | default: 144 | break; 145 | } 146 | 147 | } finally { 148 | transactionManager.cleanAfterCompletion(transaction); 149 | } 150 | return returnObj; 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/interceptor/FeignHystrixConcurrencyStrategy.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.interceptor; 2 | 3 | 4 | import com.netflix.hystrix.HystrixThreadPoolKey; 5 | import com.netflix.hystrix.HystrixThreadPoolProperties; 6 | import com.netflix.hystrix.strategy.HystrixPlugins; 7 | import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; 8 | import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable; 9 | import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle; 10 | import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier; 11 | import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook; 12 | import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher; 13 | import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; 14 | import com.netflix.hystrix.strategy.properties.HystrixProperty; 15 | import lombok.extern.slf4j.Slf4j; 16 | import org.micro.tcc.common.constant.Constant; 17 | import org.micro.tcc.common.core.Transaction; 18 | import org.micro.tcc.tc.component.TransactionManager; 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 20 | import org.springframework.stereotype.Component; 21 | import org.springframework.web.context.request.RequestAttributes; 22 | import org.springframework.web.context.request.RequestContextHolder; 23 | 24 | import java.util.Map; 25 | import java.util.concurrent.BlockingQueue; 26 | import java.util.concurrent.Callable; 27 | import java.util.concurrent.ThreadPoolExecutor; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | /** 31 | *@author jeff.liu 32 | *@desc FeignHystrix 拦截 33 | *@date 2019/8/30 34 | */ 35 | @Slf4j 36 | @Component 37 | @ConditionalOnClass(HystrixConcurrencyStrategy.class) 38 | public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy { 39 | 40 | private HystrixConcurrencyStrategy delegate; 41 | 42 | public FeignHystrixConcurrencyStrategy() { 43 | try { 44 | this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy(); 45 | if (this.delegate instanceof FeignHystrixConcurrencyStrategy) { 46 | log.debug("Non another HystrixConcurrencyStrategy."); 47 | return; 48 | } 49 | HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook(); 50 | HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier(); 51 | HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher(); 52 | HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy(); 53 | this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy); 54 | log.debug("HystrixEventNotifier:{}, HystrixMetricsPublisher:{}, HystrixPropertiesStrategy:{}", eventNotifier, metricsPublisher, propertiesStrategy); 55 | HystrixPlugins.reset(); 56 | HystrixPlugins.getInstance().registerConcurrencyStrategy(this); 57 | HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook); 58 | HystrixPlugins.getInstance().registerEventNotifier(eventNotifier); 59 | HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher); 60 | HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy); 61 | } catch (Exception e) { 62 | log.error("Failed to register Tracing Hystrix Concurrency Strategy", e); 63 | } 64 | } 65 | 66 | private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier, 67 | HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) { 68 | if (log.isDebugEnabled()) { 69 | log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy [" 70 | + this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher [" 71 | + metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]"); 72 | log.debug("Registering Sleuth Hystrix Concurrency Strategy."); 73 | } 74 | } 75 | @Override 76 | public Callable wrapCallable(Callable callable) { 77 | RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); 78 | Transaction transaction = TransactionManager.getInstance().getCurrentTransaction(); 79 | return new WrappedCallable<>(callable, requestAttributes,transaction); 80 | 81 | } 82 | 83 | static class WrappedCallable implements Callable { 84 | private final Callable target; 85 | private final RequestAttributes requestAttributes; 86 | private final Transaction transaction; 87 | 88 | public WrappedCallable(Callable target, RequestAttributes requestAttributes,Transaction transactionVal) { 89 | this.target = target; 90 | this.requestAttributes = requestAttributes; 91 | this.transaction=transactionVal; 92 | } 93 | 94 | @Override 95 | public T call() throws Exception { 96 | try { 97 | RequestContextHolder.setRequestAttributes(requestAttributes); 98 | if(null!=transaction){ 99 | log.debug("TCC:Hystrix transfer transaction."); 100 | TransactionManager.getInstance().registerTransactionTrace(transaction); 101 | requestAttributes.setAttribute(Constant.GLOBAL_TCCTRANSACTION_ID, transaction.getTransactionXid().getGlobalTccTransactionId(),RequestAttributes.SCOPE_REQUEST); 102 | requestAttributes.setAttribute(Constant.TCCTRANSACTION_STATUS, String.valueOf(transaction.getStatus().value()),RequestAttributes.SCOPE_REQUEST); 103 | 104 | } 105 | 106 | RequestContextHolder.setRequestAttributes(requestAttributes); 107 | return target.call(); 108 | } finally { 109 | RequestContextHolder.resetRequestAttributes(); 110 | } 111 | } 112 | } 113 | 114 | @Override 115 | public ThreadPoolExecutor getThreadPool(final HystrixThreadPoolKey threadPoolKey, HystrixProperty corePoolSize, HystrixProperty maximumPoolSize, HystrixProperty keepAliveTime, TimeUnit unit, BlockingQueue workQueue) { 116 | return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); 117 | } 118 | 119 | @Override 120 | public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) { 121 | return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties); 122 | } 123 | 124 | @Override 125 | public BlockingQueue getBlockingQueue(int maxQueueSize) { 126 | return this.delegate.getBlockingQueue(maxQueueSize); 127 | } 128 | 129 | @Override 130 | public HystrixRequestVariable getRequestVariable(HystrixRequestVariableLifecycle rv) { 131 | return this.delegate.getRequestVariable(rv); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.sonatype.oss 8 | oss-parent 9 | 7 10 | 11 | com.github.mytcctransaction 12 | micro-tcc 13 | 1.5.1 14 | pom 15 | micro-tcc 16 | 17 | 18 | micro-tcc-common 19 | micro-tcc-tc 20 | 21 | 22 | 23 | 24 | 25 | UTF-8 26 | UTF-8 27 | 1.8 28 | 29 | 1.6.3 30 | 31 | yyyy-MM-dd HH:mm:ss 32 | 1.8 33 | 1.8 34 | 3.0.1 35 | 3.6.0 36 | 2.10.3 37 | 1.6 38 | 39 | Greenwich.RELEASE 40 | 41 | 1.18.0 42 | 43 | 44 | 45 | 46 | 47 | The Apache Software License, Version 2.0 48 | http://www.apache.org/licenses/LICENSE-2.0.txt 49 | repo 50 | 51 | 52 | 53 | 54 | 55 | scm:git:git@github.com:mytcctransaction/micro-tcc.git 56 | scm:git:https://github.com/mytcctransaction/micro-tcc.git 57 | https://github.com/mytcctransaction/micro-tcc 58 | v${project.version} 59 | 60 | 61 | 62 | 63 | 64 | liuzhijian 65 | reasonsoft@163.com 66 | https://github.com/mytcctransaction/micro-tcc 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.projectlombok 74 | lombok 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-dependencies 84 | 2.1.1.RELEASE 85 | pom 86 | import 87 | 88 | 89 | org.springframework.cloud 90 | spring-cloud-dependencies 91 | ${springcloud.version} 92 | pom 93 | import 94 | 95 | 96 | 97 | org.projectlombok 98 | lombok 99 | ${lombok.version} 100 | 101 | 102 | com.github.mytcctransaction 103 | micro-tcc-common 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-compiler-plugin 118 | 3.0 119 | 120 | 1.8 121 | 1.8 122 | UTF-8 123 | 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-source-plugin 129 | 2.2.1 130 | 131 | 132 | attach-sources 133 | package 134 | 135 | jar-no-fork 136 | 137 | 138 | 139 | 140 | 141 | org.apache.maven.plugins 142 | maven-javadoc-plugin 143 | 2.10.4 144 | 145 | ${project.build.sourceEncoding} 146 | true 147 | ${project.build.sourceEncoding} 148 | ${project.build.sourceEncoding} 149 | 150 | 151 | 152 | attach-javadocs 153 | package 154 | 155 | jar 156 | 157 | 158 | -Xdoclint:none 159 | 160 | 161 | 162 | 163 | 164 | 165 | org.apache.maven.plugins 166 | maven-release-plugin 167 | 2.5.1 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | sonatype-nexus-staging 176 | Nexus Release Repository 177 | false 178 | 179 | https://oss.sonatype.org/service/local/staging/deploy/maven2 180 | 181 | 182 | 183 | sonatype-nexus-snapshots 184 | Nexus Snapshot Repository 185 | false 186 | 187 | https://oss.sonatype.org/content/repositories/snapshots 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/component/CoordinatorWatcher.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.component; 2 | 3 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.apache.commons.lang.StringUtils; 6 | import org.apache.curator.RetryPolicy; 7 | import org.apache.curator.framework.CuratorFramework; 8 | import org.apache.curator.framework.CuratorFrameworkFactory; 9 | import org.apache.curator.framework.recipes.cache.*; 10 | import org.apache.curator.framework.state.ConnectionState; 11 | import org.apache.curator.framework.state.ConnectionStateListener; 12 | import org.apache.curator.retry.ExponentialBackoffRetry; 13 | import org.apache.curator.utils.ZKPaths; 14 | import org.apache.zookeeper.CreateMode; 15 | import org.apache.zookeeper.data.Stat; 16 | import org.micro.tcc.common.constant.Constant; 17 | import org.micro.tcc.common.constant.TransactionType; 18 | import org.micro.tcc.common.core.Transaction; 19 | import org.micro.tcc.common.constant.TransactionStatus; 20 | import org.micro.tcc.common.util.CoordinatorUtils; 21 | import org.springframework.beans.factory.annotation.Value; 22 | import org.springframework.boot.ApplicationArguments; 23 | import org.springframework.boot.ApplicationRunner; 24 | 25 | 26 | import java.io.IOException; 27 | import java.util.List; 28 | import java.util.Map; 29 | import java.util.concurrent.*; 30 | import java.util.concurrent.atomic.AtomicInteger; 31 | 32 | /** 33 | * zookeeper-分布式事务管理器 34 | * 35 | * 设计思路: 36 | * 1,当主事务发起方发出提交指令,所有子系统执行提交,成功提交会删除zk节点上指令 37 | * 2,当某子系统发生异常,发出回滚指令,所有子系统执行回滚,成功回滚会删除zk节点上的指令 38 | * 39 | * jeff.liu 40 | * 2018-04-28 13:40 41 | **/ 42 | @Slf4j 43 | public class CoordinatorWatcher implements ApplicationRunner { 44 | 45 | 46 | private static String zkIp; 47 | @Value("${micro.tcc.coordinator.ip}") 48 | public void setZkIp(String zkIp) { 49 | CoordinatorWatcher.zkIp = zkIp; 50 | } 51 | 52 | private static final String TRANSACTION_PATH = "/DistributedTransaction"; 53 | 54 | private static final String TRANSACTION_GROUP_PATH = "/DistributedTransaction/DistributedTransactionGroup"; 55 | 56 | private TransactionManager transactionManager=null; 57 | 58 | private static CuratorFramework client; 59 | 60 | @Value("${micro.tcc.coordinator.executorService.corePoolSize:0}") 61 | private int corePoolSize ; 62 | @Value("${micro.tcc.coordinator.executorService.maximumPoolSize:0}") 63 | private int maximumPoolSize ; 64 | @Value("${micro.tcc.coordinator.executorService.keepAliveTime:60}") 65 | private long keepAliveTime; 66 | 67 | /** 68 | * 线程池初始化 69 | * 70 | * corePoolSize 核心线程池大小----10 71 | * maximumPoolSize 最大线程池大小----30 72 | **/ 73 | private ExecutorService nodeEcutorService= null; 74 | 75 | PathChildrenCache childrenCache=null; 76 | 77 | private static CoordinatorWatcher coordinatorWatcher=new CoordinatorWatcher(); 78 | 79 | private AtomicInteger count = new AtomicInteger(0); 80 | public static CoordinatorWatcher getInstance(){ 81 | CoordinatorWatcher cWatcher= SpringContextAware.getBean(CoordinatorWatcher.class); 82 | if(cWatcher==null){ 83 | cWatcher= coordinatorWatcher; 84 | } 85 | return cWatcher; 86 | } 87 | 88 | private TransactionManager getTransactionManager(){ 89 | if(transactionManager==null){ 90 | return TransactionManager.getInstance(); 91 | } 92 | return transactionManager; 93 | } 94 | 95 | public void start()throws Exception { 96 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000*60, 50); 97 | 98 | client = CuratorFrameworkFactory.builder() 99 | .connectString(zkIp) 100 | .sessionTimeoutMs(1000*30) 101 | .connectionTimeoutMs(1000*6) 102 | .retryPolicy(retryPolicy) 103 | .build(); 104 | /** 105 | * 创建会话 106 | * */ 107 | client.start(); 108 | client.getConnectionStateListenable().addListener(new ConnectionStateListener() { 109 | @Override 110 | public void stateChanged(CuratorFramework client, ConnectionState state) { 111 | if (state == ConnectionState.LOST) { 112 | //连接丢失 113 | log.debug("TCC:lost session with zookeeper"); 114 | if(null!=childrenCache){ 115 | try { 116 | childrenCache.close(); 117 | } catch (IOException e) { 118 | log.error(e.getMessage(),e); 119 | } 120 | } 121 | 122 | } else if (state == ConnectionState.CONNECTED) { 123 | //连接新建 124 | log.debug("TCC:connected with zookeeper"); 125 | try { 126 | addPathChildWatch(); 127 | } catch (Exception e) { 128 | log.error(e.getMessage(),e); 129 | } 130 | } else if (state == ConnectionState.RECONNECTED) { 131 | log.debug("TCC:reconnected with zookeeper"); 132 | try { 133 | addPathChildWatch(); 134 | } catch (Exception e) { 135 | log.error(e.getMessage(),e); 136 | } 137 | } 138 | } 139 | }); 140 | 141 | } 142 | public void stop() { 143 | client.close(); 144 | } 145 | 146 | private void addPathChildWatch() throws Exception{ 147 | // PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件 148 | //childrenCache = new PathChildrenCache(client, TRANSACTION_PATH, true,false,nodeEcutorService); 149 | childrenCache = new PathChildrenCache(client, TRANSACTION_PATH, true); 150 | /** 151 | * StartMode: 初始化方式 152 | * POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件 153 | * NORMAL:异步初始化 154 | * BUILD_INITIAL_CACHE:同步初始化 155 | */ 156 | childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); 157 | // 添加事件监听器 158 | childrenCache.getListenable().addListener(new PathChildrenCacheListener() { 159 | @Override 160 | public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception { 161 | try { 162 | //先释放资源,避免cpu占用过高 163 | Thread.sleep((long) 0.3); 164 | } catch (Exception e) { 165 | 166 | } 167 | // 通过判断event type的方式来实现不同事件的触发 168 | if (event.getType().equals(PathChildrenCacheEvent.Type.INITIALIZED)) { // 子节点初始化时触发 169 | log.debug("TCC:子节点初始化成功"); 170 | } else if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)) { // 添加子节点时触发 171 | count.incrementAndGet(); 172 | log.debug(">>>>>>>>>>>>>>>count:{}",count); 173 | log.debug("TCC:node子节点:" + event.getData().getPath() + " 添加成功"); 174 | getTransactionManager().process(CoordinatorUtils.getNodeGroupId(ZKPaths.getNodeFromPath(event.getData().getPath())),new String(event.getData().getData())); 175 | } else if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) { // 删除子节点时触发 176 | log.debug("TCC:node子节点:" + event.getData().getPath() + " 删除成功"); 177 | } else if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)) { // 修改子节点数据时触发 178 | log.debug("TCC:node子节点:" + event.getData().getPath() + " 数据更新成功"); 179 | getTransactionManager().process(CoordinatorUtils.getNodeGroupId(ZKPaths.getNodeFromPath(event.getData().getPath())),new String(event.getData().getData())); 180 | } 181 | } 182 | },nodeEcutorService); 183 | 184 | } 185 | 186 | @Override 187 | public void run(ApplicationArguments args) throws Exception { 188 | start(); 189 | if(corePoolSize==0){ 190 | corePoolSize=Runtime.getRuntime().availableProcessors(); 191 | maximumPoolSize=corePoolSize*2; 192 | } 193 | ThreadFactory threadFactory= new ThreadFactoryBuilder().setNameFormat("Tcc-CD-Pool-%d").build(); 194 | nodeEcutorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue(),threadFactory); 195 | // 等待线程池任务完成 196 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 197 | nodeEcutorService.shutdown(); 198 | try { 199 | nodeEcutorService.awaitTermination(6, TimeUnit.SECONDS); 200 | } catch (InterruptedException ignored) { 201 | } 202 | })); 203 | } 204 | 205 | /** 206 | * 遍历zk 事务节点,把没有监控到watch的事务节点重新执行 207 | * 主要修复zk watch机制缺陷 208 | */ 209 | public void processTransactionStart(){ 210 | try{ 211 | 212 | log.debug("TCC:开始定时处理zookeeper事件"); 213 | String appGroupPath=TRANSACTION_GROUP_PATH; 214 | List list=client.getChildren().forPath(appGroupPath); 215 | for(String key:list){ 216 | List childList=null; 217 | try{ 218 | childList=client.getChildren().forPath(appGroupPath+"/"+key); 219 | }catch (Exception e){ 220 | 221 | } 222 | if(childList==null || childList.size()==0){ 223 | client.delete().inBackground().forPath(appGroupPath+"/"+key); 224 | client.delete().inBackground().forPath(TRANSACTION_PATH+"/"+key+Constant.DELIMIT+Constant.CONFIRM); 225 | client.delete().inBackground().forPath(TRANSACTION_PATH+"/"+key+Constant.DELIMIT+Constant.CANCEL); 226 | } 227 | 228 | } 229 | log.debug("TCC:结束定时处理zookeeper事件"); 230 | }catch (Exception e){ 231 | log.error(e.getMessage(),e); 232 | } 233 | 234 | } 235 | 236 | private void jobTransactionProcess(String k,Map childDataMap){ 237 | String groupId=CoordinatorUtils.getNodeGroupId(k); 238 | ChildData childData=childDataMap.get(k); 239 | String status=""; 240 | if(null!=childData){ 241 | if(childData.getData()!=null){ 242 | status=new String(childData.getData()); 243 | } 244 | 245 | } 246 | if(StringUtils.isEmpty(groupId)|| StringUtils.isEmpty(status)){ 247 | return; 248 | } 249 | getTransactionManager().syncProcess(groupId,status); 250 | } 251 | 252 | /** 253 | * try阶段 生成事务组,格式:key[事务组/groupId/groupId#appName1] ,value[appName] 254 | * 生成事务,格式:key[事务/appName/groupId#** ] ,value [status] 255 | * @param transaction 256 | * @return 257 | * @throws Exception 258 | */ 259 | public boolean add(Transaction transaction) throws Exception { 260 | int status=transaction.getStatus().value(); 261 | String globalTccTransactionId=transaction.getTransactionXid().getGlobalTccTransactionId(); 262 | String appPath=TRANSACTION_PATH+"/" +globalTccTransactionId+Constant.DELIMIT; 263 | String appGroupPath=TRANSACTION_GROUP_PATH+"/"+globalTccTransactionId+"/"+globalTccTransactionId+Constant.DELIMIT+Constant.getAppName(); 264 | switch (TransactionType.valueOf(transaction.getTransactionType().value())){ 265 | case ROOT: 266 | client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT). 267 | forPath(appGroupPath,(Constant.getAppName()).getBytes()); 268 | break; 269 | case BRANCH: 270 | client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT). 271 | forPath(appGroupPath,(Constant.getAppName()).getBytes()); 272 | break; 273 | default: 274 | break; 275 | } 276 | return true; 277 | } 278 | 279 | /** 280 | * 协调者发出确认和取消指令 281 | * @param transaction 282 | * @return 283 | * @throws Exception 284 | */ 285 | public boolean modify(Transaction transaction) throws Exception{ 286 | String globalTccTransactionId=transaction.getTransactionXid().getGlobalTccTransactionId(); 287 | String appPath=TRANSACTION_PATH+"/" +globalTccTransactionId+Constant.DELIMIT; 288 | int status=transaction.getStatus().value(); 289 | switch (TransactionStatus.valueOf(status)) { 290 | case TRY: 291 | break; 292 | case CONFIRM: 293 | Stat stat=client.checkExists().forPath(appPath+Constant.CONFIRM); 294 | if(null==stat) { 295 | client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT). 296 | forPath(appPath + Constant.CONFIRM, (String.valueOf(status)).getBytes()); 297 | } 298 | break; 299 | case CANCEL: 300 | Stat _stat=client.checkExists().forPath(appPath+Constant.CANCEL); 301 | if(null==_stat){ 302 | client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT). 303 | forPath(appPath+Constant.CANCEL,(String.valueOf(status)).getBytes()); 304 | } 305 | 306 | break; 307 | default: 308 | break; 309 | } 310 | return true; 311 | } 312 | 313 | /** 314 | * key[事务组/groupId/appName1] 315 | * @param transaction 316 | * @throws Exception 317 | */ 318 | public void deleteDataNodeForCancel(Transaction transaction) throws Exception{ 319 | String globalTccTransactionId=transaction.getTransactionXid().getGlobalTccTransactionId(); 320 | deleteDataNodeForConfirm(transaction); 321 | String appPath=TRANSACTION_PATH+"/" +globalTccTransactionId+Constant.DELIMIT+Constant.CANCEL; 322 | client.delete().guaranteed().deletingChildrenIfNeeded().inBackground().forPath(appPath); 323 | 324 | } 325 | 326 | /** 327 | * [事务/appName/groupId/groupId#** ] , value [status] 328 | * key[事务组/groupId/appName1] 329 | * @param transaction 330 | * @throws Exception 331 | */ 332 | public void deleteDataNodeForConfirm(Transaction transaction) throws Exception{ 333 | String globalTccTransactionId=transaction.getTransactionXid().getGlobalTccTransactionId(); 334 | String appPath=TRANSACTION_PATH+"/" +globalTccTransactionId+Constant.DELIMIT+Constant.CONFIRM; 335 | client.delete().guaranteed().deletingChildrenIfNeeded().inBackground().forPath(appPath); 336 | 337 | } 338 | public static void main(String[] args){ 339 | ThreadFactory threadFactory= new ThreadFactoryBuilder().setNameFormat("TransactionManager-ThreadPool-%d").build(); 340 | ExecutorService executorService = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(),threadFactory); 341 | System.out.println("333"); 342 | } 343 | 344 | } -------------------------------------------------------------------------------- /micro-tcc-common/micro-tcc-common.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /micro-tcc-tc/src/main/java/org/micro/tcc/tc/component/CoordinatorTreeCacheWatcher.java: -------------------------------------------------------------------------------- 1 | package org.micro.tcc.tc.component; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.apache.commons.lang.StringUtils; 6 | import org.apache.curator.RetryPolicy; 7 | import org.apache.curator.framework.CuratorFramework; 8 | import org.apache.curator.framework.CuratorFrameworkFactory; 9 | import org.apache.curator.framework.recipes.cache.*; 10 | import org.apache.curator.framework.state.ConnectionState; 11 | import org.apache.curator.framework.state.ConnectionStateListener; 12 | import org.apache.curator.retry.ExponentialBackoffRetry; 13 | import org.apache.curator.utils.ZKPaths; 14 | import org.apache.zookeeper.CreateMode; 15 | import org.micro.tcc.common.constant.Constant; 16 | import org.micro.tcc.common.constant.TransactionType; 17 | import org.micro.tcc.common.core.Transaction; 18 | import org.micro.tcc.common.constant.TransactionStatus; 19 | import org.springframework.beans.factory.annotation.Value; 20 | import org.springframework.boot.ApplicationArguments; 21 | import org.springframework.boot.ApplicationRunner; 22 | 23 | 24 | import java.util.Map; 25 | import java.util.concurrent.ExecutorService; 26 | import java.util.concurrent.Executors; 27 | import java.util.concurrent.Future; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | /** 31 | * zookeeper-分布式事务管理器 32 | * 33 | * 设计思路: 34 | * 1,当主事务发起方发出提交指令,所有子系统执行提交,成功提交会删除zk节点上指令 35 | * 2,当某子系统发生异常,发出回滚指令,所有子系统执行回滚,成功回滚会删除zk节点上的指令 36 | * 3,当主事务发起方发出的指令还存在zk上,说明事务没有正常结束,程序会在启动时候查找异常指令,恢复事务 37 | * 38 | * jeff.liu 39 | * 2018-04-28 13:40 40 | **/ 41 | @Slf4j 42 | public class CoordinatorTreeCacheWatcher implements ApplicationRunner { 43 | 44 | 45 | private static String zkIp; 46 | @Value("${micro.tcc.coordinator.ip}") 47 | public void setZkIp(String zkIp) { 48 | CoordinatorTreeCacheWatcher.zkIp = zkIp; 49 | } 50 | 51 | private static final String TRANSACTION_PATH = "/DistributedTransaction"; 52 | 53 | private static final String TRANSACTION_GROUP_PATH = "/DistributedTransaction/DistributedTransactionGroup"; 54 | 55 | ExecutorService pool = Executors.newCachedThreadPool(); 56 | private final static String DELIMIT="#"; 57 | private TransactionManager transactionManager=null; 58 | private static CuratorFramework client; 59 | 60 | private static volatile long timeMillis=System.currentTimeMillis(); 61 | TreeCache treeCache=null; 62 | private static CoordinatorWatcher coordinatorWatcher=new CoordinatorWatcher(); 63 | 64 | public static CoordinatorWatcher getInstance(){ 65 | CoordinatorWatcher cWatcher= SpringContextAware.getBean(CoordinatorWatcher.class); 66 | if(cWatcher==null){ 67 | cWatcher= coordinatorWatcher; 68 | } 69 | return cWatcher; 70 | } 71 | 72 | private TransactionManager getTransactionManager(){ 73 | if(transactionManager==null){ 74 | return TransactionManager.getInstance(); 75 | } 76 | return transactionManager; 77 | } 78 | 79 | public void start()throws Exception { 80 | RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000*60, 50); 81 | 82 | client = CuratorFrameworkFactory.builder() 83 | .connectString(zkIp) 84 | .sessionTimeoutMs(1000*30) 85 | .connectionTimeoutMs(1000*6) 86 | .retryPolicy(retryPolicy) 87 | .build(); 88 | /** 89 | * 创建会话 90 | * */ 91 | client.start(); 92 | 93 | client.getConnectionStateListenable().addListener(new ConnectionStateListener() { 94 | @Override 95 | public void stateChanged(CuratorFramework client, ConnectionState state) { 96 | if (state == ConnectionState.LOST) { 97 | //连接丢失 98 | log.debug("TCC:lost session with zookeeper"); 99 | if(null!=treeCache){ 100 | treeCache.close(); 101 | } 102 | 103 | } else if (state == ConnectionState.CONNECTED) { 104 | //连接新建 105 | log.debug("TCC:connected with zookeeper"); 106 | try { 107 | addTreeWatch(); 108 | } catch (Exception e) { 109 | log.error(e.getMessage(),e); 110 | } 111 | } else if (state == ConnectionState.RECONNECTED) { 112 | log.debug("TCC:reconnected with zookeeper"); 113 | try { 114 | addTreeWatch(); 115 | } catch (Exception e) { 116 | log.error(e.getMessage(),e); 117 | } 118 | } 119 | } 120 | }); 121 | 122 | } 123 | public void stop() { 124 | client.close(); 125 | } 126 | 127 | private String getNodeGroupId(String TRANSACTION_PATH){ 128 | String[] path=TRANSACTION_PATH.split("\\"+DELIMIT); 129 | if(path.length==2){ 130 | return path[0]; 131 | } 132 | return ""; 133 | } 134 | private void addTreeWatch() throws Exception{ 135 | //设置节点的cache 136 | treeCache =TreeCache.newBuilder(client,TRANSACTION_PATH).setCacheData(false).setExecutor(pool).build(); 137 | //设置监听器和处理过程 138 | treeCache.getListenable().addListener(new TreeCacheListener() { 139 | @Override 140 | public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { 141 | 142 | ChildData data = event.getData(); 143 | String dataVal=""; 144 | if(null!=data){ 145 | if(data.getData()!=null){ 146 | dataVal=new String(data.getData()); 147 | } 148 | 149 | } 150 | if(data !=null && !StringUtils.isEmpty(dataVal)){ 151 | String temp=event.getData().getPath(); 152 | if(temp.indexOf(Constant.getAppName())==-1){ 153 | return; 154 | } 155 | switch (event.getType()) { 156 | case NODE_ADDED: 157 | log.debug("TCC:NODE_ADDED : "+ data.getPath() +" 数据:"+ new String(data.getData())); 158 | getTransactionManager().process(getNodeGroupId(ZKPaths.getNodeFromPath(event.getData().getPath())),new String(event.getData().getData())); 159 | break; 160 | case NODE_REMOVED: 161 | log.debug("TCC:NODE_REMOVED : "+ data.getPath() +" 数据:"+ new String(data.getData())); 162 | // process(ZKPaths.getNodeFromPath(event.getData().getPath()),new String(event.getData().getData())) 163 | break; 164 | case NODE_UPDATED: 165 | log.debug("TCC:NODE_UPDATED : "+ data.getPath() +" 数据:"+ new String(data.getData())); 166 | getTransactionManager().process(getNodeGroupId(ZKPaths.getNodeFromPath(event.getData().getPath())),new String(event.getData().getData())); 167 | case INITIALIZED: 168 | log.debug("TCC:NODE_INITIALIZED : "+ data.getPath() +" 数据:"+ new String(data.getData())); 169 | 170 | break; 171 | default: 172 | break; 173 | } 174 | }else{ 175 | log.debug( "TCC: node data : "+ JSON.toJSONString(event)); 176 | } 177 | } 178 | },pool); 179 | //开始监听 180 | treeCache.start(); 181 | 182 | } 183 | 184 | @Override 185 | public void run(ApplicationArguments args) throws Exception { 186 | start(); 187 | } 188 | 189 | /** 190 | * 遍历zk 事务节点,把没有监控到watch的事务节点重新执行 191 | * 主要修复zk watch机制缺陷 192 | */ 193 | public void processTransactionStart(Future future){ 194 | try{ 195 | future.get(1000*3, TimeUnit.SECONDS); 196 | if(!future.isDone()){ 197 | return; 198 | } 199 | String appPath=TRANSACTION_PATH+"/"+ Constant.getAppName(); 200 | // appPath=TRANSACTION_PATH+"/"+ Constant.getAppName()+"/" +globalTccTransactionId+"/"+globalTccTransactionId#**; 201 | log.debug("TCC:开始定时处理zookeeper事件"); 202 | if(treeCache.getCurrentChildren(appPath)!=null){ 203 | for(String key: treeCache.getCurrentChildren(appPath).keySet()){ 204 | String childrenPath=appPath+"/"+key; 205 | Map childDataMap= treeCache.getCurrentChildren(childrenPath); 206 | if(childDataMap!=null && childDataMap.keySet()!=null){ 207 | for(String k:childDataMap.keySet()){ 208 | if(k.indexOf(DELIMIT+Constant.CONFIRM)!=-1){ 209 | jobTransactionProcess(k,childDataMap); 210 | }else if(k.indexOf(DELIMIT+Constant.CANCEL)!=-1){ 211 | jobTransactionProcess(k,childDataMap); 212 | } 213 | } 214 | } 215 | 216 | } 217 | } 218 | log.debug("TCC:结束定时处理zookeeper事件"); 219 | }catch (Exception e){ 220 | log.error(e.getMessage(),e); 221 | } 222 | 223 | } 224 | 225 | private void jobTransactionProcess(String k,Map childDataMap){ 226 | String groupId=getNodeGroupId(k); 227 | ChildData childData=childDataMap.get(k); 228 | //String status=new String(childData.getData()); 229 | String status=""; 230 | if(null!=childData){ 231 | if(childData.getData()!=null){ 232 | status=new String(childData.getData()); 233 | } 234 | 235 | } 236 | if(StringUtils.isEmpty(groupId)|| StringUtils.isEmpty(status)){ 237 | return; 238 | } 239 | getTransactionManager().syncProcess(groupId,status); 240 | } 241 | 242 | /** 243 | * try阶段 生成事务组,格式:key[事务组/groupId/appName1] ,value[appName] 244 | * 生成事务,格式:key[事务/appName/groupId/groupId#** ] ,value [status] 245 | * @param transaction 246 | * @return 247 | * @throws Exception 248 | */ 249 | public boolean add(Transaction transaction) throws Exception { 250 | int status=transaction.getStatus().value(); 251 | String globalTccTransactionId=transaction.getTransactionXid().getGlobalTccTransactionId(); 252 | String appPath=TRANSACTION_PATH+"/"+ Constant.getAppName()+"/" +globalTccTransactionId+"/"+globalTccTransactionId; 253 | switch (TransactionType.valueOf(transaction.getTransactionType().value())){ 254 | case ROOT: 255 | client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT). 256 | forPath(TRANSACTION_GROUP_PATH+"/"+globalTccTransactionId+"/"+Constant.getAppName(),(Constant.getAppName()).getBytes()); 257 | //client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(appPath+DELIMIT+Constant.TRY,(String.valueOf(status)).getBytes()); 258 | break; 259 | case BRANCH: 260 | client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT). 261 | forPath(TRANSACTION_GROUP_PATH+"/"+globalTccTransactionId+"/"+Constant.getAppName(),(Constant.getAppName()).getBytes()); 262 | //client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(appPath+DELIMIT+Constant.TRY,(String.valueOf(status)).getBytes()); 263 | break; 264 | default: 265 | break; 266 | } 267 | return true; 268 | } 269 | 270 | public boolean modify(Transaction transaction) throws Exception{ 271 | String globalTccTransactionId=transaction.getTransactionXid().getGlobalTccTransactionId(); 272 | //String appPath=TRANSACTION_PATH+"/"+ Constant.getAppName()+"/" +globalTccTransactionId+"/"+globalTccTransactionId; 273 | int status=transaction.getStatus().value(); 274 | switch (TransactionStatus.valueOf(status)) { 275 | case TRY: 276 | break; 277 | case CONFIRM: 278 | //key[事务组/groupId/appName1] 279 | for(String key: treeCache.getCurrentChildren(TRANSACTION_GROUP_PATH+"/"+globalTccTransactionId).keySet()){ 280 | String appPath=TRANSACTION_PATH+"/"+ key+"/" +globalTccTransactionId+"/"+globalTccTransactionId; 281 | client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT). 282 | forPath(appPath+DELIMIT+Constant.CONFIRM,(String.valueOf(status)).getBytes()); 283 | } 284 | break; 285 | case CANCEL: 286 | for(String key: treeCache.getCurrentChildren(TRANSACTION_GROUP_PATH+"/"+globalTccTransactionId).keySet()) { 287 | String appPath=TRANSACTION_PATH+"/"+ key+"/" +globalTccTransactionId+"/"+globalTccTransactionId; 288 | client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT). 289 | forPath(appPath+DELIMIT+Constant.CANCEL,(String.valueOf(status)).getBytes()); 290 | } 291 | 292 | break; 293 | default: 294 | break; 295 | } 296 | 297 | return true; 298 | } 299 | 300 | /** 301 | * //key[事务组/groupId/appName1] 302 | * @param transaction 303 | * @throws Exception 304 | */ 305 | public void deleteDataNode(Transaction transaction) throws Exception{ 306 | String globalTccTransactionId=transaction.getTransactionXid().getGlobalTccTransactionId(); 307 | String appPath=TRANSACTION_PATH+"/"+ Constant.getAppName()+"/" +globalTccTransactionId; 308 | /* Stat stat = client.checkExists().forPath(path); 309 | log.debug("deleteNode : "+stat);*/ 310 | client.delete().deletingChildrenIfNeeded()/*.inBackground()*/.forPath(appPath); 311 | log.debug("TCC:delete zk path:"+appPath); 312 | } 313 | 314 | /** 315 | * [事务/appName/groupId/groupId#** ] , value [status] 316 | * //key[事务组/groupId/appName1] 317 | * @param transaction 318 | * @throws Exception 319 | */ 320 | public void deleteDataNodeForConfirm(Transaction transaction) throws Exception{ 321 | String globalTccTransactionId=transaction.getTransactionXid().getGlobalTccTransactionId(); 322 | String appPath=TRANSACTION_PATH+"/"+ Constant.getAppName()+"/" +globalTccTransactionId; 323 | /* Stat stat = client.checkExists().forPath(path); 324 | log.debug("deleteNode : "+stat);*/ 325 | client.delete().deletingChildrenIfNeeded()/*.inBackground()*/.forPath(appPath); 326 | log.debug("TCC:delete zk path:"+appPath); 327 | } 328 | public static void main(String[] args){ 329 | try { 330 | long a=System.currentTimeMillis(); 331 | Thread.sleep(2000); 332 | long b=System.currentTimeMillis(); 333 | System.out.println("time:"+(b-a)); 334 | } catch (Exception e) { 335 | e.printStackTrace(); 336 | } 337 | } 338 | 339 | } -------------------------------------------------------------------------------- /micro-tcc-tc/micro-tcc-tc.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | --------------------------------------------------------------------------------