diff --git a/pigx-common/pigx-common-bom/pom.xml b/pigx-common/pigx-common-bom/pom.xml index df0880845..11eafcb80 100644 --- a/pigx-common/pigx-common-bom/pom.xml +++ b/pigx-common/pigx-common-bom/pom.xml @@ -36,6 +36,8 @@ 2.3.0 3.0.52.ALL 1.11.543 + 1.4.2 + 7.1 2.17.1 0.0.23 0.33.0 @@ -144,6 +146,17 @@ pigx-upms-api ${pigx.version} + + org.ow2.asm + asm + ${asm.version} + + + + io.seata + seata-serializer-kryo + ${seata.version} + com.baomidou diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/factory/YamlPropertySourceFactory.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/factory/YamlPropertySourceFactory.java new file mode 100644 index 000000000..22f32b9b3 --- /dev/null +++ b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/factory/YamlPropertySourceFactory.java @@ -0,0 +1,44 @@ +package com.pig4cloud.pigx.common.core.factory; + +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.support.EncodedResource; +import org.springframework.core.io.support.PropertySourceFactory; +import org.springframework.lang.Nullable; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Properties; + +/** + * @author lengleng + * @date 2022/3/29 + *

+ * 读取自定义 yaml 文件工厂类 + */ +public class YamlPropertySourceFactory implements PropertySourceFactory { + + @Override + public PropertySource createPropertySource(@Nullable String name, EncodedResource resource) throws IOException { + Properties propertiesFromYaml = loadYamlIntoProperties(resource); + String sourceName = name != null ? name : resource.getResource().getFilename(); + return new PropertiesPropertySource(sourceName, propertiesFromYaml); + } + + private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException { + try { + YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); + factory.setResources(resource.getResource()); + factory.afterPropertiesSet(); + return factory.getObject(); + } + catch (IllegalStateException e) { + Throwable cause = e.getCause(); + if (cause instanceof FileNotFoundException) + throw (FileNotFoundException) e.getCause(); + throw e; + } + } + +} diff --git a/pigx-common/pigx-common-seata/pom.xml b/pigx-common/pigx-common-seata/pom.xml new file mode 100644 index 000000000..7e31126dd --- /dev/null +++ b/pigx-common/pigx-common-seata/pom.xml @@ -0,0 +1,51 @@ + + + + + 4.0.0 + + com.pig4cloud + pigx-common + 4.3.0 + + + pigx-common-seata + jar + pigx 分布式事务处理模块 + + + + + com.pig4cloud + pigx-common-core + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-seata + + + + io.seata + seata-serializer-kryo + + + + \ No newline at end of file diff --git a/pigx-common/pigx-common-seata/src/main/java/com/pig4cloud/pigx/common/seata/config/SeataAutoConfiguration.java b/pigx-common/pigx-common-seata/src/main/java/com/pig4cloud/pigx/common/seata/config/SeataAutoConfiguration.java new file mode 100644 index 000000000..82acca7aa --- /dev/null +++ b/pigx-common/pigx-common-seata/src/main/java/com/pig4cloud/pigx/common/seata/config/SeataAutoConfiguration.java @@ -0,0 +1,19 @@ +package com.pig4cloud.pigx.common.seata.config; + +import com.pig4cloud.pigx.common.core.factory.YamlPropertySourceFactory; +import io.seata.spring.annotation.datasource.EnableAutoDataSourceProxy; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +/** + * Seata 配置类 + * + * @author lengleng + * @date 2022/3/29 + */ +@PropertySource(value = "classpath:seata-config.yml", factory = YamlPropertySourceFactory.class) +@EnableAutoDataSourceProxy +@Configuration(proxyBeanMethods = false) +public class SeataAutoConfiguration { + +} diff --git a/pigx-common/pigx-common-seata/src/main/java/io/seata/spring/util/SpringProxyUtils.java b/pigx-common/pigx-common-seata/src/main/java/io/seata/spring/util/SpringProxyUtils.java new file mode 100644 index 000000000..6b9ded757 --- /dev/null +++ b/pigx-common/pigx-common-seata/src/main/java/io/seata/spring/util/SpringProxyUtils.java @@ -0,0 +1,177 @@ +/** + * @author lengleng + * @date 2022/3/29 + */ +package io.seata.spring.util; + +import io.seata.common.util.CollectionUtils; +import io.seata.rm.tcc.remoting.parser.DubboUtil; +import org.springframework.aop.TargetSource; +import org.springframework.aop.framework.Advised; +import org.springframework.aop.framework.AdvisedSupport; +import org.springframework.aop.support.AopUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Proxy tools base on spring 主要解决 v1.4.2 兼容性问题 https://github.com/seata/seata/issues/3709 + * + * @author zhangsen + */ +public class SpringProxyUtils { + + private SpringProxyUtils() { + } + + /** + * Find target class class. + * @param proxy the proxy + * @return the class + * @throws Exception the exception + */ + public static Class findTargetClass(Object proxy) throws Exception { + if (proxy == null) { + return null; + } + if (AopUtils.isAopProxy(proxy) && proxy instanceof Advised) { + // #issue 3709 + final TargetSource targetSource = ((Advised) proxy).getTargetSource(); + if (!targetSource.isStatic()) { + return targetSource.getTargetClass(); + } + return findTargetClass(targetSource.getTarget()); + } + return proxy.getClass(); + } + + public static Class[] findInterfaces(Object proxy) throws Exception { + if (AopUtils.isJdkDynamicProxy(proxy)) { + AdvisedSupport advised = getAdvisedSupport(proxy); + return getInterfacesByAdvised(advised); + } + else { + return new Class[] {}; + } + } + + private static Class[] getInterfacesByAdvised(AdvisedSupport advised) { + Class[] interfaces = advised.getProxiedInterfaces(); + if (interfaces.length > 0) { + return interfaces; + } + else { + throw new IllegalStateException("Find the jdk dynamic proxy class that does not implement the interface"); + } + } + + /** + * Gets advised support. + * @param proxy the proxy + * @return the advised support + * @throws Exception the exception + */ + public static AdvisedSupport getAdvisedSupport(Object proxy) throws Exception { + Field h; + if (AopUtils.isJdkDynamicProxy(proxy)) { + h = proxy.getClass().getSuperclass().getDeclaredField("h"); + } + else { + h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); + } + h.setAccessible(true); + Object dynamicAdvisedInterceptor = h.get(proxy); + Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); + advised.setAccessible(true); + return (AdvisedSupport) advised.get(dynamicAdvisedInterceptor); + } + + /** + * Is proxy boolean. + * @param bean the bean + * @return the boolean + */ + public static boolean isProxy(Object bean) { + if (bean == null) { + return false; + } + // check dubbo proxy ? + return DubboUtil.isDubboProxyName(bean.getClass().getName()) + || (Proxy.class.isAssignableFrom(bean.getClass()) || AopUtils.isAopProxy(bean)); + } + + /** + * Get the target class , get the interface of its agent if it is a Proxy + * @param proxy the proxy + * @return target interface + * @throws Exception the exception + */ + public static Class getTargetInterface(Object proxy) throws Exception { + if (proxy == null) { + throw new IllegalArgumentException("proxy can not be null"); + } + + // jdk proxy + if (Proxy.class.isAssignableFrom(proxy.getClass())) { + Proxy p = (Proxy) proxy; + return p.getClass().getInterfaces()[0]; + } + + return getTargetClass(proxy); + } + + /** + * Get the class type of the proxy target object, if hadn't a target object, return + * the interface of the proxy + * @param proxy the proxy + * @return target interface + * @throws Exception the exception + */ + protected static Class getTargetClass(Object proxy) throws Exception { + if (proxy == null) { + throw new IllegalArgumentException("proxy can not be null"); + } + // not proxy + if (!AopUtils.isAopProxy(proxy)) { + return proxy.getClass(); + } + AdvisedSupport advisedSupport = getAdvisedSupport(proxy); + Object target = advisedSupport.getTargetSource().getTarget(); + /* + * the Proxy of sofa:reference has no target + */ + if (target == null) { + if (CollectionUtils.isNotEmpty(advisedSupport.getProxiedInterfaces())) { + return advisedSupport.getProxiedInterfaces()[0]; + } + else { + return proxy.getClass(); + } + } + else { + return getTargetClass(target); + } + } + + /** + * get the all interfaces of bean, if the bean is null, then return empty array + * @param bean the bean + * @return target interface + */ + public static Class[] getAllInterfaces(Object bean) { + Set> interfaces = new HashSet<>(); + if (bean != null) { + Class clazz = bean.getClass(); + while (!Object.class.getName().equalsIgnoreCase(clazz.getName())) { + Class[] clazzInterfaces = clazz.getInterfaces(); + interfaces.addAll(Arrays.asList(clazzInterfaces)); + clazz = clazz.getSuperclass(); + } + } + return interfaces.toArray(new Class[0]); + } + +} diff --git a/pigx-common/pigx-common-seata/src/main/resources/META-INF/spring.factories b/pigx-common/pigx-common-seata/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..68bbc5866 --- /dev/null +++ b/pigx-common/pigx-common-seata/src/main/resources/META-INF/spring.factories @@ -0,0 +1,5 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.pig4cloud.pigx.common.seata.config.SeataAutoConfiguration + + + diff --git a/pigx-common/pigx-common-seata/src/main/resources/seata-config.yml b/pigx-common/pigx-common-seata/src/main/resources/seata-config.yml new file mode 100644 index 000000000..183ff4911 --- /dev/null +++ b/pigx-common/pigx-common-seata/src/main/resources/seata-config.yml @@ -0,0 +1,56 @@ +seata: + enabled: true + tx-service-group: pigx_tx_group # 事务群组(可以每个应用独立取名,也可以使用相同的名字) + client: + rm-report-success-enable: true + rm-table-meta-check-enable: false # 自动刷新缓存中的表结构(默认false) + rm-report-retry-count: 5 # 一阶段结果上报TC重试次数(默认5) + rm-async-commit-buffer-limit: 10000 # 异步提交缓存队列长度(默认10000) + rm: + lock: + lock-retry-internal: 10 # 校验或占用全局锁重试间隔(默认10ms) + lock-retry-times: 30 # 校验或占用全局锁重试次数(默认30) + lock-retry-policy-branch-rollback-on-conflict: true # 分支事务与其它全局回滚事务冲突时锁策略(优先释放本地锁让回滚成功) + tm-commit-retry-count: 3 # 一阶段全局提交结果上报TC重试次数(默认1次,建议大于1) + tm-rollback-retry-count: 3 # 一阶段全局回滚结果上报TC重试次数(默认1次,建议大于1) + undo: + data-validation: true # 二阶段回滚镜像校验(默认true开启) + log-serialization: kryo # undo序列化方式(默认jackson 不支持 LocalDateTime) + log-table: undo_log # 自定义undo表名(默认undo_log) + log: + exceptionRate: 100 # 日志异常输出概率(默认100) + support: + spring: + datasource-autoproxy: true + service: + vgroup-mapping: + pigx_tx_group: default # TC 集群(必须与seata-server保持一致) + enable-degrade: false # 降级开关 + disable-global-transaction: false # 禁用全局事务(默认false) + grouplist: + default: pig-seata:8091 + transport: + shutdown: + wait: 3 + thread-factory: + boss-thread-prefix: NettyBoss + worker-thread-prefix: NettyServerNIOWorker + server-executor-thread-prefix: NettyServerBizHandler + share-boss-worker: false + client-selector-thread-prefix: NettyClientSelector + client-selector-thread-size: 1 + client-worker-thread-prefix: NettyClientWorkerThread + type: TCP + server: NIO + heartbeat: true + serialization: seata + compressor: none + enable-client-batch-send-request: true # 客户端事务消息请求是否批量合并发送(默认true) + registry: + file: + name: file.conf + type: file + config: + file: + name: file.conf + type: file diff --git a/pigx-common/pom.xml b/pigx-common/pom.xml index 988088ed2..6c376a0e2 100644 --- a/pigx-common/pom.xml +++ b/pigx-common/pom.xml @@ -45,6 +45,7 @@ pigx-common-job pigx-common-log pigx-common-oss + pigx-common-seata pigx-common-security pigx-common-sentinel pigx-common-sequence