diff --git a/README.md b/README.md index de6726962..f002052a0 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,11 @@ ## 目录说明 -transaction-dubbo LCN dubbo rpc框架扩展支持 +extensions feign扩展配置类 -transaction-springcloud LCN springcloud rpc框架扩展支持 +springcloud-lcn-demo demo案例 -transaction-motan LCN motan rpc框架扩展支持 +transaction-springcloud LCN springcloud rpc框架扩展支持 tx-client 是LCN核心tx模块端控制框架 @@ -48,7 +48,6 @@ tx-plugins-db 是LCN 对关系型数据库的插件支持 分布式事务发起方: ``` - @Override @TxTransaction(isStart=true) @Transactional @@ -61,13 +60,10 @@ tx-plugins-db 是LCN 对关系型数据库的插件支持 int v = 100/0; return true; } - - ``` 分布式事务被调用方(test2Service的业务实现类) ``` - @Override @Transactional @TxTransaction @@ -76,7 +72,6 @@ tx-plugins-db 是LCN 对关系型数据库的插件支持 testDao.save(); return true; } - ``` 如上代码执行完成以后两个模块都将回滚事务。 @@ -119,27 +114,12 @@ tx-plugins-db 是LCN 对关系型数据库的插件支持 ${lcn.last.version} - com.codingapi tx-plugins-db ${lcn.last.version} - - - com.codingapi - transaction-dubbo - ${lcn.last.version} - - - - com.codingapi - transaction-motan - ${lcn.last.version} - - - com.codingapi transaction-springcloud @@ -148,19 +128,6 @@ tx-plugins-db 是LCN 对关系型数据库的插件支持 ``` -依赖gradle等形式,见中心库 - -[http://mvnrepository.com/search?q=codingapi](http://mvnrepository.com/search?q=codingapi) - - ## demo演示教程 -每个demo下有区分为 jdbc/hibernate/mybatis不同框架的版本demo - -[springcloud版本](https://github.com/codingapi/springcloud-lcn-demo) - -[dubbo版本](https://github.com/codingapi/dubbo-lcn-demo) - -[motan版本](https://gitee.com/zfvipCase/motan-lcn-demo) - -技术交流群:554855843 +[springcloud版本](https://github.com/yizhishang/tx-lcn/tree/springcloud-2.0.4/springcloud-lcn-demo) diff --git a/pom.xml b/pom.xml index b93c6d794..900705dcc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.codingapi tx-lcn - 4.1.0 + 4.2.0 pom tx-lcn @@ -16,64 +16,34 @@ tx-client tx-manager - transaction-dubbo transaction-springcloud - transaction-motan - tx-plugins-db + + springcloud-lcn-demo yyyyMMddHHmmss UTF-8 UTF-8 - 1.7 - 1.7 - 1.7 + 1.8 + 1.8 + 1.8 3.6.0 - 4.1.0 - - - - - - - The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - lorne - 1991wangliang@gmail.com - - developer - - +8 - - - - - scm:git:https://github.com/codingapi/tx-lcn.git - scm:git:https://github.com/codingapi/tx-lcn.git - https://github.com/codingapi/tx-lcn - v${project.version} - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - Maven Central Staging Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - + 1.8.4 + 19.0 + 4.0.38 + 1.1.3 + 4.0.0 + 1.7.7 + 4.2.0 + 2.0.4.RELEASE + 5.0.8.RELEASE + 2.0.0.RELEASE + @@ -101,7 +71,6 @@ - org.sonatype.plugins nexus-staging-maven-plugin @@ -113,37 +82,6 @@ true - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.3 - - - attach-javadocs - - jar - - - - - - - - - - - - - - - - - - - - - - diff --git a/springcloud-lcn-demo/README.md b/springcloud-lcn-demo/README.md new file mode 100644 index 000000000..b592b60f0 --- /dev/null +++ b/springcloud-lcn-demo/README.md @@ -0,0 +1,41 @@ +>1.maven引入 +````xml + + com.codingapi + transaction-springcloud + ${lcn.last.version} + + + org.slf4j + * + + + + + com.codingapi + tx-plugins-db + ${lcn.last.version} + + + org.slf4j + * + + + +```` + +>2.application.properties文件配置eureka地址 +```properties +eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/ +``` + +>3.发起方和参与方的业务方法添加@Transactional注解和@TxTransaction注解 + +>4.发起方中的涉及分布式事务的方法要添加@TxTransaction(isStart = true)注解 + +>5.tx-manager启动前要配置redis + +>6.springboot启动类添加注解配置@EnableFeignClients(basePackages = {"com.codingapi.tx"}) + + +注意事项:避免事务发起方和调用方操作一张表的数据,会导致锁表 \ No newline at end of file diff --git a/springcloud-lcn-demo/pom.xml b/springcloud-lcn-demo/pom.xml new file mode 100644 index 000000000..23919c8f7 --- /dev/null +++ b/springcloud-lcn-demo/pom.xml @@ -0,0 +1,125 @@ + + + 4.0.0 + + com.codingapi + springcloud-lcn-demo + 4.2.0 + pom + + springcloud-lcn-demo + + + springcloud-mybatis-demo1 + springcloud-mybatis-demo2 + springcloud-mybatis-demo3 + + + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.8 + + 4.2.0 + Finchley.RELEASE + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + com.codingapi + transaction-springcloud + ${lcn.last.version} + + + org.slf4j + * + + + + + + com.codingapi + tx-plugins-db + ${lcn.last.version} + + + org.slf4j + * + + + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + 2.0.0.RELEASE + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-undertow + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compile.source} + ${maven.compile.target} + ${project.build.sourceEncoding} + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + \ No newline at end of file diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/pom.xml b/springcloud-lcn-demo/springcloud-mybatis-demo1/pom.xml new file mode 100644 index 000000000..db80dd3d1 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + com.example + springcloud-mybatis-demo1 + 0.0.1-SNAPSHOT + jar + + springcloud-mybatis-demo1 + Demo project for Spring Boot + + + com.codingapi + springcloud-lcn-demo + 4.2.0 + + + + UTF-8 + UTF-8 + + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.1.1 + + + + org.springframework.boot + spring-boot-starter-actuator + + + + com.alibaba + druid + 1.0.19 + + + + mysql + mysql-connector-java + 5.1.43 + + + + + diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/MybatisDemo1Application.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/MybatisDemo1Application.java new file mode 100644 index 000000000..904a1ad3d --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/MybatisDemo1Application.java @@ -0,0 +1,45 @@ +package com.example.demo; + +import com.alibaba.druid.pool.DruidDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import javax.sql.DataSource; + +@Configuration +@SpringBootApplication +@EnableEurekaClient +@EnableFeignClients(basePackages = {"com.codingapi.tx", "com.example.demo"}) +public class MybatisDemo1Application { + + public static void main(String[] args) { + SpringApplication.run(MybatisDemo1Application.class, args); + } + + @Autowired + private Environment env; + + @Bean + public DataSource dataSource() { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name")); + dataSource.setUrl(env.getProperty("spring.datasource.url")); + dataSource.setUsername(env.getProperty("spring.datasource.username")); + dataSource.setPassword(env.getProperty("spring.datasource.password")); + dataSource.setInitialSize(10); + dataSource.setMaxActive(50); + dataSource.setMinIdle(0); + dataSource.setMaxWait(60000); + dataSource.setValidationQuery("SELECT 1"); + dataSource.setTestOnBorrow(false); + dataSource.setTestWhileIdle(true); + dataSource.setPoolPreparedStatements(false); + return dataSource; + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo2Client.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo2Client.java new file mode 100644 index 000000000..dae430696 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo2Client.java @@ -0,0 +1,23 @@ +package com.example.demo.client; + +import com.example.demo.entity.Test; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * Created by lorne on 2017/6/27. + */ +@FeignClient(value = "demo2", fallback = Demo2ClientHystric.class) +public interface Demo2Client { + + @RequestMapping(value = "/demo/list", method = RequestMethod.GET) + List list(); + + + @RequestMapping(value = "/demo/save", method = RequestMethod.GET) + int save(@RequestParam(value = "id") String id, @RequestParam(value = "name") String name); +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo2ClientHystric.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo2ClientHystric.java new file mode 100644 index 000000000..8e4796335 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo2ClientHystric.java @@ -0,0 +1,24 @@ +package com.example.demo.client; + +import com.example.demo.entity.Test; +import org.springframework.stereotype.Component; + +import java.util.List; + + +@Component +public class Demo2ClientHystric implements Demo2Client { + + + @Override + public List list() { + System.out.println("进入断路器-list。。。"); + throw new RuntimeException("list 保存失败."); + } + + @Override + public int save(String id, String name) { + System.out.println("进入断路器-save。。。"); + throw new RuntimeException("save 保存失败."); + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo3Client.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo3Client.java new file mode 100644 index 000000000..45fcf1d73 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo3Client.java @@ -0,0 +1,19 @@ +package com.example.demo.client; + +import com.example.demo.entity.Test; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * Created by lorne on 2017/6/27. + */ +@FeignClient(value = "demo3", fallback = Demo3ClientHystric.class) +public interface Demo3Client { + + @RequestMapping(value = "/demo/save", method = RequestMethod.GET) + int save(); +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo3ClientHystric.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo3ClientHystric.java new file mode 100644 index 000000000..e43ce0c63 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/client/Demo3ClientHystric.java @@ -0,0 +1,17 @@ +package com.example.demo.client; + +import com.example.demo.entity.Test; +import org.springframework.stereotype.Component; + +import java.util.List; + + +@Component +public class Demo3ClientHystric implements Demo3Client { + + @Override + public int save() { + System.out.println("进入断路器-save。。。"); + throw new RuntimeException("save 保存失败."); + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/controller/DemoController.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/controller/DemoController.java new file mode 100644 index 000000000..69d80a032 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/controller/DemoController.java @@ -0,0 +1,51 @@ +package com.example.demo.controller; + + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.framework.utils.SerializerUtils; +import com.codingapi.tx.model.TransactionInvocation; +import com.example.demo.entity.Test; +import com.example.demo.service.DemoService; +import com.lorne.core.framework.utils.encode.Base64Utils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * Created by lorne on 2017/6/26. + */ +@RestController +@RequestMapping("/demo") +public class DemoController { + + @Autowired + private DemoService demoService; + + + @RequestMapping("/list") + @ResponseBody + public List list(){ + return demoService.list(); + } + + + @RequestMapping("/save") + @ResponseBody + public int save(String id, String name){ + return demoService.save(id, name); + } + + @PostMapping("notifyResult") + public String notifyResult(@RequestBody String jsonStr) + { + System.out.println("通知地址..." + jsonStr); + String data = (String) JSONObject.parseObject(jsonStr).get("json"); + data = (String) JSONObject.parseObject(data).get("data"); + byte[] serializers = Base64Utils.decode(data); + TransactionInvocation transactionInvocation = SerializerUtils.parserTransactionInvocation(serializers); + + System.out.println(transactionInvocation.getMethodStr()); + return null; + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/dao/TestMapper.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/dao/TestMapper.java new file mode 100644 index 000000000..4a0148fc7 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/dao/TestMapper.java @@ -0,0 +1,24 @@ +package com.example.demo.dao; + +import com.example.demo.entity.Test; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * Created by lorne on 2017/6/28. + */ +@Mapper +public interface TestMapper { + + + @Select("SELECT * FROM T_TEST") + List findAll(); + + @Insert("INSERT INTO T_TEST(NAME) VALUES(#{name})") + int save(@Param("name") String name); + +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/entity/Test.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/entity/Test.java new file mode 100644 index 000000000..763d8bffa --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/entity/Test.java @@ -0,0 +1,29 @@ +package com.example.demo.entity; + +/** + * Created by lorne on 2017/6/26. + */ + +public class Test { + + + private Integer id; + + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/service/DemoService.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/service/DemoService.java new file mode 100644 index 000000000..979dccf67 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/service/DemoService.java @@ -0,0 +1,16 @@ +package com.example.demo.service; + +import com.example.demo.entity.Test; + +import java.util.List; + +/** + * Created by lorne on 2017/6/26. + */ +public interface DemoService { + + List list(); + + int save(String id, String name); + +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/service/impl/DemoServiceImpl.java b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/service/impl/DemoServiceImpl.java new file mode 100644 index 000000000..f33a27d2b --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/java/com/example/demo/service/impl/DemoServiceImpl.java @@ -0,0 +1,53 @@ +package com.example.demo.service.impl; + +import com.codingapi.tx.annotation.TxTransaction; +import com.example.demo.client.Demo2Client; +import com.example.demo.client.Demo3Client; +import com.example.demo.dao.TestMapper; +import com.example.demo.entity.Test; +import com.example.demo.service.DemoService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * Created by lorne on 2017/6/26. + */ +@Service +public class DemoServiceImpl implements DemoService { + + + @Autowired + private Demo2Client demo2Client; + + @Autowired + private Demo3Client demo3Client; + + @Autowired + private TestMapper testMapper; + + private Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); + + @Override + public List list() { + return testMapper.findAll(); + } + + @Override + @TxTransaction(isStart = true) + @Transactional + public int save(String id, String name) { + + testMapper.save("mybatis1"); + + demo2Client.save(id, name); + + demo3Client.save(); + + return 2; + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/resources/application.properties b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/resources/application.properties new file mode 100644 index 000000000..a26c20ceb --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo1/src/main/resources/application.properties @@ -0,0 +1,32 @@ +#feign.hystrix.enabled=true + +spring.datasource.driver-class-name = com.mysql.jdbc.Driver +spring.datasource.url= jdbc:mysql://localhost:3306/test?useSSL=false +spring.datasource.username= root +spring.datasource.password=root +spring.datasource.initialize = true +init-db= true + + +## \u5173\u4E8Espringcloud-hystrix\u673A\u5236 +#hystrix.command.default.execution.isolation.strategy= SEMAPHORE +#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000 + + +spring.application.name = demo1 +server.port = 8081 +#${random.int[9000,9999]} +eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka/ + +#Ribbon\u7684\u8D1F\u8F7D\u5747\u8861\u7B56\u7565 +tx-manager.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule +tx-manager.ribbon.MaxAutoRetriesNextServer=0 + +ribbon.eureka.enabled=true +ribbon.readTimeout=6000 +ribbon.ConnectTimeout=6000 + +#txmanager\u5730\u5740 +tm.manager.url=http://127.0.0.1:9010/tx/manager/ + +logging.level.com.codingapi=debug \ No newline at end of file diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo2/pom.xml b/springcloud-lcn-demo/springcloud-mybatis-demo2/pom.xml new file mode 100644 index 000000000..98724199f --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo2/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + com.example + springcloud-mybatis-demo2 + 0.0.1-SNAPSHOT + jar + + springcloud-mybatis-demo2 + Demo project for Spring Boot + + + com.codingapi + springcloud-lcn-demo + 4.2.0 + + + + UTF-8 + UTF-8 + + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.1.1 + + + + org.springframework.boot + spring-boot-starter-actuator + + + + com.alibaba + druid + 1.0.19 + + + + mysql + mysql-connector-java + 5.1.43 + + + + + + + diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/MybatisDemo2Application.java b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/MybatisDemo2Application.java new file mode 100644 index 000000000..b4d943f83 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/MybatisDemo2Application.java @@ -0,0 +1,46 @@ +package com.example.demo; + +import com.alibaba.druid.pool.DruidDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import javax.sql.DataSource; + +@Configuration +@SpringBootApplication +@EnableEurekaClient +@EnableFeignClients(basePackages = {"com.codingapi.tx"}) +public class MybatisDemo2Application { + + public static void main(String[] args) { + SpringApplication.run(MybatisDemo2Application.class, args); + } + + @Autowired + private Environment env; + + @Bean + public DataSource dataSource() { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name")); + dataSource.setUrl(env.getProperty("spring.datasource.url")); + dataSource.setUsername(env.getProperty("spring.datasource.username")); + dataSource.setPassword(env.getProperty("spring.datasource.password")); + dataSource.setInitialSize(2); + dataSource.setMaxActive(20); + dataSource.setMinIdle(0); + dataSource.setMaxWait(60000); + dataSource.setValidationQuery("SELECT 1"); + dataSource.setTestOnBorrow(false); + dataSource.setTestWhileIdle(true); + dataSource.setPoolPreparedStatements(false); + return dataSource; + } + +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/controller/DemoController.java b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/controller/DemoController.java new file mode 100644 index 000000000..832188de9 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/controller/DemoController.java @@ -0,0 +1,36 @@ +package com.example.demo.controller; + + +import com.example.demo.entity.Test; +import com.example.demo.service.DemoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * Created by lorne on 2017/6/26. + */ +@RestController +@RequestMapping("/demo") +public class DemoController { + + @Autowired + private DemoService demoService; + + + @RequestMapping("/list") + @ResponseBody + public List list(){ + return demoService.list(); + } + + + @RequestMapping("/save") + @ResponseBody + public int save(String id, String name){ + return demoService.save(id, name); + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/dao/TestMapper.java b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/dao/TestMapper.java new file mode 100644 index 000000000..648d9ac2f --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/dao/TestMapper.java @@ -0,0 +1,24 @@ +package com.example.demo.dao; + +import com.example.demo.entity.Test; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * Created by lorne on 2017/6/28. + */ +@Mapper +public interface TestMapper { + + + @Select("SELECT * FROM T_TEST") + List findAll(); + + @Insert("INSERT INTO T_TEST(ID, NAME) VALUES(#{id}, #{name})") + int save(@Param("id") String id, @Param("name") String name); + +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/entity/Test.java b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/entity/Test.java new file mode 100644 index 000000000..973c06547 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/entity/Test.java @@ -0,0 +1,28 @@ +package com.example.demo.entity; + +/** + * Created by lorne on 2017/6/26. + */ + +public class Test { + + private Integer id; + + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/service/DemoService.java b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/service/DemoService.java new file mode 100644 index 000000000..979dccf67 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/service/DemoService.java @@ -0,0 +1,16 @@ +package com.example.demo.service; + +import com.example.demo.entity.Test; + +import java.util.List; + +/** + * Created by lorne on 2017/6/26. + */ +public interface DemoService { + + List list(); + + int save(String id, String name); + +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/service/impl/DemoServiceImpl.java b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/service/impl/DemoServiceImpl.java new file mode 100644 index 000000000..deb941fdb --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/java/com/example/demo/service/impl/DemoServiceImpl.java @@ -0,0 +1,34 @@ +package com.example.demo.service.impl; + +import com.codingapi.tx.annotation.ITxTransaction; +import com.codingapi.tx.annotation.TxTransaction; +import com.example.demo.dao.TestMapper; +import com.example.demo.entity.Test; +import com.example.demo.service.DemoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * Created by lorne on 2017/6/26. + */ +@Service +public class DemoServiceImpl implements DemoService, ITxTransaction { + + @Autowired + private TestMapper testMapper; + + @Override + public List list() { + return testMapper.findAll(); + } + + @Override + @TxTransaction + @Transactional + public int save(String id, String name) { + return testMapper.save(id, name); + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/resources/application.properties b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/resources/application.properties new file mode 100644 index 000000000..e12fa5eb6 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo2/src/main/resources/application.properties @@ -0,0 +1,24 @@ +spring.datasource.driver-class-name = com.mysql.jdbc.Driver +spring.datasource.url= jdbc:mysql://localhost:3306/test?useSSL=false +spring.datasource.username= root +spring.datasource.password=root +spring.datasource.initialize = true +init-db= true + +spring.application.name = demo2 +server.port = 8082 +#${random.int[9000,9999]} +eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka/ + +##Ribbon\u7684\u8D1F\u8F7D\u5747\u8861\u7B56\u7565 +tx-manager.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule +tx-manager.ribbon.MaxAutoRetriesNextServer=0 + +ribbon.eureka.enabled=true +ribbon.readTimeout=6000 +ribbon.ConnectTimeout=6000 + +#txmanager\u5730\u5740 +tm.manager.url=http://127.0.0.1:9010/tx/manager/ + +logging.level.com.codingapi=debug \ No newline at end of file diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo3/pom.xml b/springcloud-lcn-demo/springcloud-mybatis-demo3/pom.xml new file mode 100644 index 000000000..bf2594e1c --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo3/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + com.example + springcloud-mybatis-demo3 + 0.0.1-SNAPSHOT + jar + + springcloud-mybatis-demo2 + Demo project for Spring Boot + + + com.codingapi + springcloud-lcn-demo + 4.2.0 + + + + UTF-8 + UTF-8 + + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.1.1 + + + + org.springframework.boot + spring-boot-starter-actuator + + + + com.alibaba + druid + 1.0.19 + + + + mysql + mysql-connector-java + 5.1.43 + + + + + + + diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/MybatisDemo3Application.java b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/MybatisDemo3Application.java new file mode 100644 index 000000000..baeb723b7 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/MybatisDemo3Application.java @@ -0,0 +1,46 @@ +package com.example.demo; + +import com.alibaba.druid.pool.DruidDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import javax.sql.DataSource; + +@Configuration +@SpringBootApplication +@EnableEurekaClient +@EnableFeignClients(basePackages = {"com.codingapi.tx"}) +public class MybatisDemo3Application { + + public static void main(String[] args) { + SpringApplication.run(MybatisDemo3Application.class, args); + } + + @Autowired + private Environment env; + + @Bean + public DataSource dataSource() { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name")); + dataSource.setUrl(env.getProperty("spring.datasource.url")); + dataSource.setUsername(env.getProperty("spring.datasource.username")); + dataSource.setPassword(env.getProperty("spring.datasource.password")); + dataSource.setInitialSize(2); + dataSource.setMaxActive(20); + dataSource.setMinIdle(0); + dataSource.setMaxWait(60000); + dataSource.setValidationQuery("SELECT 1"); + dataSource.setTestOnBorrow(false); + dataSource.setTestWhileIdle(true); + dataSource.setPoolPreparedStatements(false); + return dataSource; + } + +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/controller/DemoController.java b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/controller/DemoController.java new file mode 100644 index 000000000..941745160 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/controller/DemoController.java @@ -0,0 +1,36 @@ +package com.example.demo.controller; + + +import com.example.demo.entity.Test; +import com.example.demo.service.DemoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * Created by lorne on 2017/6/26. + */ +@RestController +@RequestMapping("/demo") +public class DemoController { + + @Autowired + private DemoService demoService; + + + @RequestMapping("/list") + @ResponseBody + public List list(){ + return demoService.list(); + } + + + @RequestMapping("/save") + @ResponseBody + public int save(){ + return demoService.save(); + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/dao/TestMapper.java b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/dao/TestMapper.java new file mode 100644 index 000000000..648d9ac2f --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/dao/TestMapper.java @@ -0,0 +1,24 @@ +package com.example.demo.dao; + +import com.example.demo.entity.Test; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * Created by lorne on 2017/6/28. + */ +@Mapper +public interface TestMapper { + + + @Select("SELECT * FROM T_TEST") + List findAll(); + + @Insert("INSERT INTO T_TEST(ID, NAME) VALUES(#{id}, #{name})") + int save(@Param("id") String id, @Param("name") String name); + +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/entity/Test.java b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/entity/Test.java new file mode 100644 index 000000000..973c06547 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/entity/Test.java @@ -0,0 +1,28 @@ +package com.example.demo.entity; + +/** + * Created by lorne on 2017/6/26. + */ + +public class Test { + + private Integer id; + + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/service/DemoService.java b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/service/DemoService.java new file mode 100644 index 000000000..ecf199308 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/service/DemoService.java @@ -0,0 +1,16 @@ +package com.example.demo.service; + +import com.example.demo.entity.Test; + +import java.util.List; + +/** + * Created by lorne on 2017/6/26. + */ +public interface DemoService { + + List list(); + + int save(); + +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/service/impl/DemoServiceImpl.java b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/service/impl/DemoServiceImpl.java new file mode 100644 index 000000000..3cc1d8f29 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/java/com/example/demo/service/impl/DemoServiceImpl.java @@ -0,0 +1,37 @@ +package com.example.demo.service.impl; + +import com.codingapi.tx.annotation.ITxTransaction; +import com.codingapi.tx.annotation.TxTransaction; +import com.example.demo.dao.TestMapper; +import com.example.demo.entity.Test; +import com.example.demo.service.DemoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Random; + +/** + * Created by lorne on 2017/6/26. + */ +@Service +public class DemoServiceImpl implements DemoService, ITxTransaction { + + @Autowired + private TestMapper testMapper; + + @Override + public List list() { + return testMapper.findAll(); + } + + @Override + @TxTransaction + @Transactional + public int save() { + Random random = new Random(); + Integer ss = random.nextInt(1000); + return testMapper.save(ss + "", "demo3"); + } +} diff --git a/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/resources/application.properties b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/resources/application.properties new file mode 100644 index 000000000..3cc2347d7 --- /dev/null +++ b/springcloud-lcn-demo/springcloud-mybatis-demo3/src/main/resources/application.properties @@ -0,0 +1,22 @@ +spring.datasource.driver-class-name = com.mysql.jdbc.Driver +spring.datasource.url= jdbc:mysql://localhost:3306/test?useSSL=false +spring.datasource.username= root +spring.datasource.password=root + +spring.application.name = demo3 +server.port = 8083 +#${random.int[9000,9999]} +eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka/ + +##Ribbon\u7684\u8D1F\u8F7D\u5747\u8861\u7B56\u7565 +tx-manager.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule +tx-manager.ribbon.MaxAutoRetriesNextServer=0 + +ribbon.eureka.enabled=true +ribbon.readTimeout=6000 +ribbon.ConnectTimeout=6000 + +#txmanager\u5730\u5740 +tm.manager.url=http://127.0.0.1:9010/tx/manager/ + +logging.level.com.codingapi=debug \ No newline at end of file diff --git a/transaction-dubbo/.gitignore b/transaction-dubbo/.gitignore deleted file mode 100644 index e371e574f..000000000 --- a/transaction-dubbo/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -.DS_Store -node_modules/ -dist/ -npm-debug.log -test/unit/coverage -test/e2e/reports -selenium-debug.log - -.classpath -.project -.settings/ -target/ -out/ -output/ - -.idea/ -*.iml -logging.path_IS_UNDEFINED/ -nohup.out diff --git a/transaction-dubbo/README.md b/transaction-dubbo/README.md deleted file mode 100644 index 6b7eea35e..000000000 --- a/transaction-dubbo/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# transaction-dubbo - -是LCN基于dubbo的分布式事务框架 \ No newline at end of file diff --git a/transaction-dubbo/pom.xml b/transaction-dubbo/pom.xml deleted file mode 100644 index 29e9a600d..000000000 --- a/transaction-dubbo/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - 4.0.0 - - - - com.codingapi - tx-lcn - 4.1.0 - - - com.codingapi - transaction-dubbo - ${lcn.last.version} - - - transaction-dubbo - https://github.com/codingapi/tx-lcn - - transaction-dubbo project for Spring Boot - - - - 2.5.7 - - - - - - com.codingapi - tx-client - ${lcn.last.version} - - - - com.alibaba - dubbo - ${dubbo.version} - - - org.springframework - spring - - - - - - com.101tec - zkclient - 0.3 - - - - - \ No newline at end of file diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/ConsistentHashLoadBalanceProxy.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/ConsistentHashLoadBalanceProxy.java deleted file mode 100644 index 77723a737..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/ConsistentHashLoadBalanceProxy.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.codingapi.tx.dubbo.balance; - -import com.alibaba.dubbo.common.URL; -import com.alibaba.dubbo.rpc.Invocation; -import com.alibaba.dubbo.rpc.Invoker; -import com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance; - -import java.util.List; - -/** - * create by lorne on 2017/11/29 - */ -public class ConsistentHashLoadBalanceProxy extends ConsistentHashLoadBalance { - - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - public Invoker select(List> invokers, URL url, Invocation invocation) { - return lcnBalanceProxy.proxy(invokers,super.select(invokers, url, invocation)); - } -} diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/LCNBalanceProxy.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/LCNBalanceProxy.java deleted file mode 100644 index 04e2f7e6b..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/LCNBalanceProxy.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.codingapi.tx.dubbo.balance; - -import com.alibaba.dubbo.rpc.Invoker; -import com.alibaba.dubbo.rpc.RpcException; -import com.codingapi.tx.aop.bean.TxTransactionLocal; -import com.lorne.core.framework.utils.encode.MD5Util; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * create by lorne on 2017/11/29 - */ - -public class LCNBalanceProxy { - - private Logger logger = LoggerFactory.getLogger(LCNBalanceProxy.class); - - public Invoker proxy(List> invokers,Invoker invoker) throws RpcException { - TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); - if(txTransactionLocal==null){ - return invoker; - } - - try { - logger.debug("LCNBalanceProxy - > start"); - - String groupId = txTransactionLocal.getGroupId(); - - String uniqueKey = invoker.getUrl().getServiceInterface(); - - logger.debug("LCNBalanceProxy - > uniqueKey - >" + uniqueKey); - - String key = MD5Util.md5((groupId + "_" + uniqueKey).getBytes()); - - //请求tm获取模块信息 - Invoker old = getInvoker(txTransactionLocal, invokers, key); - - if (old != null) { - logger.debug("LCNBalanceProxy - > load old invoker "); - - return old; - } - putInvoker(key, txTransactionLocal, invoker); - - logger.debug("LCNBalanceProxy - > load new invoker "); - - return invoker; - }finally { - logger.debug("LCNBalanceProxy - > end"); - } - } - - - private void putInvoker(String key,TxTransactionLocal txTransactionLocal,Invoker invoker){ - String serviceName = invoker.getUrl().getServiceInterface(); - String address = invoker.getUrl().getAddress(); - - String md5 = MD5Util.md5((address+serviceName).getBytes()); - - logger.debug("putInvoker->address->"+address+",md5-->"+md5); - - txTransactionLocal.putLoadBalance(key,md5); - } - - - private Invoker getInvoker(TxTransactionLocal txTransactionLocal,List> invokers,String key){ - String val = txTransactionLocal.getLoadBalance(key); - if(StringUtils.isEmpty(val)){ - return null; - } - for(Invoker invoker:invokers){ - String serviceName = invoker.getUrl().getServiceInterface(); - String address = invoker.getUrl().getAddress(); - - String md5 = MD5Util.md5((address+serviceName).getBytes()); - - logger.debug("getInvoker->address->"+address+",md5-->"+md5); - - if(val.equals(md5)){ - return invoker; - } - } - return null; - } -} diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/LeastActiveLoadBalanceProxy.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/LeastActiveLoadBalanceProxy.java deleted file mode 100644 index 7ef9473f0..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/LeastActiveLoadBalanceProxy.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.codingapi.tx.dubbo.balance; - -import com.alibaba.dubbo.common.URL; -import com.alibaba.dubbo.rpc.Invocation; -import com.alibaba.dubbo.rpc.Invoker; -import com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance; - -import java.util.List; - -/** - * create by lorne on 2017/11/29 - */ -public class LeastActiveLoadBalanceProxy extends LeastActiveLoadBalance{ - - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - public Invoker select(List> invokers, URL url, Invocation invocation) { - return lcnBalanceProxy.proxy(invokers,super.select(invokers, url, invocation)); - } -} diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/RandomLoadBalanceProxy.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/RandomLoadBalanceProxy.java deleted file mode 100644 index 3b4329e6b..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/RandomLoadBalanceProxy.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.codingapi.tx.dubbo.balance; - -import com.alibaba.dubbo.common.URL; -import com.alibaba.dubbo.rpc.Invocation; -import com.alibaba.dubbo.rpc.Invoker; -import com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance; - -import java.util.List; - -/** - * create by lorne on 2017/11/29 - */ -public class RandomLoadBalanceProxy extends RandomLoadBalance { - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - public Invoker select(List> invokers, URL url, Invocation invocation) { - return lcnBalanceProxy.proxy(invokers,super.select(invokers, url, invocation)); - } -} diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/RoundRobinLoadBalanceProxy.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/RoundRobinLoadBalanceProxy.java deleted file mode 100644 index f90d988d5..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/balance/RoundRobinLoadBalanceProxy.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.codingapi.tx.dubbo.balance; - -import com.alibaba.dubbo.common.URL; -import com.alibaba.dubbo.rpc.Invocation; -import com.alibaba.dubbo.rpc.Invoker; -import com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance; - -import java.util.List; - -/** - * create by lorne on 2017/11/29 - */ -public class RoundRobinLoadBalanceProxy extends RoundRobinLoadBalance { - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - public Invoker select(List> invokers, URL url, Invocation invocation) { - return lcnBalanceProxy.proxy(invokers,super.select(invokers, url, invocation)); - } -} diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/filter/TransactionFilter.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/filter/TransactionFilter.java deleted file mode 100644 index 2a6ad0a26..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/filter/TransactionFilter.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.codingapi.tx.dubbo.filter; - -import com.alibaba.dubbo.rpc.*; -import com.codingapi.tx.aop.bean.TxTransactionLocal; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Created by lorne on 2017/6/30. - */ -public class TransactionFilter implements Filter { - - - private Logger logger = LoggerFactory.getLogger(TransactionFilter.class); - - @Override - public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { - - TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); - String groupId = txTransactionLocal == null ? null : txTransactionLocal.getGroupId(); - - logger.info("LCN-dubbo TxGroup info -> groupId:"+groupId); - - if(txTransactionLocal!=null){ - RpcContext.getContext().setAttachment("tx-group",groupId); - } - - return invoker.invoke(invocation); - } -} diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/interceptor/TransactionAspect.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/interceptor/TransactionAspect.java deleted file mode 100644 index da7a70141..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/interceptor/TransactionAspect.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.codingapi.tx.dubbo.interceptor; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.Ordered; -import org.springframework.stereotype.Component; - -/** - * LCN 事务拦截器 - * create by lorne on 2018/1/5 - */ - -@Aspect -@Component -public class TransactionAspect implements Ordered { - - private Logger logger = LoggerFactory.getLogger(TransactionAspect.class); - - - @Autowired - private TxManagerInterceptor txManagerInterceptor; - - - @Around("@annotation(com.codingapi.tx.annotation.TxTransaction)") - public Object transactionRunning(ProceedingJoinPoint point)throws Throwable{ - logger.debug("annotation-TransactionRunning-start---->"); - Object obj = txManagerInterceptor.around(point); - logger.debug("annotation-TransactionRunning-end---->"); - return obj; - } - - - @Around("this(com.codingapi.tx.annotation.ITxTransaction) && execution( * *(..))") - public Object around(ProceedingJoinPoint point)throws Throwable{ - logger.debug("interface-ITransactionRunning-start---->"); - Object obj = txManagerInterceptor.around(point); - logger.debug("interface-ITransactionRunning-end---->"); - return obj; - } - - - @Override - public int getOrder() { - return HIGHEST_PRECEDENCE; - } - - -} diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/interceptor/TxManagerInterceptor.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/interceptor/TxManagerInterceptor.java deleted file mode 100644 index 2e684e77b..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/interceptor/TxManagerInterceptor.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.codingapi.tx.dubbo.interceptor; - - -import com.alibaba.dubbo.rpc.RpcContext; -import com.codingapi.tx.aop.service.AspectBeforeService; -import org.aspectj.lang.ProceedingJoinPoint; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Created by lorne on 2017/6/7. - */ - -@Component -public class TxManagerInterceptor { - - - @Autowired - private AspectBeforeService aspectBeforeService; - - - public Object around(ProceedingJoinPoint point) throws Throwable { - - String groupId = null; - try { - groupId = RpcContext.getContext().getAttachment("tx-group"); - }catch (Exception e){} - return aspectBeforeService.around(groupId,point); - } - -} diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/listener/TransactionSocketListener.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/listener/TransactionSocketListener.java deleted file mode 100644 index 817edf9b0..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/listener/TransactionSocketListener.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.codingapi.tx.dubbo.listener; - -import com.codingapi.tx.listener.service.InitService; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; - -/** - * Created by lorne on 2017/7/1. - */ -@Component -public class TransactionSocketListener implements ApplicationContextAware { - - - @Autowired - private InitService initService; - - - @Override - public void setApplicationContext(ApplicationContext event) throws BeansException { - initService.start(); - } - -} diff --git a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/service/impl/ModelNameServiceImpl.java b/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/service/impl/ModelNameServiceImpl.java deleted file mode 100644 index 1eab4bd1a..000000000 --- a/transaction-dubbo/src/main/java/com/codingapi/tx/dubbo/service/impl/ModelNameServiceImpl.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.codingapi.tx.dubbo.service.impl; - -import com.alibaba.dubbo.config.ApplicationConfig; -import com.alibaba.dubbo.config.ProviderConfig; -import com.alibaba.dubbo.config.RegistryConfig; -import com.codingapi.tx.listener.service.ModelNameService; -import com.lorne.core.framework.utils.encode.MD5Util; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Service; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Map; - -/** - * Created by lorne on 2017/7/12. - */ -@Service -public class ModelNameServiceImpl implements ModelNameService { - - - @Autowired - private ApplicationConfig applicationConfig; - - @Autowired - private ApplicationContext applicationContext; - - private ProviderConfig providerConfig(){ - Map beans = applicationContext.getBeansOfType(ProviderConfig.class); - ProviderConfig providerConfig = null; - if(beans!=null){ - String defaultKey = "default"; - for(String key:beans.keySet()){ - defaultKey = key; - } - - providerConfig = beans.get(defaultKey); - } - return providerConfig; - } - - private RegistryConfig getRegistryConfig(){ - Map beans = applicationContext.getBeansOfType(RegistryConfig.class); - RegistryConfig registryConfig = null; - if(beans!=null){ - String defaultKey = "default"; - for(String key:beans.keySet()){ - defaultKey = key; - } - - registryConfig = beans.get(defaultKey); - } - return registryConfig; - } - - private String host = null; - - @Override - public String getModelName() { - return applicationConfig.getName(); - } - - - private String getIp() { - if (host == null) { - try { - host = InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException e) { - e.printStackTrace(); - } - } - return host; - } - - @Override - public String getUniqueKey() { - String address = getIp() + getPort(); - return MD5Util.md5(address.getBytes()); - } - - - @Override - public String getIpAddress() { - return getIp() + ":" + getPort(); - } - - private int getPort(){ - if(providerConfig()!=null&&providerConfig().getPort()!=null){ - return providerConfig().getPort(); - } - - RegistryConfig registryConfig = getRegistryConfig(); - if(registryConfig!=null&®istryConfig.getPort()!=null){ - return registryConfig.getPort(); - } - return 20880; - } -} diff --git a/transaction-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/transaction-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter deleted file mode 100644 index 6b59bf1fc..000000000 --- a/transaction-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter +++ /dev/null @@ -1 +0,0 @@ -transactionFilter=com.codingapi.tx.dubbo.filter.TransactionFilter \ No newline at end of file diff --git a/transaction-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance b/transaction-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance deleted file mode 100644 index 7f8ebadc0..000000000 --- a/transaction-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance +++ /dev/null @@ -1,4 +0,0 @@ -lcn_random = com.codingapi.tx.dubbo.balance.RandomLoadBalanceProxy -lcn_roundrobin = com.codingapi.tx.dubbo.balance.RoundRobinLoadBalanceProxy -lcn_leastactive = com.codingapi.tx.dubbo.balance.LeastActiveLoadBalanceProxy -lcn_consistenthash = com.codingapi.tx.dubbo.balance.ConsistentHashLoadBalanceProxy \ No newline at end of file diff --git a/transaction-motan/pom.xml b/transaction-motan/pom.xml deleted file mode 100644 index 9bdcbfa7b..000000000 --- a/transaction-motan/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - 4.0.0 - - - - com.codingapi - tx-lcn - 4.1.0 - - - com.codingapi - transaction-motan - ${lcn.last.version} - - - transaction-motan - https://github.com/codingapi/tx-lcn - - transaction-motan project for Spring Boot - - - - - michael - zfvip_it@163.com - - developer - - +8 - - - - - 1.1.0 - - - - - com.codingapi - tx-client - ${lcn.last.version} - - - - - com.weibo - motan-core - ${motan.versoin} - - - - com.weibo - motan-springsupport - ${motan.versoin} - - - - - \ No newline at end of file diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/ActiveWeightLoadBalanceProxy.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/ActiveWeightLoadBalanceProxy.java deleted file mode 100644 index 395375f17..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/ActiveWeightLoadBalanceProxy.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.codingapi.tx.motan.balance; - -import com.weibo.api.motan.cluster.loadbalance.ActiveWeightLoadBalance; -import com.weibo.api.motan.common.MotanConstants; -import com.weibo.api.motan.core.extension.Activation; -import com.weibo.api.motan.core.extension.SpiMeta; -import com.weibo.api.motan.rpc.Referer; -import com.weibo.api.motan.rpc.Request; - -import java.util.List; - -/** - *

"低并发优化" 负载均衡代理

- * - * @author 张峰 zfvip_it@163.com - * 2017/12/1 10:42 - */ -@SpiMeta(name = "activeWeightLcn") -@Activation(key = {MotanConstants.NODE_TYPE_SERVICE, MotanConstants.NODE_TYPE_REFERER}) -public class ActiveWeightLoadBalanceProxy extends ActiveWeightLoadBalance { - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - protected Referer doSelect(Request request) { - return lcnBalanceProxy.proxy(getReferers(),super.doSelect(request)); - } - - @Override - protected void doSelectToHolder(Request request, List refersHolder) { - super.doSelectToHolder(request, refersHolder); - refersHolder.set(0, lcnBalanceProxy.proxy(getReferers(),super.doSelect(request))); - } -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/ConfigurableWeightLoadBalanceProxy.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/ConfigurableWeightLoadBalanceProxy.java deleted file mode 100644 index ca1906eab..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/ConfigurableWeightLoadBalanceProxy.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.codingapi.tx.motan.balance; - -import com.weibo.api.motan.cluster.loadbalance.ConfigurableWeightLoadBalance; -import com.weibo.api.motan.common.MotanConstants; -import com.weibo.api.motan.core.extension.Activation; -import com.weibo.api.motan.core.extension.SpiMeta; -import com.weibo.api.motan.rpc.Referer; -import com.weibo.api.motan.rpc.Request; - -import java.util.List; - -/** - *

权重可配置的负载均衡器

- * - * @author 张峰 zfvip_it@163.com - * 2017/12/1 10:44 - */ -@SpiMeta(name = "configurableWeightLcn") -@Activation(key = {MotanConstants.NODE_TYPE_SERVICE, MotanConstants.NODE_TYPE_REFERER}) -public class ConfigurableWeightLoadBalanceProxy extends ConfigurableWeightLoadBalance { - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - protected Referer doSelect(Request request) { - return lcnBalanceProxy.proxy(getReferers(),super.doSelect(request)); - } - - @Override - protected void doSelectToHolder(Request request, List refersHolder) { - super.doSelectToHolder(request, refersHolder); - refersHolder.set(0, lcnBalanceProxy.proxy(getReferers(),super.doSelect(request))); - } -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/ConsistentHashLoadBalanceProxy.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/ConsistentHashLoadBalanceProxy.java deleted file mode 100644 index 98c6b42ba..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/ConsistentHashLoadBalanceProxy.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.codingapi.tx.motan.balance; - -import com.weibo.api.motan.cluster.loadbalance.ConsistentHashLoadBalance; -import com.weibo.api.motan.common.MotanConstants; -import com.weibo.api.motan.core.extension.Activation; -import com.weibo.api.motan.core.extension.SpiMeta; -import com.weibo.api.motan.rpc.Referer; -import com.weibo.api.motan.rpc.Request; - -import java.util.List; - -/** - *

一致性 Hash,相同参数的请求总是发到同一提供者

- * - * @author 张峰 zfvip_it@163.com - * 2017/12/1 10:44 - */ -@SpiMeta(name = "consistentLcn") -@Activation(key = {MotanConstants.NODE_TYPE_SERVICE, MotanConstants.NODE_TYPE_REFERER}) -public class ConsistentHashLoadBalanceProxy extends ConsistentHashLoadBalance { - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - protected Referer doSelect(Request request) { - return lcnBalanceProxy.proxy(getReferers(),super.doSelect(request)); - } - - @Override - protected void doSelectToHolder(Request request, List refersHolder) { - super.doSelectToHolder(request, refersHolder); - refersHolder.set(0, lcnBalanceProxy.proxy(getReferers(),super.doSelect(request))); - } -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/LCNBalanceProxy.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/LCNBalanceProxy.java deleted file mode 100644 index c7e9160dc..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/LCNBalanceProxy.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.codingapi.tx.motan.balance; - -import com.codingapi.tx.aop.bean.TxTransactionLocal; -import com.lorne.core.framework.utils.encode.MD5Util; -import com.weibo.api.motan.rpc.Referer; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - *

LCN负载均衡代理

- * - * @author 张峰 zfvip_it@163.com - * 2017/12/1 10:21 - */ -public class LCNBalanceProxy { - - private Logger logger = LoggerFactory.getLogger(LCNBalanceProxy.class); - - protected Referer proxy(List referers,Referer referer) { - TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); - if (txTransactionLocal == null) { - return referer; - } - - try { - logger.debug("LCNBalanceProxy - > start"); - - String groupId = txTransactionLocal.getGroupId(); - - String uniqueKey = referer.getInterface().getName(); - - logger.debug("LCNBalanceProxy - > uniqueKey - >" + uniqueKey); - - String key = MD5Util.md5((groupId + "_" + uniqueKey).getBytes()); - - Referer old = getReferer(txTransactionLocal,referers,key); - if (old != null) { - logger.debug("LCNBalanceProxy - > load old referer "); - - return old; - } - - putReferer(key,txTransactionLocal,referer); - - logger.debug("LCNBalanceProxy - > load new referer "); - - return referer; - }finally { - logger.debug("LCNBalanceProxy - > end"); - } - } - - - private void putReferer(String key,TxTransactionLocal txTransactionLocal,Referer referer){ - String serviceName = referer.getInterface().getName(); - String address = referer.getUrl().getHost()+":"+referer.getUrl().getPort(); - - String md5 = MD5Util.md5((address+serviceName).getBytes()); - - logger.debug("putReferer->address->"+address+",md5-->"+md5); - - txTransactionLocal.putLoadBalance(key,md5); - } - - - private Referer getReferer(TxTransactionLocal txTransactionLocal,List referers,String key){ - String val = txTransactionLocal.getLoadBalance(key); - if(StringUtils.isEmpty(val)){ - return null; - } - for(Referer invoker:referers){ - String serviceName = invoker.getInterface().getName(); - String address = invoker.getUrl().getHost()+":"+invoker.getUrl().getPort(); - - String md5 = MD5Util.md5((address+serviceName).getBytes()); - - logger.debug("getReferer->address->"+address+",md5-->"+md5); - - if(val.equals(md5)){ - return invoker; - } - } - return null; - } -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/LocalFirstLoadBalanceProxy.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/LocalFirstLoadBalanceProxy.java deleted file mode 100644 index ce2eb4b4a..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/LocalFirstLoadBalanceProxy.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.codingapi.tx.motan.balance; - -import com.weibo.api.motan.cluster.loadbalance.LocalFirstLoadBalance; -import com.weibo.api.motan.common.MotanConstants; -import com.weibo.api.motan.core.extension.Activation; -import com.weibo.api.motan.core.extension.SpiMeta; -import com.weibo.api.motan.rpc.Referer; -import com.weibo.api.motan.rpc.Request; - -import java.util.List; - -/** - *

"本地服务优先" 负载均衡代理

- * - * @author 张峰 zfvip_it@163.com - * 2017/12/1 10:46 - */ -@SpiMeta(name = "localFirstLcn") -@Activation(key = {MotanConstants.NODE_TYPE_SERVICE, MotanConstants.NODE_TYPE_REFERER}) -public class LocalFirstLoadBalanceProxy extends LocalFirstLoadBalance { - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - protected Referer doSelect(Request request) { - return lcnBalanceProxy.proxy(getReferers(),super.doSelect(request)); - } - - @Override - protected void doSelectToHolder(Request request, List refersHolder) { - super.doSelectToHolder(request, refersHolder); - refersHolder.set(0, lcnBalanceProxy.proxy(getReferers(),super.doSelect(request))); - } -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/RandomLoadBalanceProxy.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/RandomLoadBalanceProxy.java deleted file mode 100644 index bb3785e68..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/RandomLoadBalanceProxy.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.codingapi.tx.motan.balance; - -import com.weibo.api.motan.cluster.loadbalance.RandomLoadBalance; -import com.weibo.api.motan.common.MotanConstants; -import com.weibo.api.motan.core.extension.Activation; -import com.weibo.api.motan.core.extension.SpiMeta; -import com.weibo.api.motan.rpc.Referer; -import com.weibo.api.motan.rpc.Request; - -import java.util.List; - -/** - *

随机,按权重设置随机概率

- * - * @author 张峰 zfvip_it@163.com - * 2017/12/1 10:46 - */ -@SpiMeta(name = "randomLcn") -@Activation(key = {MotanConstants.NODE_TYPE_SERVICE, MotanConstants.NODE_TYPE_REFERER}) -public class RandomLoadBalanceProxy extends RandomLoadBalance { - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - protected Referer doSelect(Request request) { - return lcnBalanceProxy.proxy(getReferers(),super.doSelect(request)); - } - - @Override - protected void doSelectToHolder(Request request, List refersHolder) { - super.doSelectToHolder(request, refersHolder); - refersHolder.set(0, lcnBalanceProxy.proxy(getReferers(),super.doSelect(request))); - } -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/RoundRobinLoadBalanceProxy.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/RoundRobinLoadBalanceProxy.java deleted file mode 100644 index 22edf5a5e..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/balance/RoundRobinLoadBalanceProxy.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.codingapi.tx.motan.balance; - -import com.weibo.api.motan.cluster.loadbalance.RoundRobinLoadBalance; -import com.weibo.api.motan.common.MotanConstants; -import com.weibo.api.motan.core.extension.Activation; -import com.weibo.api.motan.core.extension.SpiMeta; -import com.weibo.api.motan.rpc.Referer; -import com.weibo.api.motan.rpc.Request; - -import java.util.List; - -/** - *

轮循,按公约后的权重设置轮循比率

- * - * @author 张峰 zfvip_it@163.com - * 2017/12/1 10:47 - */ -@SpiMeta(name = "roundrobinLcn") -@Activation(key = {MotanConstants.NODE_TYPE_SERVICE, MotanConstants.NODE_TYPE_REFERER}) -public class RoundRobinLoadBalanceProxy extends RoundRobinLoadBalance { - - private LCNBalanceProxy lcnBalanceProxy = new LCNBalanceProxy(); - - @Override - protected Referer doSelect(Request request) { - return lcnBalanceProxy.proxy(getReferers(),super.doSelect(request)); - } - - @Override - protected void doSelectToHolder(Request request, List refersHolder) { - super.doSelectToHolder(request, refersHolder); - refersHolder.set(0, lcnBalanceProxy.proxy(getReferers(),super.doSelect(request))); - } -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/filter/TransactionFilter.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/filter/TransactionFilter.java deleted file mode 100644 index 56bd7d381..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/filter/TransactionFilter.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.codingapi.tx.motan.filter; - -import com.codingapi.tx.aop.bean.TxTransactionLocal; -import com.weibo.api.motan.common.MotanConstants; -import com.weibo.api.motan.core.extension.Activation; -import com.weibo.api.motan.core.extension.SpiMeta; -import com.weibo.api.motan.filter.Filter; -import com.weibo.api.motan.rpc.Caller; -import com.weibo.api.motan.rpc.Request; -import com.weibo.api.motan.rpc.Response; -import com.weibo.api.motan.rpc.RpcContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Map; - -/** - *

motan拦截器

- * - * @author 张峰 zfvip_it@163.com - * 2017/11/17 15:38 - */ -@SpiMeta(name = "transaction") -@Activation(key = {MotanConstants.NODE_TYPE_SERVICE, MotanConstants.NODE_TYPE_REFERER}) -public class TransactionFilter implements Filter { - - private Logger logger = LoggerFactory.getLogger(TransactionFilter.class); - - /** - * 实现新浪的filter接口 rpc传参数 - * @param caller caller - * @param request 请求 - * @return Response - */ - public Response filter(Caller caller, Request request) { - TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); - if (txTransactionLocal != null) { - - request.setAttachment("tx-group", txTransactionLocal.getGroupId()); - - logger.info("LCN-dubbo TxGroup info -> groupId:"+ txTransactionLocal.getGroupId()+",maxTimeOut:"+txTransactionLocal.getMaxTimeOut()); - - } else { - Map map = request.getAttachments(); - if (map != null && !map.isEmpty()) { - if (map.containsKey("tx-group")) { - RpcContext.getContext().putAttribute("tx-group", request.getAttachments().get("tx-group")); - } - } - } - - - return caller.call(request); - } -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/interceptor/TransactionAspect.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/interceptor/TransactionAspect.java deleted file mode 100644 index 74574604d..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/interceptor/TransactionAspect.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.codingapi.tx.motan.interceptor; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.Ordered; -import org.springframework.stereotype.Component; - -/** - * LCN 事务拦截器 - * create by lorne on 2018/1/5 - */ - -@Aspect -@Component -public class TransactionAspect implements Ordered { - - private Logger logger = LoggerFactory.getLogger(TransactionAspect.class); - - @Autowired - private TxManagerInterceptor txManagerInterceptor; - - - @Around("@annotation(com.codingapi.tx.annotation.TxTransaction)") - public Object transactionRunning(ProceedingJoinPoint point)throws Throwable{ - logger.debug("annotation-TransactionRunning-start---->"); - Object obj = txManagerInterceptor.around(point); - logger.debug("annotation-TransactionRunning-end---->"); - return obj; - } - - @Around("this(com.codingapi.tx.annotation.ITxTransaction) && execution( * *(..))") - public Object around(ProceedingJoinPoint point)throws Throwable{ - logger.debug("interface-ITransactionRunning-start---->"); - Object obj = txManagerInterceptor.around(point); - logger.debug("interface-ITransactionRunning-end---->"); - return obj; - } - - - @Override - public int getOrder() { - return HIGHEST_PRECEDENCE; - } - - -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/interceptor/TxManagerInterceptor.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/interceptor/TxManagerInterceptor.java deleted file mode 100644 index 3e4b1cbd2..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/interceptor/TxManagerInterceptor.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.codingapi.tx.motan.interceptor; - -import com.codingapi.tx.aop.service.AspectBeforeService; -import com.weibo.api.motan.rpc.RpcContext; -import org.aspectj.lang.ProceedingJoinPoint; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - *

类说明

- * - * @author 张峰 zfvip_it@163.com - * 2017/11/17 15:42 - */ -@Component -public class TxManagerInterceptor { - - @Resource - private AspectBeforeService aspectBeforeService; - - - public Object around(ProceedingJoinPoint point) throws Throwable { - - String groupId = null; - try { - groupId = (String) RpcContext.getContext().getAttribute("tx-group"); - } catch (Exception e) { - } - return aspectBeforeService.around(groupId, point); - } -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/listener/TransactionSocketListener.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/listener/TransactionSocketListener.java deleted file mode 100644 index b0ae2bff1..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/listener/TransactionSocketListener.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.codingapi.tx.motan.listener; - -import com.codingapi.tx.listener.service.InitService; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; - -/** - * Created by lorne on 2017/7/1. - */ -@Component -public class TransactionSocketListener implements ApplicationContextAware { - - - @Autowired - private InitService initService; - - - @Override - public void setApplicationContext(ApplicationContext event) throws BeansException { - initService.start(); - } - -} diff --git a/transaction-motan/src/main/java/com/codingapi/tx/motan/service/impl/ModelNameServiceImpl.java b/transaction-motan/src/main/java/com/codingapi/tx/motan/service/impl/ModelNameServiceImpl.java deleted file mode 100644 index 84d0c186d..000000000 --- a/transaction-motan/src/main/java/com/codingapi/tx/motan/service/impl/ModelNameServiceImpl.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.codingapi.tx.motan.service.impl; - -import com.codingapi.tx.listener.service.ModelNameService; -import com.lorne.core.framework.utils.encode.MD5Util; -import com.weibo.api.motan.config.springsupport.BasicServiceConfigBean; -import org.apache.commons.lang.StringUtils; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - *

类说明

- * - * @author 张峰 zfvip_it@163.com - * 2017/11/17 13:30 - */ -@Service -public class ModelNameServiceImpl implements ModelNameService { - - @Resource - private BasicServiceConfigBean basicServiceConfigBean; - - @Resource - private Environment environment; - - private String host = null; - - public String getModelName() { - return environment.getProperty("tx.application"); - } - - public String getUniqueKey() { - String address = getIp() + getPort(); - return MD5Util.md5(address.getBytes()); - } - - public String getIpAddress() { - String address = getIp() + ":" + getPort(); - return address; - } - - private String getIp() { - if (host == null) { - try { - host = InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException e) { - e.printStackTrace(); - } - } - return host; - } - - private String getPort() { - String export = basicServiceConfigBean.getExport(); - if (StringUtils.isNotBlank(export)) { - return export.split(":")[1]; - } - return null; - } - - /** - * 超时时间 - * - * @return 超时时间 - */ - public String getTimeOut() { - return basicServiceConfigBean.getRequestTimeout().toString(); - } -} diff --git a/transaction-motan/src/main/resources/META-INF/services/com.weibo.api.motan.cluster.LoadBalance b/transaction-motan/src/main/resources/META-INF/services/com.weibo.api.motan.cluster.LoadBalance deleted file mode 100644 index f43532524..000000000 --- a/transaction-motan/src/main/resources/META-INF/services/com.weibo.api.motan.cluster.LoadBalance +++ /dev/null @@ -1,6 +0,0 @@ -com.codingapi.tx.motan.balance.ActiveWeightLoadBalanceProxy -com.codingapi.tx.motan.balance.LocalFirstLoadBalanceProxy -com.codingapi.tx.motan.balance.RandomLoadBalanceProxy -com.codingapi.tx.motan.balance.RoundRobinLoadBalanceProxy -com.codingapi.tx.motan.balance.ConfigurableWeightLoadBalanceProxy -com.codingapi.tx.motan.balance.ConsistentHashLoadBalanceProxy \ No newline at end of file diff --git a/transaction-motan/src/main/resources/META-INF/services/com.weibo.api.motan.filter.Filter b/transaction-motan/src/main/resources/META-INF/services/com.weibo.api.motan.filter.Filter deleted file mode 100644 index 42974e950..000000000 --- a/transaction-motan/src/main/resources/META-INF/services/com.weibo.api.motan.filter.Filter +++ /dev/null @@ -1 +0,0 @@ -com.codingapi.tx.motan.filter.TransactionFilter \ No newline at end of file diff --git a/transaction-springcloud/.gitignore b/transaction-springcloud/.gitignore deleted file mode 100644 index e371e574f..000000000 --- a/transaction-springcloud/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -.DS_Store -node_modules/ -dist/ -npm-debug.log -test/unit/coverage -test/e2e/reports -selenium-debug.log - -.classpath -.project -.settings/ -target/ -out/ -output/ - -.idea/ -*.iml -logging.path_IS_UNDEFINED/ -nohup.out diff --git a/transaction-springcloud/pom.xml b/transaction-springcloud/pom.xml index 5342e94ae..4c890ce05 100644 --- a/transaction-springcloud/pom.xml +++ b/transaction-springcloud/pom.xml @@ -1,30 +1,22 @@ - 4.0.0 com.codingapi tx-lcn - 4.1.0 + 4.2.0 - com.codingapi transaction-springcloud - ${lcn.last.version} - transaction-springcloud https://github.com/codingapi/tx-lcn transaction-springcloud project for Spring Boot - - 1.3.2.RELEASE - 4.3.7.RELEASE - - - @@ -34,23 +26,37 @@ - org.springframework.cloud - spring-cloud-starter-feign - ${spring-cloud.version} - - - com.google.guava - guava - - + org.springframework + spring-context + ${org.springframework-version} + + com.netflix.ribbon + ribbon-core + 2.2.5 + org.springframework - spring-context + spring-web ${org.springframework-version} - + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + 2.0.1.RELEASE + + + com.netflix.feign + feign-core + 8.18.0 + runtime + + + org.springframework.boot + spring-boot + ${spring-cloud.version} + diff --git a/transaction-springcloud/src/main/java/com/codingapi/tx/RequestInterceptorConfiguration.java b/transaction-springcloud/src/main/java/com/codingapi/tx/RequestInterceptorConfiguration.java index 1b7c36f17..7aea96456 100644 --- a/transaction-springcloud/src/main/java/com/codingapi/tx/RequestInterceptorConfiguration.java +++ b/transaction-springcloud/src/main/java/com/codingapi/tx/RequestInterceptorConfiguration.java @@ -1,9 +1,12 @@ package com.codingapi.tx; import com.codingapi.tx.springcloud.feign.TransactionRestTemplateInterceptor; +import com.codingapi.tx.springcloud.http.TransactionHttpRequestInterceptor; import feign.RequestInterceptor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; /** * create by lorne on 2018/1/18 @@ -15,4 +18,13 @@ public class RequestInterceptorConfiguration { public RequestInterceptor requestInterceptor(){ return new TransactionRestTemplateInterceptor(); } + + @Bean + public TransactionHttpRequestInterceptor transactionHttpRequestInterceptor(@Autowired(required = false) RestTemplate restTemplate){ + TransactionHttpRequestInterceptor transactionHttpRequestInterceptor = new TransactionHttpRequestInterceptor(); + if(restTemplate != null){ + restTemplate.getInterceptors().add(transactionHttpRequestInterceptor); + } + return transactionHttpRequestInterceptor; + } } diff --git a/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TransactionAspect.java b/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TransactionAspect.java index db649e382..aec99dd02 100644 --- a/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TransactionAspect.java +++ b/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TransactionAspect.java @@ -1,5 +1,6 @@ package com.codingapi.tx.springcloud.interceptor; +import com.codingapi.tx.Constants; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -12,38 +13,41 @@ /** * LCN 事务拦截器 * create by lorne on 2018/1/5 + * + * @author Administrator */ - @Aspect @Component -public class TransactionAspect implements Ordered { +public class TransactionAspect implements Ordered +{ private Logger logger = LoggerFactory.getLogger(TransactionAspect.class); @Autowired private TxManagerInterceptor txManagerInterceptor; - @Around("@annotation(com.codingapi.tx.annotation.TxTransaction)") - public Object transactionRunning(ProceedingJoinPoint point)throws Throwable{ + public Object transactionRunning(ProceedingJoinPoint point) throws Throwable + { logger.debug("annotation-TransactionRunning-start---->"); - Object obj = txManagerInterceptor.around(point); + Object obj = txManagerInterceptor.around(point); logger.debug("annotation-TransactionRunning-end---->"); return obj; } @Around("this(com.codingapi.tx.annotation.ITxTransaction) && execution( * *(..))") - public Object around(ProceedingJoinPoint point)throws Throwable{ + public Object around(ProceedingJoinPoint point) throws Throwable + { logger.debug("interface-ITransactionRunning-start---->"); - Object obj = txManagerInterceptor.around(point); + Object obj = txManagerInterceptor.around(point); logger.debug("interface-ITransactionRunning-end---->"); return obj; } - @Override - public int getOrder() { - return HIGHEST_PRECEDENCE; + public int getOrder() + { + return Constants.ASPECT_ORDER; } diff --git a/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TxManagerInterceptor.java b/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TxManagerInterceptor.java index 497175330..1898d7498 100644 --- a/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TxManagerInterceptor.java +++ b/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TxManagerInterceptor.java @@ -22,11 +22,13 @@ public class TxManagerInterceptor { public Object around(ProceedingJoinPoint point) throws Throwable { String groupId = null; + String mode = null; try { RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = requestAttributes == null ? null : ((ServletRequestAttributes) requestAttributes).getRequest(); groupId = request == null ? null : request.getHeader("tx-group"); + mode = request == null ? null : request.getHeader("tx-mode"); }catch (Exception e){} - return aspectBeforeService.around(groupId, point); + return aspectBeforeService.around(groupId, point, mode); } } diff --git a/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/listener/ServerListener.java b/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/listener/ServerListener.java index b936ec38e..69c47aed3 100644 --- a/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/listener/ServerListener.java +++ b/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/listener/ServerListener.java @@ -4,30 +4,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @Component -public class ServerListener implements ApplicationListener { +public class ServerListener implements ApplicationListener { private Logger logger = LoggerFactory.getLogger(ServerListener.class); + @Value("${server.port}") private int serverPort; @Autowired private InitService initService; @Override - public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) { - logger.info("onApplicationEvent -> onApplicationEvent. "+event.getEmbeddedServletContainer()); - this.serverPort = event.getEmbeddedServletContainer().getPort(); - - initService.start(); + public void onApplicationEvent(WebServerInitializedEvent event) { + logger.info("onApplicationEvent -> onApplicationEvent. "+event.getWebServer()); + this.serverPort = event.getWebServer().getPort(); + + Thread thread = new Thread(() -> { + // 若连接不上txmanager start()方法将阻塞 + initService.start(); + }); + thread.setName("TxInit-thread"); + thread.start(); } - public int getPort() { - return this.serverPort; + public int getServerPort() { + return serverPort; } } diff --git a/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/service/impl/ModelNameServiceImpl.java b/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/service/impl/ModelNameServiceImpl.java index a9b6f9de4..5fff0e50a 100644 --- a/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/service/impl/ModelNameServiceImpl.java +++ b/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/service/impl/ModelNameServiceImpl.java @@ -44,7 +44,7 @@ private String getIp() { } private int getPort() { - int port = serverListener.getPort(); + int port = serverListener.getServerPort(); int count = 0; while (port == 0) { try { @@ -52,7 +52,7 @@ private int getPort() { } catch (InterruptedException e) { e.printStackTrace(); } - port = serverListener.getPort(); + port = serverListener.getServerPort(); count++; if(count==2000){ diff --git a/transaction-springcloud/src/main/resources/banner.txt b/transaction-springcloud/src/main/resources/banner.txt new file mode 100644 index 000000000..9161fc577 --- /dev/null +++ b/transaction-springcloud/src/main/resources/banner.txt @@ -0,0 +1,13 @@ + + >=> >=> >==> >=> + >=> >=> >=> >> >=> >=> + >=> >=> >=> >=> >=> + >=> >=> >=> >=>>=> + >=> >=> >=> > >=> + >=> >=> >=> >=> >>=> + >=======> >===> >=> >=> + + LCN-Client version:4.2.0 + + + diff --git a/tx-client/.editorconfig b/tx-client/.editorconfig deleted file mode 100644 index e291365a9..000000000 --- a/tx-client/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 4 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true diff --git a/tx-client/.gitignore b/tx-client/.gitignore deleted file mode 100644 index e371e574f..000000000 --- a/tx-client/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -.DS_Store -node_modules/ -dist/ -npm-debug.log -test/unit/coverage -test/e2e/reports -selenium-debug.log - -.classpath -.project -.settings/ -target/ -out/ -output/ - -.idea/ -*.iml -logging.path_IS_UNDEFINED/ -nohup.out diff --git a/tx-client/pom.xml b/tx-client/pom.xml index 08574c890..f185e56e5 100644 --- a/tx-client/pom.xml +++ b/tx-client/pom.xml @@ -6,31 +6,30 @@ com.codingapi tx-lcn - 4.1.0 + 4.2.0 com.codingapi tx-client - ${lcn.last.version} tx-client https://github.com/codingapi/tx-lcn tx-client project for Spring Boot - - - 4.3.7.RELEASE - 19.0 - 4.0.38 - 1.1.3 - 4.0.0 - 1.7.7 - - - + + org.springframework.cloud + spring-cloud-starter-openfeign + ${spring-cloud-starter-openfeign.version} + + + org.springframework.cloud + spring-cloud-starter-ribbon + 1.4.4.RELEASE + + com.github.1991wangliang lorne_core @@ -43,19 +42,17 @@ 4.1.12.Final - org.aspectj aspectjweaver - 1.8.4 + ${org.aspectj.version} org.aspectj aspectjrt - 1.8.4 + ${org.aspectj.version} - org.slf4j slf4j-api @@ -77,22 +74,18 @@ ${org.slf4j-version} - - javax.servlet javax.servlet-api 3.1.0 - org.springframework spring-context ${org.springframework-version} - com.caucho hessian diff --git a/tx-client/src/main/java/com/codingapi/tx/Constants.java b/tx-client/src/main/java/com/codingapi/tx/Constants.java index c7c8fb771..7dded7743 100644 --- a/tx-client/src/main/java/com/codingapi/tx/Constants.java +++ b/tx-client/src/main/java/com/codingapi/tx/Constants.java @@ -15,4 +15,9 @@ public class Constants { */ public static TxServer txServer; + /** + * 主切面的 order值 + */ + public static final int ASPECT_ORDER = 1000; + } diff --git a/tx-client/src/main/java/com/codingapi/tx/MQTxManagerFegin.java b/tx-client/src/main/java/com/codingapi/tx/MQTxManagerFegin.java new file mode 100644 index 000000000..c6ca8ae9b --- /dev/null +++ b/tx-client/src/main/java/com/codingapi/tx/MQTxManagerFegin.java @@ -0,0 +1,41 @@ +package com.codingapi.tx; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author yizhishang + */ +@FeignClient(value = "tx-manager") +public interface MQTxManagerFegin { + + /** + * 检查并清理事务数据 + * @param groupId 事务组id + * @param waitTaskId 任务id + * @return 事务状态 + */ + @GetMapping("/tx/manager/cleanNotifyTransactionHttp") + String cleanNotifyTransactionHttp(@RequestParam(value = "groupId") String groupId, @RequestParam(value = "waitTaskId") String waitTaskId); + + /** + * 记录补偿事务数据到tm + */ + @PostMapping("/tx/manager/sendCompensateMsg") + String sendCompensateMsg(@RequestParam("currentTime") long currentTime, @RequestParam("groupId") String groupId, + @RequestParam("model") String model, @RequestParam("address") String address, + @RequestParam("uniqueKey") String uniqueKey, + @RequestParam("className") String className, @RequestParam("methodStr") String methodStr, + @RequestParam("data") String data, @RequestParam("time") long time, + @RequestParam("startError") int startError); + + /** + * 获取TM服务地址 + * @return txServer + */ + @GetMapping("/tx/manager/getServer") + String getServer(); + +} diff --git a/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransaction.java b/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransaction.java index 6540ddc45..4584b13ad 100644 --- a/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransaction.java +++ b/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransaction.java @@ -32,4 +32,17 @@ */ Class[] noRollbackFor() default {}; + /** + * 事务模式 仅在事务发起方配置有效 + * @return + */ + TxTransactionMode mode() default TxTransactionMode.TX_MODE_LCN; + + /** + * 标示本服务是否是只读 + * 若为true : 不会加入事务组; Connection 不会被 Wrap; 事务信息能正常传递 + * 在本服务无DB操作或仅有查询时请配置 true 将提高性能 + * 若应用都没有DB配置,此配置无意义不用设值 + */ + boolean readOnly() default false; } diff --git a/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransactionMode.java b/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransactionMode.java new file mode 100644 index 000000000..1a549edea --- /dev/null +++ b/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransactionMode.java @@ -0,0 +1,22 @@ +package com.codingapi.tx.annotation; + +/** + * + * + * @author caican + * 2018/7/24 + */ +public enum TxTransactionMode { + /** LCN 模式 */ + TX_MODE_LCN("LCN 模式,2阶段提交 读提交"), + + /** TXC 模式 */ + TX_MODE_TXC("TXC 模式,未提交读(READ UNCOMMITTED)"); + + + private String description; + + TxTransactionMode(String description) { + this.description = description; + } +} diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxCompensateLocal.java b/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxCompensateLocal.java index c4053460a..19037daf2 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxCompensateLocal.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxCompensateLocal.java @@ -6,7 +6,7 @@ */ public class TxCompensateLocal { - private final static ThreadLocal currentLocal = new InheritableThreadLocal(); + private final static ThreadLocal currentLocal = new InheritableThreadLocal<>(); private String groupId; diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionInfo.java b/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionInfo.java index 7c4c87c41..194788f0b 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionInfo.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionInfo.java @@ -1,6 +1,7 @@ package com.codingapi.tx.aop.bean; import com.codingapi.tx.annotation.TxTransaction; +import com.codingapi.tx.annotation.TxTransactionMode; import com.codingapi.tx.model.TransactionInvocation; @@ -11,8 +12,7 @@ public class TxTransactionInfo { - private TxTransaction transaction; - + private TxTransaction txTransaction; private TxTransactionLocal txTransactionLocal; @@ -24,17 +24,25 @@ public class TxTransactionInfo { private TransactionInvocation invocation; + private TxTransactionMode mode; - public TxTransactionInfo(TxTransaction transaction, TxTransactionLocal txTransactionLocal, TransactionInvocation invocation, String txGroupId) { - this.transaction = transaction; + public TxTransactionInfo(TxTransaction txTransaction, TxTransactionLocal txTransactionLocal, TransactionInvocation invocation, String txGroupId) { + this.txTransaction = txTransaction; this.txTransactionLocal = txTransactionLocal; this.txGroupId = txGroupId; this.invocation = invocation; } + public TxTransactionMode getMode() { + return mode; + } + + public void setMode(TxTransactionMode mode) { + this.mode = mode; + } - public TxTransaction getTransaction() { - return transaction; + public TxTransaction getTxTransaction() { + return txTransaction; } public TxTransactionLocal getTxTransactionLocal() { diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionLocal.java b/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionLocal.java index d5998d716..3fce24713 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionLocal.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionLocal.java @@ -1,6 +1,7 @@ package com.codingapi.tx.aop.bean; import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.annotation.TxTransactionMode; import com.codingapi.tx.framework.utils.SocketManager; import com.codingapi.tx.model.Request; import org.apache.commons.lang.StringUtils; @@ -18,7 +19,7 @@ public class TxTransactionLocal { private Logger logger = LoggerFactory.getLogger(TxTransactionLocal.class); - private final static ThreadLocal currentLocal = new InheritableThreadLocal(); + private final static ThreadLocal currentLocal = new InheritableThreadLocal<>(); private String groupId; @@ -48,6 +49,8 @@ public class TxTransactionLocal { private boolean readOnly = false; + private TxTransactionMode mode; + public boolean isHasIsGroup() { return hasIsGroup; } @@ -106,6 +109,13 @@ public static void setCurrent(TxTransactionLocal current) { currentLocal.set(current); } + public TxTransactionMode getMode() { + return mode; + } + + public void setMode(TxTransactionMode mode) { + this.mode = mode; + } public void putLoadBalance(String key, String data){ cacheModelInfo.put(key,data); @@ -157,4 +167,14 @@ public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } + public static boolean isInTxcTransaction() { + TxTransactionLocal local = current(); + if (local != null + && local.mode != null + && local.mode == TxTransactionMode.TX_MODE_TXC + && !local.isReadOnly()) { + return true; + } + return false; + } } diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/service/AspectBeforeService.java b/tx-client/src/main/java/com/codingapi/tx/aop/service/AspectBeforeService.java index 419fbba93..f8dc69d7d 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/service/AspectBeforeService.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/service/AspectBeforeService.java @@ -7,5 +7,5 @@ */ public interface AspectBeforeService { - Object around(String groupId, ProceedingJoinPoint point) throws Throwable; + Object around(String groupId, ProceedingJoinPoint point, String mode) throws Throwable; } diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/AspectBeforeServiceImpl.java b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/AspectBeforeServiceImpl.java index 098e911d5..7bc860b2f 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/AspectBeforeServiceImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/AspectBeforeServiceImpl.java @@ -1,6 +1,7 @@ package com.codingapi.tx.aop.service.impl; import com.codingapi.tx.annotation.TxTransaction; +import com.codingapi.tx.annotation.TxTransactionMode; import com.codingapi.tx.aop.bean.TxTransactionInfo; import com.codingapi.tx.aop.bean.TxTransactionLocal; import com.codingapi.tx.aop.service.AspectBeforeService; @@ -28,8 +29,8 @@ public class AspectBeforeServiceImpl implements AspectBeforeService { private Logger logger = LoggerFactory.getLogger(AspectBeforeServiceImpl.class); - - public Object around(String groupId, ProceedingJoinPoint point) throws Throwable { + @Override + public Object around(String groupId, ProceedingJoinPoint point, String mode) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); @@ -46,6 +47,11 @@ public Object around(String groupId, ProceedingJoinPoint point) throws Throwable TransactionInvocation invocation = new TransactionInvocation(clazz, thisMethod.getName(), thisMethod.toString(), args, method.getParameterTypes()); TxTransactionInfo info = new TxTransactionInfo(transaction,txTransactionLocal,invocation,groupId); + try { + info.setMode(TxTransactionMode.valueOf(mode)); + } catch (Exception e) { + info.setMode(TxTransactionMode.TX_MODE_LCN); + } TransactionServer server = transactionServerFactoryService.createTransactionServer(info); diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TransactionServerFactoryServiceImpl.java b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TransactionServerFactoryServiceImpl.java index 229c89e92..f47a9d9d3 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TransactionServerFactoryServiceImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TransactionServerFactoryServiceImpl.java @@ -1,23 +1,24 @@ package com.codingapi.tx.aop.service.impl; - import com.codingapi.tx.aop.bean.TxTransactionInfo; import com.codingapi.tx.aop.service.TransactionServer; import com.codingapi.tx.aop.service.TransactionServerFactoryService; import com.codingapi.tx.datasource.ILCNTransactionControl; -import com.codingapi.tx.netty.service.NettyService; +import com.codingapi.tx.framework.utils.SocketManager; import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; - - /** - * Created by lorne on 2017/6/8. + * @author lorne + * @date 2017/6/8 */ @Service public class TransactionServerFactoryServiceImpl implements TransactionServerFactoryService { + private Logger logger = LoggerFactory.getLogger(TransactionServerFactoryServiceImpl.class); @Autowired private TransactionServer txStartTransactionServer; @@ -31,48 +32,53 @@ public class TransactionServerFactoryServiceImpl implements TransactionServerFac @Autowired private TransactionServer txRunningNoTransactionServer; - @Autowired - private NettyService nettyService; - @Autowired private ILCNTransactionControl transactionControl; - + @Override public TransactionServer createTransactionServer(TxTransactionInfo info) throws Throwable { + if (!SocketManager.getInstance().isNetState()) { + //检查socket通讯是否正常 (第一次执行时启动txRunningTransactionServer的业务处理控制,然后嵌套调用其他事务的业务方法时都并到txInServiceTransactionServer业务处理下) + logger.warn("tx-manager not connected."); + return txDefaultTransactionServer; + } + /*********分布式事务处理逻辑***********/ + logger.info("分布式事务处理逻辑...开始"); - /*********分布式事务处理逻辑*开始***********/ - - /** 尽当Transaction注解不为空,其他都为空时。表示分布式事务开始启动 **/ - if (info.getTransaction() != null && info.getTransaction().isStart() && info.getTxTransactionLocal() == null && StringUtils.isEmpty(info.getTxGroupId())) { + /** 事务发起方:仅当TxTransaction注解不为空,其他都为空时。表示分布式事务开始启动 **/ + if (info.getTxTransaction() != null && info.getTxTransaction().isStart() && info.getTxTransactionLocal() == null && StringUtils.isEmpty(info.getTxGroupId())) { //检查socket通讯是否正常 (当启动事务的主业务方法执行完以后,再执行其他业务方法时将进入txInServiceTransactionServer业务处理) - if (nettyService.checkState()) { + if (SocketManager.getInstance().isNetState()) { return txStartTransactionServer; } else { - throw new Exception("tx-manager not connected ,please check tx-manager server "); + logger.warn("tx-manager not connected."); + return txDefaultTransactionServer; } } - - /** 分布式事务已经开启,业务进行中 **/ + /** 事务参与方:分布式事务已经开启,业务进行中 **/ + logger.debug("事务参与方:分布式事务已经开启,业务进行中"); if (info.getTxTransactionLocal() != null || StringUtils.isNotEmpty(info.getTxGroupId())) { //检查socket通讯是否正常 (第一次执行时启动txRunningTransactionServer的业务处理控制,然后嵌套调用其他事务的业务方法时都并到txInServiceTransactionServer业务处理下) - if (nettyService.checkState()) { + if (SocketManager.getInstance().isNetState()) { if (info.getTxTransactionLocal() != null) { return txDefaultTransactionServer; } else { - if(!transactionControl.isNoTransactionOperation()) { //有事务业务的操作 - return txRunningTransactionServer; - }else { + /** 表示整个应用没有获取过DB连接 || 无事务业务的操作 **/ + if (transactionControl.isNoTransactionOperation() || info.getTxTransaction().readOnly()) { return txRunningNoTransactionServer; + } else { + return txRunningTransactionServer; } } } else { - throw new Exception("tx-manager not connected ,please check tx-manager server "); + logger.warn("tx-manager not connected."); + return txDefaultTransactionServer; } } /*********分布式事务处理逻辑*结束***********/ - + logger.debug("分布式事务处理逻辑*结束"); return txDefaultTransactionServer; } } diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxDefaultTransactionServerImpl.java b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxDefaultTransactionServerImpl.java index 7dca22511..b7df72477 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxDefaultTransactionServerImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxDefaultTransactionServerImpl.java @@ -3,6 +3,8 @@ import com.codingapi.tx.aop.bean.TxTransactionInfo; import com.codingapi.tx.aop.service.TransactionServer; import org.aspectj.lang.ProceedingJoinPoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; /** @@ -11,10 +13,11 @@ @Service(value = "txDefaultTransactionServer") public class TxDefaultTransactionServerImpl implements TransactionServer { - + private Logger logger = LoggerFactory.getLogger(getClass()); @Override public Object execute(ProceedingJoinPoint point, TxTransactionInfo info) throws Throwable { + logger.info("默认事务管理器..."); return point.proceed(); } } diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningNoTransactionServerImpl.java b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningNoTransactionServerImpl.java index a26372a49..0ce99a013 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningNoTransactionServerImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningNoTransactionServerImpl.java @@ -23,9 +23,11 @@ public class TxRunningNoTransactionServerImpl implements TransactionServer { @Override public Object execute(final ProceedingJoinPoint point, final TxTransactionInfo info) throws Throwable { + logger.info("无事务模块..."); + String kid = KidUtils.generateShortUuid(); String txGroupId = info.getTxGroupId(); - logger.debug("--->begin no db transaction, groupId: " + txGroupId); + logger.debug("--->begin readonly transaction, groupId: " + txGroupId); long t1 = System.currentTimeMillis(); @@ -34,6 +36,8 @@ public Object execute(final ProceedingJoinPoint point, final TxTransactionInfo i txTransactionLocal.setHasStart(false); txTransactionLocal.setKid(kid); txTransactionLocal.setMaxTimeOut(Constants.txServer.getCompensateMaxWaitTime()); + txTransactionLocal.setMode(info.getMode()); + txTransactionLocal.setReadOnly(true); TxTransactionLocal.setCurrent(txTransactionLocal); try { @@ -43,7 +47,7 @@ public Object execute(final ProceedingJoinPoint point, final TxTransactionInfo i } finally { TxTransactionLocal.setCurrent(null); long t2 = System.currentTimeMillis(); - logger.debug("<---end no db transaction,groupId:" + txGroupId+",execute time:"+(t2-t1)); + logger.debug("<---end readonly transaction,groupId:" + txGroupId+",execute time:"+(t2-t1)); } } diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningTransactionServerImpl.java b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningTransactionServerImpl.java index b2a606bf7..68ec4add4 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningTransactionServerImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningTransactionServerImpl.java @@ -26,20 +26,19 @@ @Service(value = "txRunningTransactionServer") public class TxRunningTransactionServerImpl implements TransactionServer { - @Autowired private MQTxManagerService txManagerService; - @Autowired private ILCNTransactionControl transactionControl; - private Logger logger = LoggerFactory.getLogger(TxRunningTransactionServerImpl.class); @Override public Object execute(final ProceedingJoinPoint point, final TxTransactionInfo info) throws Throwable { + logger.info("事务参与方..."); + String kid = KidUtils.generateShortUuid(); String txGroupId = info.getTxGroupId(); logger.debug("--->begin running transaction,groupId:" + txGroupId); @@ -47,16 +46,15 @@ public Object execute(final ProceedingJoinPoint point, final TxTransactionInfo i boolean isHasIsGroup = transactionControl.hasGroup(txGroupId); - TxTransactionLocal txTransactionLocal = new TxTransactionLocal(); txTransactionLocal.setGroupId(txGroupId); txTransactionLocal.setHasStart(false); txTransactionLocal.setKid(kid); txTransactionLocal.setHasIsGroup(isHasIsGroup); txTransactionLocal.setMaxTimeOut(Constants.txServer.getCompensateMaxWaitTime()); + txTransactionLocal.setMode(info.getMode()); TxTransactionLocal.setCurrent(txTransactionLocal); - try { Object res = point.proceed(); @@ -94,6 +92,19 @@ public Object execute(final ProceedingJoinPoint point, final TxTransactionInfo i return res; } catch (Throwable e) { + // 这里处理以下情况:当 point.proceed() 业务代码中 db事务正常提交,开始等待,后续处理发生异常。 + // 由于没有加入事务组,不会收到通知。这里唤醒并回滚 + if(!isHasIsGroup) { + String type = txTransactionLocal.getType(); + TxTask waitTask = TaskGroupManager.getInstance().getTask(kid, type); + // 有一定几率不能唤醒: wait的代码是在另一个线程,有可能线程还没执行到wait,先执行到了这里 + // TODO 要不要 sleep 1毫秒 + logger.warn("wake the waitTask: {}", (waitTask != null && waitTask.isAwait())); + if (waitTask != null && waitTask.isAwait()) { + waitTask.setState(-1); + waitTask.signalTask(); + } + } throw e; } finally { TxTransactionLocal.setCurrent(null); diff --git a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxStartTransactionServerImpl.java b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxStartTransactionServerImpl.java index ba79c537b..7dcdbcb1f 100644 --- a/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxStartTransactionServerImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxStartTransactionServerImpl.java @@ -35,7 +35,9 @@ public class TxStartTransactionServerImpl implements TransactionServer { public Object execute(ProceedingJoinPoint point,final TxTransactionInfo info) throws Throwable { //分布式事务开始执行 - logger.debug("--->begin start transaction"); + logger.info("事务发起方..."); + + logger.debug("--->分布式事务开始执行 begin start transaction"); final long start = System.currentTimeMillis(); @@ -44,16 +46,17 @@ public Object execute(ProceedingJoinPoint point,final TxTransactionInfo info) th final String groupId = TxCompensateLocal.current()==null?KidUtils.generateShortUuid():TxCompensateLocal.current().getGroupId(); //创建事务组 + logger.debug("创建事务组并发送消息"); txManagerService.createTransactionGroup(groupId); - TxTransactionLocal txTransactionLocal = new TxTransactionLocal(); txTransactionLocal.setGroupId(groupId); txTransactionLocal.setHasStart(true); txTransactionLocal.setMaxTimeOut(Constants.txServer.getCompensateMaxWaitTime()); + txTransactionLocal.setMode(info.getTxTransaction().mode()); + txTransactionLocal.setReadOnly(info.getTxTransaction().readOnly()); TxTransactionLocal.setCurrent(txTransactionLocal); - try { Object obj = point.proceed(); state = 1; @@ -99,7 +102,7 @@ public Object execute(ProceedingJoinPoint point,final TxTransactionInfo info) th long end = System.currentTimeMillis(); long time = end - start; if ((executeConnectionError == 1&&rs == 1)||(lastState == 1 && rs == 0)) { - //记录补偿日志 + logger.debug("记录补偿日志"); txManagerService.sendCompensateMsg(groupId, time, info,executeConnectionError); } }else{ @@ -111,7 +114,7 @@ public Object execute(ProceedingJoinPoint point,final TxTransactionInfo info) th } TxTransactionLocal.setCurrent(null); - logger.debug("<---end start transaction"); + logger.debug("<---分布式事务 end start transaction"); logger.debug("start transaction over, res -> groupId:" + groupId + ", now state:" + (lastState == 1 ? "commit" : "rollback")); } @@ -130,7 +133,7 @@ private int rollbackException(TxTransactionInfo info,Throwable throwable){ } //回滚异常检测. - for(Class rollbackFor:info.getTransaction().rollbackFor()){ + for(Class rollbackFor:info.getTxTransaction().rollbackFor()){ //存在关系 if(rollbackFor.isAssignableFrom(throwable.getClass())){ @@ -140,7 +143,7 @@ private int rollbackException(TxTransactionInfo info,Throwable throwable){ } //不回滚异常检测. - for(Class rollbackFor:info.getTransaction().noRollbackFor()){ + for(Class rollbackFor:info.getTxTransaction().noRollbackFor()){ //存在关系 if(rollbackFor.isAssignableFrom(throwable.getClass())){ diff --git a/tx-client/src/main/java/com/codingapi/tx/config/ConfigReader.java b/tx-client/src/main/java/com/codingapi/tx/config/ConfigReader.java deleted file mode 100644 index 63e90b257..000000000 --- a/tx-client/src/main/java/com/codingapi/tx/config/ConfigReader.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.codingapi.tx.config; - -import com.codingapi.tx.config.service.TxManagerTxUrlService; -import com.lorne.core.framework.utils.config.ConfigUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -/** - * create by lorne on 2017/11/13 - */ -@Component -public class ConfigReader { - - - private Logger logger = LoggerFactory.getLogger(ConfigReader.class); - - - private TxManagerTxUrlService txManagerTxUrlService; - - @Autowired - private ApplicationContext spring; - - - public String getTxUrl() { - - try { - txManagerTxUrlService = spring.getBean(TxManagerTxUrlService.class); - }catch (Exception e){ - logger.debug("load default txManagerTxUrlService "); - } - - if(txManagerTxUrlService == null){ - txManagerTxUrlService = new TxManagerTxUrlService() { - - private final String configName = "tx.properties"; - - private final String configKey = "url"; - - @Override - public String getTxUrl() { - return ConfigUtils.getString(configName,configKey); - } - }; - - logger.debug("load default txManagerTxUrlService"); - }else{ - logger.debug("load txManagerTxUrlService"); - } - - return txManagerTxUrlService.getTxUrl(); - } - - -} diff --git a/tx-client/src/main/java/com/codingapi/tx/config/service/TxManagerTxUrlService.java b/tx-client/src/main/java/com/codingapi/tx/config/service/TxManagerTxUrlService.java deleted file mode 100644 index 3d8004658..000000000 --- a/tx-client/src/main/java/com/codingapi/tx/config/service/TxManagerTxUrlService.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.codingapi.tx.config.service; - -/** - * create by lorne on 2017/11/18 - */ -public interface TxManagerTxUrlService { - - String getTxUrl(); -} diff --git a/tx-client/src/main/java/com/codingapi/tx/control/service/impl/TransactionControlServiceImpl.java b/tx-client/src/main/java/com/codingapi/tx/control/service/impl/TransactionControlServiceImpl.java index 85fd9fc4d..47021636e 100644 --- a/tx-client/src/main/java/com/codingapi/tx/control/service/impl/TransactionControlServiceImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/control/service/impl/TransactionControlServiceImpl.java @@ -47,6 +47,6 @@ public void notifyTransactionMsg(ChannelHandlerContext ctx,JSONObject resObj, St SocketUtils.sendMsg(ctx, data.toString()); - logger.debug("send notify data ->" + data.toString()); + logger.info("send notify data ->" + data.toString()); } } diff --git a/tx-client/src/main/java/com/codingapi/tx/datasource/AbstractResourceProxy.java b/tx-client/src/main/java/com/codingapi/tx/datasource/AbstractResourceProxy.java index bcfe99551..69d21f1b4 100644 --- a/tx-client/src/main/java/com/codingapi/tx/datasource/AbstractResourceProxy.java +++ b/tx-client/src/main/java/com/codingapi/tx/datasource/AbstractResourceProxy.java @@ -1,6 +1,7 @@ package com.codingapi.tx.datasource; +import com.codingapi.tx.annotation.TxTransactionMode; import com.codingapi.tx.aop.bean.TxTransactionLocal; import com.codingapi.tx.datasource.service.DataSourceService; import com.lorne.core.framework.utils.task.Task; @@ -64,6 +65,8 @@ public void close(ILCNResource connection) { protected abstract C createLcnConnection(C connection, TxTransactionLocal txTransactionLocal); + protected abstract C createTxcConnection(C connection, TxTransactionLocal txTransactionLocal); + protected abstract void initDbType(); @@ -76,6 +79,10 @@ protected ILCNResource loadConnection(){ logger.debug("loadConnection -> null !"); return null; } + if (txTransactionLocal.isReadOnly()) { + logger.debug("readonly tx don't reuse connection."); + return null; + } //是否获取旧连接的条件:同一个模块下被多次调用时第一次的事务操作 ILCNResource old = pools.get(txTransactionLocal.getGroupId()); @@ -95,6 +102,11 @@ protected ILCNResource loadConnection(){ private C createConnection(TxTransactionLocal txTransactionLocal, C connection){ + if (txTransactionLocal.getMode() != null + && txTransactionLocal.getMode() == TxTransactionMode.TX_MODE_TXC) { + // txc 模式下没有maxCount的限制 直接创建 + return createTxcConnection(connection, txTransactionLocal); + } if (nowCount == maxCount) { for (int i = 0; i < maxWaitTime; i++) { for(int j=0;j<100;j++){ @@ -124,7 +136,8 @@ protected C initLCNConnection(C connection) { C lcnConnection = connection; TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); - if (txTransactionLocal != null&&!txTransactionLocal.isHasConnection()) { + if (txTransactionLocal != null&&!txTransactionLocal.isHasConnection() + && !txTransactionLocal.isReadOnly()) { logger.debug("lcn datasource transaction control "); diff --git a/tx-client/src/main/java/com/codingapi/tx/framework/thread/NamedThreadFactory.java b/tx-client/src/main/java/com/codingapi/tx/framework/thread/NamedThreadFactory.java new file mode 100644 index 000000000..13bc4abbc --- /dev/null +++ b/tx-client/src/main/java/com/codingapi/tx/framework/thread/NamedThreadFactory.java @@ -0,0 +1,55 @@ +package com.codingapi.tx.framework.thread; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * [类描述] + * + * @author caican + * 17/9/12 + */ +public class NamedThreadFactory implements ThreadFactory +{ + private static final AtomicInteger POOL_SEQ = new AtomicInteger(1); + + private final AtomicInteger mThreadNum = new AtomicInteger(1); + + private final String mPrefix; + + private final boolean mDaemo; + + private final ThreadGroup mGroup; + + public NamedThreadFactory() + { + this("pool-" + POOL_SEQ.getAndIncrement(),false); + } + + public NamedThreadFactory(String prefix) + { + this(prefix,false); + } + + public NamedThreadFactory(String prefix, boolean daemo) + { + mPrefix = prefix + "-thread-"; + mDaemo = daemo; + SecurityManager s = System.getSecurityManager(); + mGroup = ( s == null ) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup(); + } + + @Override + public Thread newThread(Runnable runnable) + { + String name = mPrefix + mThreadNum.getAndIncrement(); + Thread ret = new Thread(mGroup,runnable,name,0); + ret.setDaemon(mDaemo); + return ret; + } + + public ThreadGroup getThreadGroup() + { + return mGroup; + } +} diff --git a/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketManager.java b/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketManager.java index 836073148..fa870bfff 100644 --- a/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketManager.java +++ b/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketManager.java @@ -1,5 +1,6 @@ package com.codingapi.tx.framework.utils; +import com.codingapi.tx.framework.thread.NamedThreadFactory; import com.codingapi.tx.model.Request; import com.lorne.core.framework.utils.task.ConditionUtils; import com.lorne.core.framework.utils.task.IBack; @@ -34,7 +35,7 @@ public class SocketManager { private static SocketManager manager = null; - private ExecutorService threadPool = Executors.newFixedThreadPool(max_size); + private ExecutorService threadPool = Executors.newFixedThreadPool(max_size, new NamedThreadFactory("sender")); private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(max_size); diff --git a/tx-client/src/main/java/com/codingapi/tx/listener/service/impl/InitServiceImpl.java b/tx-client/src/main/java/com/codingapi/tx/listener/service/impl/InitServiceImpl.java index b16d8a2e5..a27b0499c 100644 --- a/tx-client/src/main/java/com/codingapi/tx/listener/service/impl/InitServiceImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/listener/service/impl/InitServiceImpl.java @@ -21,20 +21,7 @@ public class InitServiceImpl implements InitService { @Override public void start() { nettyService.start(); - welcome(); logger.info("socket-start.."); } - private void welcome(){ - System.out.println(); - System.out.println(); - System.out.println("\t\t** \t\t ****\t\t** **"); - System.out.println("\t\t** \t\t** \t\t*** **"); - System.out.println("\t\t** \t\t** \t\t** ***"); - System.out.println("\t\t*****\t\t ****\t\t** **"); - System.out.println(); - System.out.println("\t\tLCN-Client version:4.1.0"); - System.out.println(); - } - } diff --git a/tx-client/src/main/java/com/codingapi/tx/netty/service/MQTxManagerFeginService.java b/tx-client/src/main/java/com/codingapi/tx/netty/service/MQTxManagerFeginService.java new file mode 100644 index 000000000..d1f0314ce --- /dev/null +++ b/tx-client/src/main/java/com/codingapi/tx/netty/service/MQTxManagerFeginService.java @@ -0,0 +1,38 @@ +package com.codingapi.tx.netty.service; + +/** + * @author yizhishang + */ +public interface MQTxManagerFeginService { + + /** + * 检查并清理事务数据 + * @param groupId 事务组id + * @param waitTaskId 任务id + * @return 事务状态 + */ + String cleanNotifyTransactionHttp(String groupId, String waitTaskId); + + /** + * 记录补偿事务数据到tm + * @param currentTime 时间 + * @param groupId 事务组id + * @param model 模块名称 + * @param address 模块地址 + * @param uniqueKey 唯一标示 + * @param className 事务启动类 + * @param methodStr 事务启动方法 + * @param data 切面数据 + * @param time 执行时间 + * @param startError 启动模块异常 + * @return 是否保存成功 + */ + String sendCompensateMsg(long currentTime, String groupId, String model, String address, String uniqueKey, String className, String methodStr, String data, long time,int startError); + + /** + * 获取TM服务地址 + * @return txServer + */ + String getServer(); + +} diff --git a/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestHelper.java b/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestHelper.java deleted file mode 100644 index c3438938d..000000000 --- a/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestHelper.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.codingapi.tx.netty.service; - - -import com.lorne.core.framework.utils.http.HttpUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -/** - * create by lorne on 2017/11/17 - */ -@Component -public class TxManagerHttpRequestHelper { - - - private TxManagerHttpRequestService httpRequestService; - - @Autowired - private ApplicationContext spring; - - private Logger logger = LoggerFactory.getLogger(TxManagerHttpRequestHelper.class); - - - private void reloadHttpRequestService(){ - try { - httpRequestService = spring.getBean(TxManagerHttpRequestService.class); - }catch (Exception e){ - logger.debug("load default httpRequestService "); - } - - if(httpRequestService==null){ - httpRequestService = new TxManagerHttpRequestService() { - @Override - public String httpGet(String url) { - return HttpUtils.get(url); - } - - @Override - public String httpPost(String url, String params) { - return HttpUtils.post(url, params); - } - }; - logger.info("load default HttpRequestService ."); - }else { - logger.info("load HttpRequestService ."); - } - } - - public String httpGet(String url) { - reloadHttpRequestService(); - return httpRequestService.httpGet(url); - } - - public String httpPost(String url, String params) { - reloadHttpRequestService(); - return httpRequestService.httpPost(url,params); - } - - -} diff --git a/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestService.java b/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestService.java deleted file mode 100644 index e8ae5769d..000000000 --- a/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.codingapi.tx.netty.service; - -/** - * create by lorne on 2017/11/17 - */ -public interface TxManagerHttpRequestService { - - String httpGet(String url); - - String httpPost(String url, String params); - -} diff --git a/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerFeginServiceImpl.java b/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerFeginServiceImpl.java new file mode 100644 index 000000000..bc6a7c2c8 --- /dev/null +++ b/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerFeginServiceImpl.java @@ -0,0 +1,48 @@ +package com.codingapi.tx.netty.service.impl; + +import com.codingapi.tx.MQTxManagerFegin; +import com.codingapi.tx.netty.service.MQTxManagerFeginService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +/** + * @author yizhishang + */ +@Service +public class MQTxManagerFeginServiceImpl implements MQTxManagerFeginService { + + @Autowired + private MQTxManagerFegin mqTxManagerFegin; + + /** + * 检查并清理事务数据 + * + * @param groupId 事务组id + * @param waitTaskId 任务id + * @return 事务状态 + */ + @Override + public String cleanNotifyTransactionHttp(String groupId, String waitTaskId) { + return mqTxManagerFegin.cleanNotifyTransactionHttp(groupId, waitTaskId); + } + + /** + * 记录补偿事务数据到tm + */ + @Override + public String sendCompensateMsg(long currentTime, String groupId, String model, String address, String uniqueKey, String className, String methodStr, String data, long time,int startError) { + return mqTxManagerFegin.sendCompensateMsg(currentTime, groupId, model, address, uniqueKey, className, methodStr, data, time,startError); + } + + /** + * 获取TM服务地址 + * + * @return txServer + */ + @Override + public String getServer() { + return mqTxManagerFegin.getServer(); + } + +} diff --git a/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerServiceImpl.java b/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerServiceImpl.java index 5362b63d8..a6fbb4a10 100644 --- a/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerServiceImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerServiceImpl.java @@ -1,18 +1,16 @@ package com.codingapi.tx.netty.service.impl; import com.alibaba.fastjson.JSONObject; -import com.codingapi.tx.aop.bean.TxCompensateLocal; import com.codingapi.tx.aop.bean.TxTransactionInfo; import com.codingapi.tx.compensate.model.CompensateInfo; import com.codingapi.tx.compensate.service.CompensateService; -import com.codingapi.tx.config.ConfigReader; import com.codingapi.tx.framework.utils.SerializerUtils; import com.codingapi.tx.framework.utils.SocketManager; import com.codingapi.tx.listener.service.ModelNameService; import com.codingapi.tx.model.Request; import com.codingapi.tx.model.TxGroup; +import com.codingapi.tx.netty.service.MQTxManagerFeginService; import com.codingapi.tx.netty.service.MQTxManagerService; -import com.codingapi.tx.netty.service.TxManagerHttpRequestHelper; import com.lorne.core.framework.utils.encode.Base64Utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -27,15 +25,11 @@ public class MQTxManagerServiceImpl implements MQTxManagerService { @Autowired private ModelNameService modelNameService; - @Autowired - private ConfigReader configReader; - @Autowired private CompensateService compensateService; @Autowired - private TxManagerHttpRequestHelper managerHelper; - + private MQTxManagerFeginService mqTxManagerFeginService; @Override public void createTransactionGroup(String groupId) { @@ -57,7 +51,6 @@ public TxGroup addTransactionGroup(String groupId, String taskId, boolean isGrou return TxGroup.parser(json); } - @Override public int closeTransactionGroup(final String groupId, final int state) { JSONObject jsonObject = new JSONObject(); @@ -100,8 +93,7 @@ public int cleanNotifyTransaction(String groupId, String taskId) { @Override public int cleanNotifyTransactionHttp(String groupId, String waitTaskId) { - String url = configReader.getTxUrl() + "cleanNotifyTransactionHttp?groupId=" + groupId + "&taskId=" + waitTaskId; - String clearRes = managerHelper.httpGet(url); + String clearRes = mqTxManagerFeginService.cleanNotifyTransactionHttp(groupId, waitTaskId); if(clearRes==null){ return -1; } @@ -111,8 +103,7 @@ public int cleanNotifyTransactionHttp(String groupId, String waitTaskId) { @Override public String httpGetServer() { - String url = configReader.getTxUrl() + "getServer"; - return managerHelper.httpGet(url); + return mqTxManagerFeginService.getServer(); } @Override @@ -133,7 +124,7 @@ public void sendCompensateMsg(String groupId, long time, TxTransactionInfo info, CompensateInfo compensateInfo = new CompensateInfo(currentTime, modelName, uniqueKey, data, methodStr, className, groupId, address, time,startError); - String json = managerHelper.httpPost(configReader.getTxUrl() + "sendCompensateMsg", compensateInfo.toParamsString()); + String json = mqTxManagerFeginService.sendCompensateMsg(currentTime, groupId, modelName, address, uniqueKey, className, methodStr, data, time,startError); compensateInfo.setResJson(json); diff --git a/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyControlServiceImpl.java b/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyControlServiceImpl.java index 052a647b8..24b57e348 100644 --- a/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyControlServiceImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyControlServiceImpl.java @@ -13,6 +13,8 @@ import com.lorne.core.framework.utils.task.Task; import io.netty.channel.ChannelHandlerContext; import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -22,6 +24,7 @@ @Service public class NettyControlServiceImpl implements NettyControlService { + private Logger logger = LoggerFactory.getLogger(NettyControlServiceImpl.class); @Autowired private NettyService nettyService; @@ -49,18 +52,15 @@ public void restart() { @Override public void uploadModelInfo() { - new Thread(new Runnable() { - @Override - public void run() { - while (!SocketManager.getInstance().isNetState()|| !IpAddressUtils.isIpAddress(modelNameService.getIpAddress())) { - try { - Thread.sleep(1000 * 5); - } catch (InterruptedException e) { - e.printStackTrace(); - } + new Thread(() -> { + while (!SocketManager.getInstance().isNetState()|| !IpAddressUtils.isIpAddress(modelNameService.getIpAddress())) { + try { + Thread.sleep(1000 * 5); + } catch (InterruptedException e) { + e.printStackTrace(); } - mqTxManagerService.uploadModelInfo(); } + mqTxManagerService.uploadModelInfo(); }).start(); } @@ -71,12 +71,15 @@ public void executeService(final ChannelHandlerContext ctx,final String json) { JSONObject resObj = JSONObject.parseObject(json); if (resObj.containsKey("a")) { // tm发送数据给tx模块的处理指令 - + logger.info("receive cmd -> {}", json); transactionControlService.notifyTransactionMsg(ctx,resObj,json); }else{ //tx发送数据给tm的响应返回数据 String key = resObj.getString("k"); + if (!"h".equals(key)) { + logger.info("receive response -> {}", json); + } responseMsg(key,resObj); } } diff --git a/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyServiceImpl.java b/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyServiceImpl.java index 599e38878..d633fed26 100644 --- a/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyServiceImpl.java +++ b/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyServiceImpl.java @@ -1,6 +1,7 @@ package com.codingapi.tx.netty.service.impl; import com.codingapi.tx.Constants; +import com.codingapi.tx.framework.thread.NamedThreadFactory; import com.codingapi.tx.framework.utils.SocketManager; import com.codingapi.tx.netty.handler.TransactionHandler; import com.codingapi.tx.netty.service.NettyControlService; @@ -47,7 +48,7 @@ public class NettyServiceImpl implements NettyService ,DisposableBean { private Logger logger = LoggerFactory.getLogger(NettyServiceImpl.class); - private ExecutorService threadPool = Executors.newFixedThreadPool(100); + private ExecutorService threadPool = Executors.newFixedThreadPool(100,new NamedThreadFactory("receiver")); @Override public synchronized void start() { diff --git a/tx-manager/.gitignore b/tx-manager/.gitignore deleted file mode 100644 index 76331cfb8..000000000 --- a/tx-manager/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -nbproject/private/ -nbbuild/ -dist/ -nbdist/ -.nb-gradle/ \ No newline at end of file diff --git a/tx-manager/.mvn/wrapper/maven-wrapper.jar b/tx-manager/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 9cc84ea9b..000000000 Binary files a/tx-manager/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/tx-manager/.mvn/wrapper/maven-wrapper.properties b/tx-manager/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index c31504370..000000000 --- a/tx-manager/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1 +0,0 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip diff --git a/tx-manager/mvnw b/tx-manager/mvnw deleted file mode 100644 index 5bf251c07..000000000 --- a/tx-manager/mvnw +++ /dev/null @@ -1,225 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Migwn, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -echo $MAVEN_PROJECTBASEDIR -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/tx-manager/mvnw.cmd b/tx-manager/mvnw.cmd deleted file mode 100644 index 019bd74d7..000000000 --- a/tx-manager/mvnw.cmd +++ /dev/null @@ -1,143 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" - -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/tx-manager/pom.xml b/tx-manager/pom.xml index 4d163f987..684aee77a 100644 --- a/tx-manager/pom.xml +++ b/tx-manager/pom.xml @@ -5,7 +5,7 @@ com.codingapi tx-manager - 4.1.0 + 4.2.0 jar tx-manager @@ -14,21 +14,35 @@ org.springframework.boot spring-boot-starter-parent - 1.5.4.RELEASE + 2.0.4.RELEASE UTF-8 UTF-8 - 1.7 - 1.7 - 1.7 + 1.8 + 1.8 + 1.8 19.0 - Dalston.SR1 + Finchley.RELEASE + true + true + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + @@ -43,7 +57,6 @@ - io.netty netty-all @@ -52,31 +65,34 @@ org.springframework.cloud - spring-cloud-starter-eureka-server + spring-cloud-starter-netflix-eureka-client + 2.0.0.RELEASE + + + + org.springframework.boot + spring-boot-starter-web - com.google.guava - guava + org.springframework.boot + spring-boot-starter-tomcat - - - - - - - org.springframework.boot - spring-boot-starter-web + spring-boot-starter-undertow - org.springframework.boot - spring-boot-starter-redis - 1.3.8.RELEASE + spring-boot-starter-data-redis + + + + com.alibaba + fastjson + 1.2.47 @@ -87,19 +103,6 @@ - - - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud.version} - pom - import - - - - @@ -144,26 +147,6 @@ - - maven-assembly-plugin - - false - - src/main/build/package.xml - - - - - make-assembly - package - - single - - - - - - org.springframework.boot spring-boot-maven-plugin diff --git a/tx-manager/src/main/build/package.xml b/tx-manager/src/main/build/package.xml deleted file mode 100644 index 5b5cf98ea..000000000 --- a/tx-manager/src/main/build/package.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - package - - zip - - true - - - bin - / - - - src/main/resources - / - - - ${project.build.directory} - / - - *.jar - - - - - - lib - runtime - - ${groupId}:${artifactId} - - - - diff --git a/tx-manager/src/main/java/com/codingapi/tm/ServletInitializer.java b/tx-manager/src/main/java/com/codingapi/tm/ServletInitializer.java index f9ee98fd9..dbf897b36 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/ServletInitializer.java +++ b/tx-manager/src/main/java/com/codingapi/tm/ServletInitializer.java @@ -1,18 +1,16 @@ package com.codingapi.tm; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; /** * Created by lorne on 2017/7/3. */ public class ServletInitializer extends SpringBootServletInitializer { - @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(TxManagerApplication.class); } - } diff --git a/tx-manager/src/main/java/com/codingapi/tm/TxManagerApplication.java b/tx-manager/src/main/java/com/codingapi/tm/TxManagerApplication.java index 156b2821f..e4b8112d8 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/TxManagerApplication.java +++ b/tx-manager/src/main/java/com/codingapi/tm/TxManagerApplication.java @@ -2,15 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; - +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +@EnableEurekaClient @SpringBootApplication -@EnableDiscoveryClient public class TxManagerApplication { - - public static void main(String[] args) { SpringApplication.run(TxManagerApplication.class, args); } diff --git a/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/impl/CompensateDaoImpl.java b/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/impl/CompensateDaoImpl.java index 46b7c2278..2969da2cf 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/impl/CompensateDaoImpl.java +++ b/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/impl/CompensateDaoImpl.java @@ -6,6 +6,8 @@ import com.codingapi.tm.config.ConfigReader; import com.codingapi.tm.redis.service.RedisServerService; import com.lorne.core.framework.utils.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -18,6 +20,7 @@ @Service public class CompensateDaoImpl implements CompensateDao { + Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RedisServerService redisServerService; @@ -33,6 +36,7 @@ public String saveCompensateMsg(TransactionCompensateMsg transactionCompensateMs String json = JSON.toJSONString(transactionCompensateMsg); + logger.debug("保存补偿数据至redis: {}", json); redisServerService.saveCompensateMsg(name, json); return name; diff --git a/tx-manager/src/main/java/com/codingapi/tm/compensate/service/impl/CompensateServiceImpl.java b/tx-manager/src/main/java/com/codingapi/tm/compensate/service/impl/CompensateServiceImpl.java index 84d183bd6..398455aa7 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/compensate/service/impl/CompensateServiceImpl.java +++ b/tx-manager/src/main/java/com/codingapi/tm/compensate/service/impl/CompensateServiceImpl.java @@ -114,6 +114,7 @@ public void autoCompensate(final String compensateKey, TransactionCompensateMsg final String json = JSON.toJSONString(transactionCompensateMsg); logger.info("Auto Compensate->" + json); //自动补偿业务执行... + logger.debug("自动补偿业务执行..."); final int tryTime = configReader.getCompensateTryTime(); boolean autoExecuteRes = false; try { @@ -278,9 +279,11 @@ public void reloadCompensate(TxGroup txGroup) { } } } + logger.info("重新加载补偿数据..."); logger.info("Compensate Loaded->"+JSON.toJSONString(txGroup)); } + @Override public TxGroup getCompensateByGroupId(String groupId) { String json = compensateDao.getCompensateByGroupId(groupId); if (json == null) { diff --git a/tx-manager/src/main/java/com/codingapi/tm/listener/ApplicationStartListener.java b/tx-manager/src/main/java/com/codingapi/tm/listener/ApplicationStartListener.java index ecb20d049..3d86c4918 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/listener/ApplicationStartListener.java +++ b/tx-manager/src/main/java/com/codingapi/tm/listener/ApplicationStartListener.java @@ -1,7 +1,7 @@ package com.codingapi.tm.listener; import com.codingapi.tm.Constants; -import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; +import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @@ -12,12 +12,12 @@ * create by lorne on 2017/8/7 */ @Component -public class ApplicationStartListener implements ApplicationListener { +public class ApplicationStartListener implements ApplicationListener { @Override - public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) { - int serverPort = event.getEmbeddedServletContainer().getPort(); + public void onApplicationEvent(WebServerInitializedEvent event) { + int serverPort = event.getWebServer().getPort(); String ip = getIp(); Constants.address = ip+":"+serverPort; } diff --git a/tx-manager/src/main/java/com/codingapi/tm/listener/service/impl/InitServiceImpl.java b/tx-manager/src/main/java/com/codingapi/tm/listener/service/impl/InitServiceImpl.java index af3e803b2..e9f401841 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/listener/service/impl/InitServiceImpl.java +++ b/tx-manager/src/main/java/com/codingapi/tm/listener/service/impl/InitServiceImpl.java @@ -28,24 +28,8 @@ public void start() { Constants.socketPort = configReader.getSocketPort(); Constants.maxConnection = configReader.getSocketMaxConnection(); nettyServerService.start(); - - welcome(); } - - private void welcome(){ - System.out.println(); - System.out.println(); - System.out.println("\t\t** \t\t ****\t\t** **"); - System.out.println("\t\t** \t\t** \t\t*** **"); - System.out.println("\t\t** \t\t** \t\t** ***"); - System.out.println("\t\t*****\t\t ****\t\t** **"); - System.out.println(); - System.out.println("\t\tLCN-TxManager version:4.1.0"); - System.out.println(); - } - - @Override public void close() { nettyServerService.close(); diff --git a/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/MicroServiceImpl.java b/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/MicroServiceImpl.java index 5a74ac913..5855b14bb 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/MicroServiceImpl.java +++ b/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/MicroServiceImpl.java @@ -6,10 +6,14 @@ import com.codingapi.tm.manager.service.MicroService; import com.codingapi.tm.model.TxServer; import com.codingapi.tm.model.TxState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; import org.springframework.web.client.RestTemplate; import java.util.ArrayList; @@ -23,6 +27,7 @@ @Service public class MicroServiceImpl implements MicroService { + private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RestTemplate restTemplate; @@ -34,7 +39,11 @@ public class MicroServiceImpl implements MicroService { @Autowired private DiscoveryClient discoveryClient; - + /** + * 服务注册 + */ + @Autowired + private Registration registration; private boolean isIp(String ipAddress) { String ip = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}"; @@ -43,15 +52,14 @@ private boolean isIp(String ipAddress) { return matcher.matches(); } - - @Override public TxState getState() { TxState state = new TxState(); - String ipAddress = discoveryClient.getLocalServiceInstance().getHost(); + String ipAddress = registration.getHost(); if(!isIp(ipAddress)){ ipAddress = "127.0.0.1"; } + logger.info("ipAddress: {}, port: {}", ipAddress, Constants.socketPort); state.setIp(ipAddress); state.setPort(Constants.socketPort); state.setMaxConnection(SocketManager.getInstance().getMaxConnection()); diff --git a/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerSenderServiceImpl.java b/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerSenderServiceImpl.java index e5ee4e270..2edae671a 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerSenderServiceImpl.java +++ b/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerSenderServiceImpl.java @@ -110,13 +110,11 @@ private void setChannel(List list) { /** * 事务提交或回归 * - * @param checkSate + * @param checkSate 1-提交事务 */ private boolean transaction(final TxGroup txGroup, final int checkSate) { - - if (checkSate == 1) { - + logger.info("事务提交"); //补偿请求,加载历史数据 if (txGroup.getIsCompensate() == 1) { compensateService.reloadCompensate(txGroup); @@ -135,8 +133,8 @@ public Boolean execute() { final JSONObject jsonObject = new JSONObject(); jsonObject.put("a", "t"); - - if (txGroup.getIsCompensate() == 1) { //补偿请求 + /** 补偿请求 **/ + if (txGroup.getIsCompensate() == 1) { jsonObject.put("c", txInfo.getIsCommit()); } else { //正常业务 jsonObject.put("c", checkSate); @@ -193,24 +191,25 @@ public Boolean execute() { } logger.info("--->" + hasOk + ",group:" + txGroup.getGroupId() + ",state:" + checkSate + ",list:" + txGroup.toJsonString()); return hasOk; - }else{ - //回滚操作只发送通过不需要等待确认 - for (TxInfo txInfo : txGroup.getList()) { - if(txInfo.getChannel()!=null) { - if (txInfo.getIsGroup() == 0) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("a", "t"); - jsonObject.put("c", checkSate); - jsonObject.put("t", txInfo.getKid()); - String key = KidUtils.generateShortUuid(); - jsonObject.put("k", key); - txInfo.getChannel().send(jsonObject.toJSONString()); - } + } + + logger.info("事务回滚"); + //回滚操作只发送通过不需要等待确认 + for (TxInfo txInfo : txGroup.getList()) { + if(txInfo.getChannel()!=null) { + if (txInfo.getIsGroup() == 0) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("a", "t"); + jsonObject.put("c", checkSate); + jsonObject.put("t", txInfo.getKid()); + String key = KidUtils.generateShortUuid(); + jsonObject.put("k", key); + txInfo.getChannel().send(jsonObject.toJSONString()); } } - txManagerService.deleteTxGroup(txGroup); - return true; } + txManagerService.deleteTxGroup(txGroup); + return true; } diff --git a/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerServiceImpl.java b/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerServiceImpl.java index da6e1360f..76700de76 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerServiceImpl.java +++ b/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerServiceImpl.java @@ -23,8 +23,6 @@ @Service public class TxManagerServiceImpl implements TxManagerService { - - @Autowired private ConfigReader configReader; @@ -42,12 +40,12 @@ public class TxManagerServiceImpl implements TxManagerService { @Autowired private CompensateService compensateService; - private Logger logger = LoggerFactory.getLogger(TxManagerServiceImpl.class); @Override public TxGroup createTransactionGroup(String groupId) { + logger.info("创建事物组"); TxGroup txGroup = new TxGroup(); if (compensateService.getCompensateByGroupId(groupId)!=null) { txGroup.setIsCompensate(1); @@ -65,6 +63,8 @@ public TxGroup createTransactionGroup(String groupId) { @Override public TxGroup addTransactionGroup(String groupId, String taskId, int isGroup, String channelAddress, String methodStr) { + + logger.info("添加事务组子对象..."); String key = getTxGroupKey(groupId); TxGroup txGroup = getTxGroup(groupId); if (txGroup==null) { @@ -77,7 +77,6 @@ public TxGroup addTransactionGroup(String groupId, String taskId, int isGroup, S txInfo.setIsGroup(isGroup); txInfo.setMethodStr(methodStr); - ModelInfo modelInfo = ModelInfoManager.getInstance().getModelByChannelName(channelAddress); if(modelInfo!=null) { txInfo.setUniqueKey(modelInfo.getUniqueKey()); @@ -94,6 +93,7 @@ public TxGroup addTransactionGroup(String groupId, String taskId, int isGroup, S @Override public boolean rollbackTransactionGroup(String groupId) { + logger.info("设置强制回滚事务..."); String key = getTxGroupKey(groupId); TxGroup txGroup = getTxGroup(groupId); if (txGroup==null) { @@ -106,6 +106,7 @@ public boolean rollbackTransactionGroup(String groupId) { @Override public int cleanNotifyTransaction(String groupId, String taskId) { + logger.info("检查事务组数据..."); int res = 0; logger.info("start-cleanNotifyTransaction->groupId:"+groupId+",taskId:"+taskId); String key = getTxGroupKey(groupId); @@ -169,6 +170,7 @@ public int cleanNotifyTransaction(String groupId, String taskId) { @Override public int closeTransactionGroup(String groupId,int state) { + logger.info("关闭事务组"); String key = getTxGroupKey(groupId); TxGroup txGroup = getTxGroup(groupId); if(txGroup==null){ diff --git a/tx-manager/src/main/java/com/codingapi/tm/redis/JedisClusterConfig.java b/tx-manager/src/main/java/com/codingapi/tm/redis/JedisClusterConfig.java deleted file mode 100644 index 6f7112431..000000000 --- a/tx-manager/src/main/java/com/codingapi/tm/redis/JedisClusterConfig.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.codingapi.tm.redis; - - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.core.env.MapPropertySource; -import org.springframework.data.redis.connection.RedisClusterConfiguration; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import redis.clients.jedis.HostAndPort; -import redis.clients.jedis.JedisCluster; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Created by lorne on 2017/10/31. - */ -@ConditionalOnClass({JedisCluster.class}) -@EnableConfigurationProperties(RedisProperties.class) -public class JedisClusterConfig { - - @Autowired - private RedisProperties redisProperties; - - @Bean - public JedisCluster jedisClusterFactory() { - String[] serverArray = redisProperties.getNodes().split(","); - Set nodes = new HashSet(); - for (String ipPort: serverArray) { - String[] ipPortPair = ipPort.split(":"); - nodes.add(new HostAndPort(ipPortPair[0].trim(),Integer.valueOf(ipPortPair[1].trim()))); - } - return new JedisCluster(nodes, redisProperties.getCommandTimeout()); - } - - @Bean - public RedisTemplate redisTemplateFactory(){ - RedisTemplate redisTemplate =new RedisTemplate(); - redisTemplate.setConnectionFactory(jedisConnectionFactory()); - - //指定具体序列化方式 不过这种方式不是很好,一个系统中可能对应值的类型不一样,如果全部使用StringRedisSerializer 序列化 - //会照成其他类型报错,所以还是推荐使用第一种,直接指定泛型的类型,spring 会根据指定类型序列化。 -// redisTemplate.setKeySerializer( new StringRedisSerializer()); -// redisTemplate.setValueSerializer(new StringRedisSerializer()); -// redisTemplate.setHashKeySerializer(new StringRedisSerializer()); -// redisTemplate.setHashValueSerializer(new StringRedisSerializer()); - return redisTemplate; - } - - - /** - * redisCluster配置 - * @return - */ - @Bean - public RedisClusterConfiguration redisClusterConfiguration() { - Map source = new HashMap(); - source.put("spring.redis.cluster.nodes", redisProperties.getNodes()); - source.put("spring.redis.cluster.timeout", redisProperties.getCommandTimeout()); - return new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration", source)); - } - - - /** - * 其实在JedisConnectionFactory的afterPropertiesSet()方法 中 - * if(cluster !=null) this.cluster =createCluster(); - * 也就是当 - * spring.redis.cluster.nodes 配置好的情况下,就可以实例化 JedisCluster. - * 也就是说,我们使用JedisCluster 的方式只需要在application.properties 配置文件中 - * - * #redis cluster - * spring.redis.cluster.nodes=127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002 - * - * RedisTemplate.afterPropertiesSet() 中查看到最终方法中使用了JedisCluster 对象。 - * 也就是说 redisTemplate依赖jedis ,内部操作的就是jedis,同理内部也操作jedisCluster. - * - * - * @return - */ - @Bean - public JedisConnectionFactory jedisConnectionFactory() { - return new JedisConnectionFactory(redisClusterConfiguration()); - } -} \ No newline at end of file diff --git a/tx-manager/src/main/java/com/codingapi/tm/redis/RedisConfig.java b/tx-manager/src/main/java/com/codingapi/tm/redis/RedisConfig.java deleted file mode 100644 index 81ceae8f6..000000000 --- a/tx-manager/src/main/java/com/codingapi/tm/redis/RedisConfig.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.codingapi.tm.redis; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.StringRedisTemplate; -import redis.clients.jedis.JedisPoolConfig; - -/** - * Created by lorne on 2017/7/5. - */ - -@EnableAutoConfiguration -public class RedisConfig { - - private static Logger logger = LoggerFactory.getLogger(RedisConfig.class); - - @Bean - @ConfigurationProperties(prefix = "spring.redis") - public JedisPoolConfig getRedisConfig() { - JedisPoolConfig config = new JedisPoolConfig(); - return config; - } - - @Bean - @ConfigurationProperties(prefix = "spring.redis") - public JedisConnectionFactory getConnectionFactory() { - JedisConnectionFactory factory = new JedisConnectionFactory(); - JedisPoolConfig config = getRedisConfig(); - factory.setPoolConfig(config); - logger.info("JedisConnectionFactory bean init success."); - return factory; - } - - - @Bean - public RedisTemplate getRedisTemplate() { - return new StringRedisTemplate(getConnectionFactory()); - } -} diff --git a/tx-manager/src/main/java/com/codingapi/tm/redis/RedisProperties.java b/tx-manager/src/main/java/com/codingapi/tm/redis/RedisProperties.java deleted file mode 100644 index bd1815c95..000000000 --- a/tx-manager/src/main/java/com/codingapi/tm/redis/RedisProperties.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.codingapi.tm.redis; - - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -/** - * Created by lorne on 2017/10/31. - */ - -@Component -@ConfigurationProperties(prefix = "spring.redis.cluster") -public class RedisProperties { - - - private String nodes; - - private Integer commandTimeout; - - public String getNodes() { - return nodes; - } - - public void setNodes(String nodes) { - this.nodes = nodes; - } - - public Integer getCommandTimeout() { - return commandTimeout; - } - - public void setCommandTimeout(Integer commandTimeout) { - this.commandTimeout = commandTimeout; - } -} diff --git a/tx-manager/src/main/java/com/codingapi/tm/redis/configuration/RedisTemplateConfig.java b/tx-manager/src/main/java/com/codingapi/tm/redis/configuration/RedisTemplateConfig.java new file mode 100644 index 000000000..e1322625f --- /dev/null +++ b/tx-manager/src/main/java/com/codingapi/tm/redis/configuration/RedisTemplateConfig.java @@ -0,0 +1,52 @@ +package com.codingapi.tm.redis.configuration; + +import com.alibaba.fastjson.parser.ParserConfig; +import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.stereotype.Component; + +/** + * @author yizhishhang + * @description 使用一个Transfer类间接注入RedisConnectionFactory + * Created on 2018/4/18 0018 10:21 + */ +@Component +public class RedisTemplateConfig +{ + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) + { + logger.info("redis...初始化"); + + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + + FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); + + ParserConfig.getGlobalInstance().addAccept("com.codingapi.tm."); + + /** + * 设置值(value)的序列化采用FastJsonRedisSerializer。 + */ + redisTemplate.setValueSerializer(fastJsonRedisSerializer); + redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); + + + /** + * 设置键(key)的序列化采用StringRedisSerializer + */ + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + + redisTemplate.afterPropertiesSet(); + + return redisTemplate; + } +} diff --git a/tx-manager/src/main/java/com/codingapi/tm/redis/service/impl/RedisServerServiceImpl.java b/tx-manager/src/main/java/com/codingapi/tm/redis/service/impl/RedisServerServiceImpl.java index 2b28ff360..c004dbd28 100644 --- a/tx-manager/src/main/java/com/codingapi/tm/redis/service/impl/RedisServerServiceImpl.java +++ b/tx-manager/src/main/java/com/codingapi/tm/redis/service/impl/RedisServerServiceImpl.java @@ -30,6 +30,7 @@ public class RedisServerServiceImpl implements RedisServerService{ private ConfigReader configReader; + @Override public String loadNotifyJson() { Set keys = redisTemplate.keys(configReader.getKeyPrefixCompensate()+"*"); ValueOperations value = redisTemplate.opsForValue(); diff --git a/tx-manager/src/main/resources/application.properties b/tx-manager/src/main/resources/application.properties index 0fd633269..e54d24f90 100644 --- a/tx-manager/src/main/resources/application.properties +++ b/tx-manager/src/main/resources/application.properties @@ -1,9 +1,9 @@ #######################################txmanager-start################################################# -#服务端口 -server.port=8899 +#\u670D\u52A1\u7AEF\u53E3 +server.port=9010 -#tx-manager不得修改 +#tx-manager\u4E0D\u5F97\u4FEE\u6539 spring.application.name=tx-manager spring.mvc.static-path-pattern=/** @@ -11,86 +11,84 @@ spring.resources.static-locations=classpath:/static/ #######################################txmanager-end################################################# -#zookeeper地址 +#zookeeper\u5730\u5740 #spring.cloud.zookeeper.connect-string=127.0.0.1:2181 #spring.cloud.zookeeper.discovery.preferIpAddress = true -#eureka 地址 -eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/ +#eureka \u5730\u5740 +eureka.client.service-url.defaultZone=http://127.0.0.1:1111/eureka/ eureka.instance.prefer-ip-address=true #######################################redis-start################################################# -#redis 配置文件,根据情况选择集群或者单机模式 +#redis \u914D\u7F6E\u6587\u4EF6\uFF0C\u6839\u636E\u60C5\u51B5\u9009\u62E9\u96C6\u7FA4\u6216\u8005\u5355\u673A\u6A21\u5F0F -##redis 集群环境配置 +##redis \u96C6\u7FA4\u73AF\u5883\u914D\u7F6E ##redis cluster -#spring.redis.cluster.nodes=127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003 +#spring.redis.cluster.nodes=127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382 #spring.redis.cluster.commandTimeout=5000 -##redis 单点环境配置 +##redis \u5355\u70B9\u73AF\u5883\u914D\u7F6E #redis -#redis主机地址 -spring.redis.host=127.0.0.1 -#redis主机端口 -spring.redis.port=6379 -#redis链接密码 -spring.redis.password= -spring.redis.pool.maxActive=10 -spring.redis.pool.maxWait=-1 -spring.redis.pool.maxIdle=5 -spring.redis.pool.minIdle=0 -spring.redis.timeout=0 +#redis\u4E3B\u673A\u5730\u5740 +#spring.redis.host=localhost +#redis\u4E3B\u673A\u7AEF\u53E3 +#spring.redis.port=6379 +#redis\u94FE\u63A5\u5BC6\u7801 +#spring.redis.password= +spring.redis.jedis.pool.max-idle=8 +spring.redis.jedis.pool.max-wait= +spring.redis.jedis.pool.min-idle=0 #####################################redis-end################################################### #######################################LCN-start################################################# -#业务模块与TxManager之间通讯的最大等待时间(单位:秒) -#通讯时间是指:发起方与响应方之间完成一次的通讯时间。 -#该字段代表的是Tx-Client模块与TxManager模块之间的最大通讯时间,超过该时间未响应本次请求失败。 +#\u4E1A\u52A1\u6A21\u5757\u4E0ETxManager\u4E4B\u95F4\u901A\u8BAF\u7684\u6700\u5927\u7B49\u5F85\u65F6\u95F4\uFF08\u5355\u4F4D\uFF1A\u79D2\uFF09 +#\u901A\u8BAF\u65F6\u95F4\u662F\u6307\uFF1A\u53D1\u8D77\u65B9\u4E0E\u54CD\u5E94\u65B9\u4E4B\u95F4\u5B8C\u6210\u4E00\u6B21\u7684\u901A\u8BAF\u65F6\u95F4\u3002 +#\u8BE5\u5B57\u6BB5\u4EE3\u8868\u7684\u662FTx-Client\u6A21\u5757\u4E0ETxManager\u6A21\u5757\u4E4B\u95F4\u7684\u6700\u5927\u901A\u8BAF\u65F6\u95F4\uFF0C\u8D85\u8FC7\u8BE5\u65F6\u95F4\u672A\u54CD\u5E94\u672C\u6B21\u8BF7\u6C42\u5931\u8D25\u3002 tm.transaction.netty.delaytime = 5 -#业务模块与TxManager之间通讯的心跳时间(单位:秒) +#\u4E1A\u52A1\u6A21\u5757\u4E0ETxManager\u4E4B\u95F4\u901A\u8BAF\u7684\u5FC3\u8DF3\u65F6\u95F4\uFF08\u5355\u4F4D\uFF1A\u79D2\uFF09 tm.transaction.netty.hearttime = 15 -#存储到redis下的数据最大保存时间(单位:秒) -#该字段仅代表的事务模块数据的最大保存时间,补偿数据会永久保存。 +#\u5B58\u50A8\u5230redis\u4E0B\u7684\u6570\u636E\u6700\u5927\u4FDD\u5B58\u65F6\u95F4\uFF08\u5355\u4F4D\uFF1A\u79D2\uFF09 +#\u8BE5\u5B57\u6BB5\u4EC5\u4EE3\u8868\u7684\u4E8B\u52A1\u6A21\u5757\u6570\u636E\u7684\u6700\u5927\u4FDD\u5B58\u65F6\u95F4\uFF0C\u8865\u507F\u6570\u636E\u4F1A\u6C38\u4E45\u4FDD\u5B58\u3002 tm.redis.savemaxtime=30 -#socket server Socket对外服务端口 -#TxManager的LCN协议的端口 +#socket server Socket\u5BF9\u5916\u670D\u52A1\u7AEF\u53E3 +#TxManager\u7684LCN\u534F\u8BAE\u7684\u7AEF\u53E3 tm.socket.port=9999 -#最大socket连接数 -#TxManager最大允许的建立连接数量 +#\u6700\u5927socket\u8FDE\u63A5\u6570 +#TxManager\u6700\u5927\u5141\u8BB8\u7684\u5EFA\u7ACB\u8FDE\u63A5\u6570\u91CF tm.socket.maxconnection=100 -#事务自动补偿 (true:开启,false:关闭) -# 说明: -# 开启自动补偿以后,必须要配置 tm.compensate.notifyUrl 地址,仅当tm.compensate.notifyUrl 在请求补偿确认时返回success或者SUCCESS时,才会执行自动补偿,否则不会自动补偿。 -# 关闭自动补偿,当出现数据时也会 tm.compensate.notifyUrl 地址。 -# 当tm.compensate.notifyUrl 无效时,不影响TxManager运行,仅会影响自动补偿。 +#\u4E8B\u52A1\u81EA\u52A8\u8865\u507F (true:\u5F00\u542F\uFF0Cfalse:\u5173\u95ED) +# \u8BF4\u660E\uFF1A +# \u5F00\u542F\u81EA\u52A8\u8865\u507F\u4EE5\u540E\uFF0C\u5FC5\u987B\u8981\u914D\u7F6E tm.compensate.notifyUrl \u5730\u5740\uFF0C\u4EC5\u5F53tm.compensate.notifyUrl \u5728\u8BF7\u6C42\u8865\u507F\u786E\u8BA4\u65F6\u8FD4\u56DEsuccess\u6216\u8005SUCCESS\u65F6\uFF0C\u624D\u4F1A\u6267\u884C\u81EA\u52A8\u8865\u507F\uFF0C\u5426\u5219\u4E0D\u4F1A\u81EA\u52A8\u8865\u507F\u3002 +# \u5173\u95ED\u81EA\u52A8\u8865\u507F\uFF0C\u5F53\u51FA\u73B0\u6570\u636E\u65F6\u4E5F\u4F1A tm.compensate.notifyUrl \u5730\u5740\u3002 +# \u5F53tm.compensate.notifyUrl \u65E0\u6548\u65F6\uFF0C\u4E0D\u5F71\u54CDTxManager\u8FD0\u884C\uFF0C\u4EC5\u4F1A\u5F71\u54CD\u81EA\u52A8\u8865\u507F\u3002 tm.compensate.auto=false -#事务补偿记录回调地址(rest api 地址,post json格式) -#请求补偿是在开启自动补偿时才会请求的地址。请求分为两种:1.补偿决策,2.补偿结果通知,可通过通过action参数区分compensate为补偿请求、notify为补偿通知。 -#*注意当请求补偿决策时,需要补偿服务返回"SUCCESS"字符串以后才可以执行自动补偿。 -#请求补偿结果通知则只需要接受通知即可。 -#请求补偿的样例数据格式: +#\u4E8B\u52A1\u8865\u507F\u8BB0\u5F55\u56DE\u8C03\u5730\u5740(rest api \u5730\u5740\uFF0Cpost json\u683C\u5F0F) +#\u8BF7\u6C42\u8865\u507F\u662F\u5728\u5F00\u542F\u81EA\u52A8\u8865\u507F\u65F6\u624D\u4F1A\u8BF7\u6C42\u7684\u5730\u5740\u3002\u8BF7\u6C42\u5206\u4E3A\u4E24\u79CD\uFF1A1.\u8865\u507F\u51B3\u7B56\uFF0C2.\u8865\u507F\u7ED3\u679C\u901A\u77E5\uFF0C\u53EF\u901A\u8FC7\u901A\u8FC7action\u53C2\u6570\u533A\u5206compensate\u4E3A\u8865\u507F\u8BF7\u6C42\u3001notify\u4E3A\u8865\u507F\u901A\u77E5\u3002 +#*\u6CE8\u610F\u5F53\u8BF7\u6C42\u8865\u507F\u51B3\u7B56\u65F6\uFF0C\u9700\u8981\u8865\u507F\u670D\u52A1\u8FD4\u56DE"SUCCESS"\u5B57\u7B26\u4E32\u4EE5\u540E\u624D\u53EF\u4EE5\u6267\u884C\u81EA\u52A8\u8865\u507F\u3002 +#\u8BF7\u6C42\u8865\u507F\u7ED3\u679C\u901A\u77E5\u5219\u53EA\u9700\u8981\u63A5\u53D7\u901A\u77E5\u5373\u53EF\u3002 +#\u8BF7\u6C42\u8865\u507F\u7684\u6837\u4F8B\u6570\u636E\u683C\u5F0F: #{"groupId":"TtQxTwJP","action":"compensate","json":"{\"address\":\"133.133.5.100:8081\",\"className\":\"com.example.demo.service.impl.DemoServiceImpl\",\"currentTime\":1511356150413,\"data\":\"C5IBLWNvbS5leGFtcGxlLmRlbW8uc2VydmljZS5pbXBsLkRlbW9TZXJ2aWNlSW1wbAwSBHNhdmUbehBqYXZhLmxhbmcuT2JqZWN0GAAQARwjeg9qYXZhLmxhbmcuQ2xhc3MYABABJCo/cHVibGljIGludCBjb20uZXhhbXBsZS5kZW1vLnNlcnZpY2UuaW1wbC5EZW1vU2VydmljZUltcGwuc2F2ZSgp\",\"groupId\":\"TtQxTwJP\",\"methodStr\":\"public int com.example.demo.service.impl.DemoServiceImpl.save()\",\"model\":\"demo1\",\"state\":0,\"time\":36,\"txGroup\":{\"groupId\":\"TtQxTwJP\",\"hasOver\":1,\"isCompensate\":0,\"list\":[{\"address\":\"133.133.5.100:8899\",\"isCompensate\":0,\"isGroup\":0,\"kid\":\"wnlEJoSl\",\"methodStr\":\"public int com.example.demo.service.impl.DemoServiceImpl.save()\",\"model\":\"demo2\",\"modelIpAddress\":\"133.133.5.100:8082\",\"channelAddress\":\"/133.133.5.100:64153\",\"notify\":1,\"uniqueKey\":\"bc13881a5d2ab2ace89ae5d34d608447\"}],\"nowTime\":0,\"startTime\":1511356150379,\"state\":1},\"uniqueKey\":\"be6eea31e382f1f0878d07cef319e4d7\"}"} -#请求补偿的返回数据样例数据格式: +#\u8BF7\u6C42\u8865\u507F\u7684\u8FD4\u56DE\u6570\u636E\u6837\u4F8B\u6570\u636E\u683C\u5F0F: #SUCCESS -#请求补偿结果通知的样例数据格式: +#\u8BF7\u6C42\u8865\u507F\u7ED3\u679C\u901A\u77E5\u7684\u6837\u4F8B\u6570\u636E\u683C\u5F0F: #{"resState":true,"groupId":"TtQxTwJP","action":"notify"} -tm.compensate.notifyUrl=http://ip:port/path +tm.compensate.notifyUrl=http://yizhishang:8081/demo/notifyResult -#补偿失败,再次尝试间隔(秒),最大尝试次数3次,当超过3次即为补偿失败,失败的数据依旧还会存在TxManager下。 +#\u8865\u507F\u5931\u8D25\uFF0C\u518D\u6B21\u5C1D\u8BD5\u95F4\u9694\uFF08\u79D2\uFF09\uFF0C\u6700\u5927\u5C1D\u8BD5\u6B21\u65703\u6B21\uFF0C\u5F53\u8D85\u8FC73\u6B21\u5373\u4E3A\u8865\u507F\u5931\u8D25,\u5931\u8D25\u7684\u6570\u636E\u4F9D\u65E7\u8FD8\u4F1A\u5B58\u5728TxManager\u4E0B\u3002 tm.compensate.tryTime=30 -#各事务模块自动补偿的时间上限(毫秒) -#指的是模块执行自动超时的最大时间,该最大时间若过段会导致事务机制异常,该时间必须要模块之间通讯的最大超过时间。 -#例如,若模块A与模块B,请求超时的最大时间是5秒,则建议改时间至少大于5秒。 +#\u5404\u4E8B\u52A1\u6A21\u5757\u81EA\u52A8\u8865\u507F\u7684\u65F6\u95F4\u4E0A\u9650(\u6BEB\u79D2) +#\u6307\u7684\u662F\u6A21\u5757\u6267\u884C\u81EA\u52A8\u8D85\u65F6\u7684\u6700\u5927\u65F6\u95F4\uFF0C\u8BE5\u6700\u5927\u65F6\u95F4\u82E5\u8FC7\u6BB5\u4F1A\u5BFC\u81F4\u4E8B\u52A1\u673A\u5236\u5F02\u5E38\uFF0C\u8BE5\u65F6\u95F4\u5FC5\u987B\u8981\u6A21\u5757\u4E4B\u95F4\u901A\u8BAF\u7684\u6700\u5927\u8D85\u8FC7\u65F6\u95F4\u3002 +#\u4F8B\u5982\uFF0C\u82E5\u6A21\u5757A\u4E0E\u6A21\u5757B\uFF0C\u8BF7\u6C42\u8D85\u65F6\u7684\u6700\u5927\u65F6\u95F4\u662F5\u79D2\uFF0C\u5219\u5EFA\u8BAE\u6539\u65F6\u95F4\u81F3\u5C11\u5927\u4E8E5\u79D2\u3002 tm.compensate.maxWaitTime=5000 #######################################LCN-end################################################# diff --git a/tx-manager/src/main/resources/banner.txt b/tx-manager/src/main/resources/banner.txt new file mode 100644 index 000000000..c4b440e01 --- /dev/null +++ b/tx-manager/src/main/resources/banner.txt @@ -0,0 +1,13 @@ + + >=> >=> >==> >=> + >=> >=> >=> >> >=> >=> + >=> >=> >=> >=> >=> + >=> >=> >=> >=>>=> + >=> >=> >=> > >=> + >=> >=> >=> >=> >>=> + >=======> >===> >=> >=> + + LCN-TxManager version:4.2.0 + + + diff --git a/tx-manager/src/main/resources/static/index.html b/tx-manager/src/main/resources/static/index.html index 4e8e32149..baa9c2d89 100644 --- a/tx-manager/src/main/resources/static/index.html +++ b/tx-manager/src/main/resources/static/index.html @@ -1,8 +1,8 @@ - + - TxManager v4.1.0 + TxManager v4.2.0 @@ -24,7 +24,7 @@
-

TxManagerV4.1.0 服务已启动

+

TxManagerV4.2.0 服务已启动

diff --git a/tx-plugins-db/pom.xml b/tx-plugins-db/pom.xml index 747907e61..51c8bd738 100644 --- a/tx-plugins-db/pom.xml +++ b/tx-plugins-db/pom.xml @@ -6,12 +6,10 @@ com.codingapi tx-lcn - 4.1.0 + 4.2.0 - com.codingapitx-plugins-db - ${lcn.last.version}tx-plugins-dbhttps://github.com/codingapi/tx-lcn @@ -25,7 +23,16 @@ tx-client${lcn.last.version} + + com.alibaba + druid + 1.0.19 + + + com.fasterxml.jackson.core + jackson-databind + 2.8.10 + - diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/AbstractTransactionThread.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/AbstractTransactionThread.java index 75f10f82a..697f8cf5f 100644 --- a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/AbstractTransactionThread.java +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/AbstractTransactionThread.java @@ -1,5 +1,6 @@ package com.codingapi.tx.datasource.relational; +import com.codingapi.tx.aop.bean.TxTransactionLocal; import com.codingapi.tx.framework.thread.HookRunnable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +25,7 @@ protected void startRunnable(){ Runnable runnable = new HookRunnable() { @Override public void run0() { + TxTransactionLocal.setCurrent(null); try { transaction(); } catch (Exception e) { diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNDBConnection.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNDBConnection.java index 8ded34e57..b73469bb8 100644 --- a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNDBConnection.java +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNDBConnection.java @@ -22,7 +22,6 @@ /** * create by lorne on 2017/7/29 */ - public class LCNDBConnection extends AbstractTransactionThread implements LCNConnection { @@ -88,6 +87,7 @@ public void rollback() throws SQLException { isClose.set(true); } + @Override protected void closeConnection() throws SQLException { runnable.close(this); connection.close(); @@ -138,6 +138,7 @@ protected void rollbackConnection() throws SQLException { connection.rollback(); } + @Override public void transaction() throws SQLException { if (waitTask == null) { rollbackConnection(); @@ -152,12 +153,12 @@ public void transaction() throws SQLException { timer.schedule(new TimerTask() { @Override public void run() { - System.out.println("auto execute ,groupId:" + getGroupId()); + logger.info("auto execute ,groupId:" + getGroupId()); dataSourceService.schedule(getGroupId(), waitTask); } }, maxOutTime); - System.out.println("transaction is wait for TxManager notify, groupId : " + getGroupId()); + logger.info("transaction is wait for TxManager notify, groupId {}", getGroupId()); waitTask.awaitTask(); @@ -173,10 +174,10 @@ public void run() { rollbackConnection(); } - System.out.println("lcn transaction over, res -> groupId:"+getGroupId()+" and state is "+(rs==1?"commit":"rollback")); + logger.info("lcn transaction over, res -> groupId:"+getGroupId()+" and state is "+(rs==1?"commit":"rollback")); }catch (SQLException e){ - System.out.println("lcn transaction over,but connection is closed, res -> groupId:"+getGroupId()); + logger.info("lcn transaction over,but connection is closed, res -> groupId:"+getGroupId()); waitTask.setState(TaskState.connectionError.getCode()); } @@ -186,10 +187,12 @@ public void run() { } + @Override public String getGroupId() { return groupId; } + @Override public TxTask getWaitTask() { return waitTask; } diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNStartConnection.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNStartConnection.java index 861e43d89..c3408bf5f 100644 --- a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNStartConnection.java +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNStartConnection.java @@ -44,8 +44,6 @@ public LCNStartConnection(Connection connection, ICallClose subNow this.connection = connection; this.subNowCount = subNowCount; - - if(TxCompensateLocal.current()!=null){ isCompensate = true; logger.info("transaction is compensate-connection."); @@ -140,6 +138,7 @@ protected void rollbackConnection() throws SQLException { connection.rollback(); } + @Override public void transaction()throws SQLException{ if (waitTask == null) { rollbackConnection(); @@ -147,7 +146,7 @@ public void transaction()throws SQLException{ return; } - System.out.println(" start transaction is wait for TxManager notify, groupId : " + getGroupId()); + logger.info(" start transaction is wait for TxManager notify, groupId : " + getGroupId()); waitTask.awaitTask(); @@ -169,7 +168,7 @@ public void transaction()throws SQLException{ } else { rollbackConnection(); } - System.out.println(" lcn start transaction over, res -> groupId:"+getGroupId()+" and state is "+(rs==1?"commit":"rollback")); + logger.info(" lcn start transaction over, res -> groupId:"+getGroupId()+" and state is "+(rs==1?"commit":"rollback")); }catch (SQLException e){ @@ -184,6 +183,7 @@ public void transaction()throws SQLException{ } + @Override protected void closeConnection() throws SQLException{ subNowCount.close(this); diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNTransactionDataSource.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNTransactionDataSource.java index 2bbf22bba..270f9fafc 100644 --- a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNTransactionDataSource.java +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNTransactionDataSource.java @@ -4,8 +4,11 @@ import com.codingapi.tx.aop.bean.TxTransactionLocal; import com.codingapi.tx.datasource.AbstractResourceProxy; import com.codingapi.tx.datasource.ILCNConnection; +import com.codingapi.tx.datasource.relational.txc.TxcDBConnection; +import com.codingapi.tx.datasource.relational.txc.rollback.TxcRollbackService; import org.aspectj.lang.ProceedingJoinPoint; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.sql.Connection; @@ -23,7 +26,8 @@ public class LCNTransactionDataSource extends AbstractResourceProxy" + txTransactionLocal.getGroupId()); + return txc; + } + @Override protected void initDbType() { diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/AbstractTxcConnection.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/AbstractTxcConnection.java new file mode 100644 index 000000000..1d5cb4202 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/AbstractTxcConnection.java @@ -0,0 +1,415 @@ +package com.codingapi.tx.datasource.relational.txc; + +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.codingapi.tx.datasource.relational.AbstractTransactionThread; +import com.codingapi.tx.datasource.relational.LCNConnection; +import com.codingapi.tx.datasource.relational.txc.parser.TxcRuntimeContext; +import com.codingapi.tx.datasource.relational.txc.rollback.TxcRollbackService; +import com.codingapi.tx.datasource.service.DataSourceService; +import com.codingapi.tx.framework.task.TaskGroup; +import com.codingapi.tx.framework.task.TaskGroupManager; +import com.codingapi.tx.framework.task.TxTask; +import com.codingapi.tx.framework.thread.HookRunnable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + + +/** + * created at 20171116 + * @author caisirius + */ +public abstract class AbstractTxcConnection extends AbstractTransactionThread + implements LCNConnection,TxcRuntimeContextService { + + private static final Logger logger = LoggerFactory.getLogger(AbstractTxcConnection.class); + + private boolean readOnly = false; + + volatile int state = 1; + + private Connection connection; + + DataSourceService dataSourceService; + + TxTask waitTask; + + int maxOutTime; + + String groupId; + + TxcRuntimeContext txcRuntimeContext; + + TxcRollbackService txcRollbackService; + + @Override + public TxcRuntimeContext getTxcRuntimeContext() { + if (txcRuntimeContext == null) { + this.txcRuntimeContext = new TxcRuntimeContext(); + txcRuntimeContext.setGroupId(groupId); + txcRuntimeContext.setBranchId(waitTask.getKey()); + + } + return txcRuntimeContext; + } + + @Override + public String getGroupId() { + return groupId; + } + + @Override + public TxTask getWaitTask() { + return waitTask; + } + + + + public AbstractTxcConnection(Connection connection, TxTransactionLocal transactionLocal, + DataSourceService dataSourceService, + TxcRollbackService txcRollbackService) { + readOnly = transactionLocal.isReadOnly(); + this.connection = connection; + this.dataSourceService = dataSourceService; + this.txcRollbackService = txcRollbackService; + + groupId = transactionLocal.getGroupId(); + maxOutTime = transactionLocal.getMaxTimeOut(); + + TaskGroup taskGroup; + if (transactionLocal.getKid() == null) { + logger.info("this is txc start-connection"); + taskGroup = TaskGroupManager.getInstance().createTask(groupId, transactionLocal.getType()); + } else { + taskGroup = TaskGroupManager.getInstance().createTask(transactionLocal.getKid(), transactionLocal.getType()); + } + waitTask = taskGroup.getCurrent(); + } + + // commit() 里要做工作 + @Override + public void commit() throws SQLException { + logger.info("commit"); + + connection.commit(); + + state = 1; + } + + @Override + public void rollback() throws SQLException { + connection.rollback(); + state = 0; + } + + @Override + public void close() throws SQLException { + connection.close(); + // 只有提交才需要 开启线程等待 + if (readOnly || state == 0) { + closeConnection(); + return; + } + startRunnable(); + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + connection.setAutoCommit(false); + } + + + // TODO Statement CallableStatement 也应该自定义!! + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + return connection.prepareCall(sql); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return connection.prepareCall(sql, resultSetType, resultSetConcurrency); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + /*****default*******/ + @Override + public String nativeSQL(String sql) throws SQLException { + return connection.nativeSQL(sql); + } + + + @Override + public boolean getAutoCommit() throws SQLException { + return connection.getAutoCommit(); + } + + + @Override + public boolean isClosed() throws SQLException { + return connection.isClosed(); + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return connection.getMetaData(); + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + if(readOnly) { + this.readOnly = readOnly; + logger.debug("setReadOnly - >" + readOnly); + connection.setReadOnly(readOnly); + TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); + txTransactionLocal.setReadOnly(readOnly); + } + } + + @Override + public boolean isReadOnly() throws SQLException { + return connection.isReadOnly(); + } + + @Override + public void setCatalog(String catalog) throws SQLException { + connection.setCatalog(catalog); + } + + @Override + public String getCatalog() throws SQLException { + return connection.getCatalog(); + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + connection.setTransactionIsolation(level); + } + + @Override + public int getTransactionIsolation() throws SQLException { + return connection.getTransactionIsolation(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return connection.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + connection.clearWarnings(); + } + + @Override + public Map> getTypeMap() throws SQLException { + return connection.getTypeMap(); + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + connection.setTypeMap(map); + } + + @Override + public void setHoldability(int holdability) throws SQLException { + connection.setHoldability(holdability); + } + + @Override + public int getHoldability() throws SQLException { + return connection.getHoldability(); + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return connection.setSavepoint(); + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + return connection.setSavepoint(name); + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + connection.rollback(savepoint); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + connection.releaseSavepoint(savepoint); + } + + @Override + public Clob createClob() throws SQLException { + return connection.createClob(); + } + + @Override + public Blob createBlob() throws SQLException { + return connection.createBlob(); + } + + @Override + public NClob createNClob() throws SQLException { + return connection.createNClob(); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return connection.createSQLXML(); + } + + @Override + public boolean isValid(int timeout) throws SQLException { + return connection.isValid(timeout); + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + connection.setClientInfo(name, value); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + connection.setClientInfo(properties); + } + + @Override + public String getClientInfo(String name) throws SQLException { + return connection.getClientInfo(name); + } + + @Override + public Properties getClientInfo() throws SQLException { + return connection.getClientInfo(); + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return connection.createArrayOf(typeName, elements); + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return connection.createStruct(typeName, attributes); + } + + @Override + public void setSchema(String schema) throws SQLException { + connection.setSchema(schema); + } + + @Override + public String getSchema() throws SQLException { + return connection.getSchema(); + } + + @Override + public void abort(Executor executor) throws SQLException { + connection.abort(executor); + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + connection.setNetworkTimeout(executor, milliseconds); + } + + @Override + public int getNetworkTimeout() throws SQLException { + return connection.getNetworkTimeout(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return connection.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return connection.isWrapperFor(iface); + } + + /***** wrap *******/ + + @Override + public Statement createStatement() throws SQLException { + Statement statement = connection.createStatement(); + return new TxcStatement(statement , this); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + Statement statement = connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + return new TxcStatement(statement , this); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + Statement statement = connection.createStatement(resultSetType, resultSetConcurrency); + return new TxcStatement(statement , this); + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + // 这里返回自定义的 PreparedStatement + PreparedStatement localPreparedStatement = connection.prepareStatement(sql); + return new TxcPreparedStatement(localPreparedStatement, this, sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws + SQLException { + PreparedStatement localPreparedStatement = connection.prepareStatement(sql, resultSetType, resultSetConcurrency); + + return new TxcPreparedStatement(localPreparedStatement, this, sql); + + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + PreparedStatement localPreparedStatement = connection.prepareStatement(sql, autoGeneratedKeys); + return new TxcPreparedStatement(localPreparedStatement, this, sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + PreparedStatement localPreparedStatement = connection.prepareStatement(sql, columnIndexes); + return new TxcPreparedStatement(localPreparedStatement, this, sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + PreparedStatement localPreparedStatement = connection.prepareStatement(sql, columnNames); + return new TxcPreparedStatement(localPreparedStatement, this, sql); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + PreparedStatement localPreparedStatement = connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + return new TxcPreparedStatement(localPreparedStatement, this, sql); + } + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/ColumnInfo.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/ColumnInfo.java new file mode 100644 index 000000000..8b5e66eb7 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/ColumnInfo.java @@ -0,0 +1,78 @@ +package com.codingapi.tx.datasource.relational.txc; + +/** + * [类描述] + * + * @author caican + */ +public class ColumnInfo { + private String tableName; + private String columnName; + private int type; + /** + * -1 : no key + * 0: PRI 主键索引 + * 1: UNI 唯一索引 + * 2: MUL 普通索引(联合索引) + */ + private int keyType; + private boolean isAllowNull; + private String defaultValue; + private String extra; + + public int getKeyType() { + return keyType; + } + + public void setKeyType(int keyType) { + this.keyType = keyType; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getColumnName() { + return columnName; + } + + public void setColumnName(String columnName) { + this.columnName = columnName; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public boolean isAllowNull() { + return isAllowNull; + } + + public void setAllowNull(boolean allowNull) { + isAllowNull = allowNull; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getExtra() { + return extra; + } + + public void setExtra(String extra) { + this.extra = extra; + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/ITxcStatement.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/ITxcStatement.java new file mode 100644 index 000000000..8e4f96b33 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/ITxcStatement.java @@ -0,0 +1,20 @@ +package com.codingapi.tx.datasource.relational.txc; + +import java.sql.Statement; + +/** + * [类描述] + * + * @author caican + */ +public interface ITxcStatement extends Statement { + /** 返回执行的SQL + * @return SQL语句 + */ + String getSql(); + + Statement getStatement(); + + AbstractTxcConnection getTxcDBConnection(); + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/IndexInfo.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/IndexInfo.java new file mode 100644 index 000000000..036767a41 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/IndexInfo.java @@ -0,0 +1,9 @@ +package com.codingapi.tx.datasource.relational.txc; + +/** + * [类描述] + * + * @author caican + */ +public class IndexInfo { +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TableMetaInfo.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TableMetaInfo.java new file mode 100644 index 000000000..ae68d640d --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TableMetaInfo.java @@ -0,0 +1,118 @@ +package com.codingapi.tx.datasource.relational.txc; + +import java.util.HashMap; +import java.util.Map; + +/** + * [表描述] + * + * @author caican + */ +public class TableMetaInfo { + /** + * SchemaName (if empty, set to CatalogName) + */ + private String schemaName; + /** + * TableName + */ + private String tableName; + + /** + * 列信息 + */ + private Map columnInfoMap = new HashMap<>(); + /** + * 索引 + */ + private Map indexInfoMap = new HashMap<>(); + + + public TableMetaInfo() { + } + + public ColumnInfo getColumnByName(String name) { + String str = name.toUpperCase(); + ColumnInfo ret = this.columnInfoMap.get(str); + if (ret == null) { + if (name.charAt(0) == '`') { + ret = this.columnInfoMap.get(str.substring(1, name.length() - 1)); + } else { + ret = this.columnInfoMap.get("`" + str + "`"); + } + } + return ret; + } + + + public Map getPrimaryKey() { + HashMap ret = new HashMap<>(); + + for (Map.Entry entry : this.columnInfoMap.entrySet()) { + ColumnInfo columnInfo = entry.getValue(); + if (columnInfo.getKeyType() == 0) { + ret.put(entry.getKey(), columnInfo); + } + } + + if (ret.size() > 1) { + throw new RuntimeException("multi pks not support yet."); + } + return ret; + } + + public String getPrimaryKeyName() { + Map primaryKey = getPrimaryKey(); + + if (primaryKey.entrySet().size() > 1) { + throw new RuntimeException("multi pks not support yet."); + + } + + Map.Entry next = primaryKey.entrySet().iterator().next(); + return next.getKey(); + } + + public String getAutoIncrementPrimaryKey() { + + for (Map.Entry entry : this.columnInfoMap.entrySet()) { + ColumnInfo columnInfo = entry.getValue(); + if (columnInfo.getKeyType() == 0 && columnInfo.getExtra().equals("auto_increment")) { + return entry.getKey(); + } + } + return null; + } + + public String getSchemaName() { + return schemaName; + } + + public void setSchemaName(String schemaName) { + this.schemaName = schemaName; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public Map getColumnInfoMap() { + return columnInfoMap; + } + + public void setColumnInfoMap(Map columnInfoMap) { + this.columnInfoMap = columnInfoMap; + } + + public Map getIndexInfoMap() { + return indexInfoMap; + } + + public void setIndexInfoMap(Map indexInfoMap) { + this.indexInfoMap = indexInfoMap; + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TableMetaUtils.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TableMetaUtils.java new file mode 100644 index 000000000..dde523efd --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TableMetaUtils.java @@ -0,0 +1,143 @@ +package com.codingapi.tx.datasource.relational.txc; + +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.lorne.core.framework.utils.config.ConfigUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +/** + * [类描述] + * + * @author caican + */ +public class TableMetaUtils { + + private static final Logger logger = LoggerFactory.getLogger(TableMetaUtils.class); + + private static final ConcurrentHashMap tableMetaInfoCache = + new ConcurrentHashMap<>(); + //TODO 定期让缓存失效 + + private static String dbType; + + + public static TableMetaInfo getTableMetaInfo(Connection connection, String tableName) { + if (StringUtils.isEmpty(tableName)) { + throw new RuntimeException("TableMeta cannot fetched without tableName"); + } + if (connection == null) { + throw new RuntimeException("TableMeta cannot fetched without Connection"); + } + String databaseName = null; + try { + databaseName = getDbNameFromUrl(connection.getMetaData().getURL()); + } catch (Exception e) { } + if (StringUtils.isEmpty(databaseName)) { + databaseName = "NULL"; + } + TableMetaInfo ret; + String fullTableName = databaseName + "." + tableName; + + // 先从本地缓存拿 + ret = tableMetaInfoCache.get(fullTableName); + if (ret == null) { + try { + logger.info("meta is null, fetch schema of " + tableName); + ret = fetchSchema(connection, tableName); + ret.setTableName(tableName); + ret.setSchemaName(databaseName); + + tableMetaInfoCache.putIfAbsent(fullTableName, ret); + } catch (SQLException e) { + logger.error("tableMeta error", e); + } + } + if (ret == null) { + throw new RuntimeException(String.format("[groupId:%s]get tablemeta failed", + TxTransactionLocal.current().getGroupId())); + } + + return ret; + } + + public static String getDbNameFromUrl (String url) { + if (StringUtils.isEmpty(url)) { + return null; + } + int start = 0; + boolean isThreeFound = true; // 要找到3个 '/' + for (int i=0; i<3;i++) { + int index = url.indexOf('/', start+1); + if (index == -1) { + isThreeFound = false; + break; + } + start = index; + } + if (! isThreeFound) { + return null; + } + + int index = url.indexOf('?', start); + String ret; + if (index == -1) { + ret = url.substring(start +1); + } else { + ret = url.substring(start + 1, index); + } + return ret; + } + + private static TableMetaInfo fetchSchema(Connection connection, String tableName) + throws SQLException { + // TODO 支持 mysql以外的 + + return fetchSchemaMysql(connection, tableName); + } + + private static TableMetaInfo fetchSchemaMysql(Connection connection, String tableName) + throws SQLException { + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery("desc " + tableName); + TableMetaInfo tableMetaInfo = new TableMetaInfo(); + tableMetaInfo.setColumnInfoMap(new HashMap()); + while (resultSet.next()) { + ColumnInfo columnInfo = new ColumnInfo(); + columnInfo.setTableName(tableName); + columnInfo.setColumnName(resultSet.getString("Field")); + boolean isAllowNull = true; + if ("NO".equalsIgnoreCase(resultSet.getString("Null"))) { + isAllowNull = false; + } + columnInfo.setAllowNull(isAllowNull); + String key = resultSet.getString("Key"); + int iKey = -1; + if ("PRI".equalsIgnoreCase(key)) { + iKey = 0; + } else if ("UNI".equalsIgnoreCase(key)) { + iKey = 1; + } else if ("MUL".equalsIgnoreCase(key)) { + iKey = 2; + } + columnInfo.setKeyType(iKey);// 这个最重要 + columnInfo.setExtra(resultSet.getString("Extra")); + + //TODO + // resultSet.getString("Type"); +// columnInfo.setType(); + + tableMetaInfo.getColumnInfoMap().put(columnInfo.getColumnName(), columnInfo); + } + return tableMetaInfo; + } + + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcDBConnection.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcDBConnection.java new file mode 100644 index 000000000..2d039dedd --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcDBConnection.java @@ -0,0 +1,91 @@ +package com.codingapi.tx.datasource.relational.txc; + +import com.alibaba.fastjson.JSON; +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.codingapi.tx.datasource.relational.txc.parser.CommitInfo; +import com.codingapi.tx.datasource.relational.txc.rollback.TxcRollbackService; +import com.codingapi.tx.datasource.service.DataSourceService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +/** + * create by caisirius on 2017/11/28 + */ + +public class TxcDBConnection extends AbstractTxcConnection { + private Logger logger = LoggerFactory.getLogger(TxcDBConnection.class); + + public TxcDBConnection(Connection connection, TxTransactionLocal txTransactionLocal, + DataSourceService dataSourceService, + TxcRollbackService txcRollbackService) { + super(connection, txTransactionLocal, dataSourceService, txcRollbackService); + } + + @Override + public void transaction() { + if (waitTask == null) { + logger.warn("waitTask is null"); + return; + } + + // start 结束就是全部事务的结束表示,考虑start挂掉的情况 + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + logger.warn("txc自动回滚->" + getGroupId()); + dataSourceService.schedule(getGroupId(), waitTask); + } + }, maxOutTime); + + logger.info("waiting for TxManager notify, groupId {}, timeout {}", getGroupId(), maxOutTime); + waitTask.awaitTask(); + + timer.cancel(); + + int rs = waitTask.getState(); + + logger.info("lcn txc transaction over, groupId {} and state is {}",getGroupId(),(rs==1?"commit":"rollback")); + // 提交 + if (rs == 1) { + // do nothing + } else { + try { + rollbackConnection(); + } catch (Exception e) { + logger.error("rollback error", e); + } + } + + waitTask.remove(); + } + + @Override + protected void closeConnection() throws SQLException { + + if (waitTask != null) { + if (!waitTask.isRemove()) { + waitTask.remove(); + } + } + } + + @Override + protected void rollbackConnection() throws SQLException { + logger.info("doTxcRollback kid:{},context:{}", waitTask.getKey() + , JSON.toJSONString(txcRuntimeContext)); + List commitInfos = txcRuntimeContext.getInfo(); + + // 逆序回滚 + for (int i = commitInfos.size() - 1; i >= 0; i--) { + txcRollbackService.rollback(commitInfos.get(i)); + } + } + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcPreparedStatement.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcPreparedStatement.java new file mode 100644 index 000000000..780a460dc --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcPreparedStatement.java @@ -0,0 +1,425 @@ +package com.codingapi.tx.datasource.relational.txc; + +import com.codingapi.tx.datasource.relational.txc.parser.ExecutePaser; +import com.codingapi.tx.datasource.relational.txc.parser.SQLType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +/** + * [类描述] + * + * @author caican + */ +public class TxcPreparedStatement extends TxcStatement implements PreparedStatement { + + private static final Logger logger = LoggerFactory.getLogger(TxcPreparedStatement.class); + + public TxcPreparedStatement(PreparedStatement localPreparedStatement, AbstractTxcConnection abstractTxcConnection, + String sql) { + super(localPreparedStatement, abstractTxcConnection); + this.sql = sql; + } + + /** + * 把这个参数缓存下来 有什么用? + */ + private List paramsList = new ArrayList<>(); + + public int getParameterCount() throws SQLException { + return getParameterMetaData().getParameterCount(); + } + + public synchronized List getParamsList() { + return this.paramsList; + } + + private void addParam(int paramInt, Object paramObject1, Object paramObject2, Object paramObject3) + throws SQLException { + List paramsList = getParamsList(); + paramsList.add(paramObject1); + } + + private void addParam(int paramInt, Object paramObject1, Object paramObject2) throws SQLException { + List paramsList = getParamsList(); + paramsList.add(paramObject1); + } + + private void addParam(int paramInt, Object paramObject) throws SQLException { + List paramsList = getParamsList(); + paramsList.add(paramObject); + } + + @Override + public void addBatch() throws SQLException { + ((PreparedStatement) this.statement).addBatch(); + } + + @Override + public void clearParameters() throws SQLException { + ((PreparedStatement) this.statement).clearParameters(); + } + + @Override + public boolean execute() throws SQLException { + if (isInTxcTransaction()) { + SQLType sqlType = ExecutePaser.parse(this); + boolean execute = ((PreparedStatement) this.statement).execute(); + ExecutePaser.after(this, sqlType); + return execute; + } + + return ((PreparedStatement) this.statement).execute(); + } + + @Override + public ResultSet executeQuery() throws SQLException { + // 查询语句也要包装么? + return ((PreparedStatement) this.statement).executeQuery(); + } + + @Override + public int[] executeBatch() throws SQLException { + if (isInTxcTransaction()) { + ExecutePaser.parse(this); + } + return this.statement.executeBatch(); + } + + @Override + public int executeUpdate() throws SQLException { + if (isInTxcTransaction()) { + SQLType sqlType = ExecutePaser.parse(this); + int num = ((PreparedStatement) this.statement).executeUpdate(); + ExecutePaser.after(this, sqlType); + return num; + } + + return ((PreparedStatement) this.statement).executeUpdate(); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return ((PreparedStatement) this.statement).getMetaData(); + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + return ((PreparedStatement) this.statement).getParameterMetaData(); + } + + @Override + public void setArray(int paramInt, Array paramArray) throws SQLException { + addParam(paramInt, paramArray); + ((PreparedStatement) this.statement).setArray(paramInt, paramArray); + } + + @Override + public void setAsciiStream(int paramInt, InputStream paramInputStream) throws SQLException { + addParam(paramInt, paramInputStream); + ((PreparedStatement) this.statement).setAsciiStream(paramInt, paramInputStream); + } + + @Override + public void setAsciiStream(int paramInt1, InputStream paramInputStream, int paramInt2) throws SQLException { + addParam(paramInt1, paramInputStream, paramInt2); + ((PreparedStatement) this.statement).setAsciiStream(paramInt1, paramInputStream, paramInt2); + } + + @Override + public void setAsciiStream(int paramInt, InputStream paramInputStream, long paramLong) throws SQLException { + addParam(paramInt, paramInputStream, paramLong); + ((PreparedStatement) this.statement).setAsciiStream(paramInt, paramInputStream, paramLong); + } + + @Override + public void setBigDecimal(int paramInt, BigDecimal paramBigDecimal) throws SQLException { + addParam(paramInt, paramBigDecimal); + ((PreparedStatement) this.statement).setBigDecimal(paramInt, paramBigDecimal); + } + + @Override + public void setBinaryStream(int paramInt, InputStream paramInputStream) throws SQLException { + addParam(paramInt, paramInputStream); + ((PreparedStatement) this.statement).setBinaryStream(paramInt, paramInputStream); + } + + @Override + public void setBinaryStream(int paramInt1, InputStream paramInputStream, int paramInt2) throws SQLException { + addParam(paramInt1, paramInputStream, paramInt2); + ((PreparedStatement) this.statement).setBinaryStream(paramInt1, paramInputStream, paramInt2); + } + + @Override + public void setBinaryStream(int paramInt, InputStream paramInputStream, long paramLong) throws SQLException { + addParam(paramInt, paramInputStream, paramLong); + ((PreparedStatement) this.statement).setBinaryStream(paramInt, paramInputStream, paramLong); + } + + @Override + public void setBlob(int paramInt, Blob paramBlob) throws SQLException { + addParam(paramInt, paramBlob); + ((PreparedStatement) this.statement).setBlob(paramInt, paramBlob); + } + + @Override + public void setBlob(int paramInt, InputStream paramInputStream) throws SQLException { + addParam(paramInt, paramInputStream); + ((PreparedStatement) this.statement).setBlob(paramInt, paramInputStream); + } + + @Override + public void setBlob(int paramInt, InputStream paramInputStream, long paramLong) throws SQLException { + addParam(paramInt, paramInputStream, paramLong); + ((PreparedStatement) this.statement).setBlob(paramInt, paramInputStream, paramLong); + } + + @Override + public void setBoolean(int paramInt, boolean paramBoolean) throws SQLException { + addParam(paramInt, paramBoolean); + ((PreparedStatement) this.statement).setBoolean(paramInt, paramBoolean); + } + + @Override + public void setByte(int paramInt, byte paramByte) throws SQLException { + addParam(paramInt, paramByte); + ((PreparedStatement) this.statement).setByte(paramInt, paramByte); + } + + @Override + public void setBytes(int paramInt, byte[] paramArrayOfByte) throws SQLException { + addParam(paramInt, paramArrayOfByte); + ((PreparedStatement) this.statement).setBytes(paramInt, paramArrayOfByte); + } + + @Override + public void setCharacterStream(int paramInt, Reader paramReader) throws SQLException { + addParam(paramInt, paramReader); + ((PreparedStatement) this.statement).setCharacterStream(paramInt, paramReader); + } + + @Override + public void setCharacterStream(int paramInt1, Reader paramReader, int paramInt2) throws SQLException { + addParam(paramInt1, paramReader, paramInt2); + ((PreparedStatement) this.statement).setCharacterStream(paramInt1, paramReader, paramInt2); + } + + @Override + public void setCharacterStream(int paramInt, Reader paramReader, long paramLong) throws SQLException { + addParam(paramInt, paramReader, paramLong); + ((PreparedStatement) this.statement).setCharacterStream(paramInt, paramReader, paramLong); + } + + @Override + public void setClob(int paramInt, Clob paramClob) throws SQLException { + addParam(paramInt, paramClob); + ((PreparedStatement) this.statement).setClob(paramInt, paramClob); + } + + @Override + public void setClob(int paramInt, Reader paramReader) throws SQLException { + addParam(paramInt, paramReader); + ((PreparedStatement) this.statement).setClob(paramInt, paramReader); + } + + @Override + public void setClob(int paramInt, Reader paramReader, long paramLong) throws SQLException { + addParam(paramInt, paramReader, paramLong); + ((PreparedStatement) this.statement).setClob(paramInt, paramReader, paramLong); + } + + @Override + public void setDate(int paramInt, Date paramDate) throws SQLException { + addParam(paramInt, paramDate); + ((PreparedStatement) this.statement).setDate(paramInt, paramDate); + } + + @Override + public void setDate(int paramInt, Date paramDate, Calendar paramCalendar) throws SQLException { + addParam(paramInt, paramDate, paramCalendar); + ((PreparedStatement) this.statement).setDate(paramInt, paramDate, paramCalendar); + } + + @Override + public void setDouble(int paramInt, double paramDouble) throws SQLException { + addParam(paramInt, paramDouble); + ((PreparedStatement) this.statement).setDouble(paramInt, paramDouble); + } + + @Override + public void setFloat(int paramInt, float paramFloat) throws SQLException { + addParam(paramInt, paramFloat); + ((PreparedStatement) this.statement).setFloat(paramInt, paramFloat); + } + + @Override + public void setInt(int paramInt1, int paramInt2) throws SQLException { + addParam(paramInt1, paramInt2); + ((PreparedStatement) this.statement).setInt(paramInt1, paramInt2); + } + + @Override + public void setLong(int paramInt, long paramLong) throws SQLException { + addParam(paramInt, paramLong); + ((PreparedStatement) this.statement).setLong(paramInt, paramLong); + } + + @Override + public void setNCharacterStream(int paramInt, Reader paramReader) throws SQLException { + addParam(paramInt, paramReader); + ((PreparedStatement) this.statement).setNCharacterStream(paramInt, paramReader); + } + + @Override + public void setNCharacterStream(int paramInt, Reader paramReader, long paramLong) throws SQLException { + addParam(paramInt, paramReader, paramLong); + ((PreparedStatement) this.statement).setNCharacterStream(paramInt, paramReader, paramLong); + } + + @Override + public void setNClob(int paramInt, NClob paramNClob) throws SQLException { + addParam(paramInt, paramNClob); + ((PreparedStatement) this.statement).setNClob(paramInt, paramNClob); + } + + @Override + public void setNClob(int paramInt, Reader paramReader) throws SQLException { + addParam(paramInt, paramReader); + ((PreparedStatement) this.statement).setNClob(paramInt, paramReader); + } + + @Override + public void setNClob(int paramInt, Reader paramReader, long paramLong) throws SQLException { + addParam(paramInt, paramReader, paramLong); + ((PreparedStatement) this.statement).setNClob(paramInt, paramReader, paramLong); + } + + @Override + public void setNString(int paramInt, String paramString) throws SQLException { + addParam(paramInt, paramString); + ((PreparedStatement) this.statement).setNString(paramInt, paramString); + } + + @Override + public void setNull(int paramInt1, int paramInt2) throws SQLException { + // addParam(paramInt1, g.addParam()); + ((PreparedStatement) this.statement).setNull(paramInt1, paramInt2); + } + + @Override + public void setNull(int paramInt1, int paramInt2, String paramString) throws SQLException { + // addParam(paramInt1, Integer.valueOf(paramInt2), g.addParam()); + ((PreparedStatement) this.statement).setNull(paramInt1, paramInt2, paramString); + } + + @Override + public void setObject(int paramInt, Object paramObject) throws SQLException { + addParam(paramInt, paramObject); + ((PreparedStatement) this.statement).setObject(paramInt, paramObject); + } + + @Override + public void setObject(int paramInt1, Object paramObject, int paramInt2) throws SQLException { + addParam(paramInt1, paramObject, paramInt2); + ((PreparedStatement) this.statement).setObject(paramInt1, paramObject, paramInt2); + } + + @Override + public void setObject(int paramInt1, Object paramObject, int paramInt2, int paramInt3) throws SQLException { + addParam(paramInt1, paramObject, paramInt2, paramInt3); + ((PreparedStatement) this.statement).setObject(paramInt1, paramObject, paramInt2, paramInt3); + } + + @Override + public void setRef(int paramInt, Ref paramRef) throws SQLException { + addParam(paramInt, paramRef); + ((PreparedStatement) this.statement).setRef(paramInt, paramRef); + } + + @Override + public void setRowId(int paramInt, RowId paramRowId) throws SQLException { + addParam(paramInt, paramRowId); + ((PreparedStatement) this.statement).setRowId(paramInt, paramRowId); + } + + @Override + public void setSQLXML(int paramInt, SQLXML paramSQLXML) throws SQLException { + addParam(paramInt, paramSQLXML); + ((PreparedStatement) this.statement).setSQLXML(paramInt, paramSQLXML); + } + + @Override + public void setShort(int paramInt, short paramShort) throws SQLException { + addParam(paramInt, paramShort); + ((PreparedStatement) this.statement).setShort(paramInt, paramShort); + } + + @Override + public void setString(int paramInt, String paramString) throws SQLException { + addParam(paramInt, paramString); + ((PreparedStatement) this.statement).setString(paramInt, paramString); + } + + @Override + public void setTime(int paramInt, Time paramTime) throws SQLException { + addParam(paramInt, paramTime); + ((PreparedStatement) this.statement).setTime(paramInt, paramTime); + } + + @Override + public void setTime(int paramInt, Time paramTime, Calendar paramCalendar) throws SQLException { + addParam(paramInt, paramTime, paramCalendar); + ((PreparedStatement) this.statement).setTime(paramInt, paramTime); + } + + @Override + public void setTimestamp(int paramInt, Timestamp paramTimestamp) throws SQLException { + // addParam(paramInt, new Timestamp(paramTimestamp.getTime() - paramTimestamp.getTime() % 1000)); + paramTimestamp.setNanos(0); + addParam(paramInt, paramTimestamp); + + ((PreparedStatement) this.statement).setTimestamp(paramInt, paramTimestamp); + } + + @Override + public void setTimestamp(int paramInt, Timestamp paramTimestamp, Calendar paramCalendar) throws SQLException { + paramTimestamp.setNanos(0); + addParam(paramInt, paramTimestamp, paramCalendar); + ((PreparedStatement) this.statement).setTimestamp(paramInt, paramTimestamp, paramCalendar); + } + + @Override + public void setURL(int paramInt, URL paramURL) throws SQLException { + addParam(paramInt, paramURL); + ((PreparedStatement) this.statement).setURL(paramInt, paramURL); + } + + @Override + public void setUnicodeStream(int paramInt1, InputStream paramInputStream, int paramInt2) throws SQLException { + addParam(paramInt1, paramInputStream, paramInt2); + ((PreparedStatement) this.statement).setUnicodeStream(paramInt1, paramInputStream, paramInt2); + } + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcRuntimeContextService.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcRuntimeContextService.java new file mode 100644 index 000000000..65b36193f --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcRuntimeContextService.java @@ -0,0 +1,13 @@ +package com.codingapi.tx.datasource.relational.txc; + + +import com.codingapi.tx.datasource.relational.txc.parser.TxcRuntimeContext; + +/** + * @author jsy. + */ +public interface TxcRuntimeContextService { + + TxcRuntimeContext getTxcRuntimeContext(); + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcSqlExecutor.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcSqlExecutor.java new file mode 100644 index 000000000..5d5f95149 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcSqlExecutor.java @@ -0,0 +1,32 @@ +package com.codingapi.tx.datasource.relational.txc; + + +import com.codingapi.tx.datasource.relational.txc.parser.ResultConvertUtils; +import com.codingapi.tx.datasource.relational.txc.parser.SQLType; +import com.codingapi.tx.datasource.relational.txc.parser.TxcLine; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +/** + * @author jsy. + */ +public class TxcSqlExecutor { + + public static List executeQuery(String sql, Connection connection) throws SQLException { + ResultSet resultSet = connection.prepareStatement(sql).executeQuery(); + return ResultConvertUtils.convertWithPrimary(resultSet, null, SQLType.SELECT); + } + + public static List executeQuery(PreparedStatement preparedStatement) throws SQLException { + ResultSet resultSet = preparedStatement.executeQuery(); + return ResultConvertUtils.convertWithPrimary(resultSet, null, SQLType.SELECT); + } + + public static void execute(String sql, Connection connection) throws SQLException { + connection.prepareStatement(sql).execute(); + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcStatement.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcStatement.java new file mode 100644 index 000000000..2e0829730 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcStatement.java @@ -0,0 +1,338 @@ +package com.codingapi.tx.datasource.relational.txc; + + +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.codingapi.tx.datasource.relational.txc.parser.ExecutePaser; +import com.codingapi.tx.datasource.relational.txc.parser.SQLType; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; + +/** + * [类描述] + * + * @author caican + */ +public class TxcStatement implements ITxcStatement { + + protected Statement statement; + protected AbstractTxcConnection abstractTxcConnection; + + protected String sql; + + public TxcStatement(Statement localStatement, AbstractTxcConnection abstractTxcConnection) { + this.statement = localStatement; + this.abstractTxcConnection = abstractTxcConnection; + } + + @Override + public String getSql() { + return sql; + } + + @Override + public Statement getStatement() { + return statement; + } + + private void setSql(String sql) { + this.sql = sql; + } + + /** + * @return txc 是否开启 + */ + protected boolean isInTxcTransaction(){ + return TxTransactionLocal.isInTxcTransaction(); + } + + @Override + public boolean execute(String paramString) throws SQLException { + setSql(paramString); + if (isInTxcTransaction()) { + SQLType sqlType = ExecutePaser.parse(this); + boolean execute = this.statement.execute(paramString); + ExecutePaser.after(this, sqlType); + return execute; + } + + return this.statement.execute(paramString); + } + + @Override + public boolean execute(String paramString, int paramInt) throws SQLException { + setSql(paramString); + if (isInTxcTransaction()) { + SQLType sqlType = ExecutePaser.parse(this); + boolean execute = this.statement.execute(paramString, paramInt); + ExecutePaser.after(this, sqlType); + return execute; + } + + return this.statement.execute(paramString, paramInt); + } + + @Override + public boolean execute(String paramString, int[] paramArrayOfInt) throws SQLException { + setSql(paramString); + if (isInTxcTransaction()) { + SQLType sqlType = ExecutePaser.parse(this); + boolean execute = this.statement.execute(paramString, paramArrayOfInt); + ExecutePaser.after(this, sqlType); + return execute; + } + + return this.statement.execute(paramString, paramArrayOfInt); + } + + @Override + public boolean execute(String paramString, String[] paramArrayOfString) throws SQLException { + setSql(paramString); + if (isInTxcTransaction()) { + SQLType sqlType = ExecutePaser.parse(this); + boolean execute = this.statement.execute(paramString, paramArrayOfString); + ExecutePaser.after(this, sqlType); + return execute; + } + + return this.statement.execute(paramString, paramArrayOfString); + } + + @Override + public int[] executeBatch() throws SQLException { + if (! isInTxcTransaction()) { + return this.statement.executeBatch(); + } + throw new RuntimeException("Unsupported"); + } + + @Override + public ResultSet executeQuery(String paramString) throws SQLException { + setSql(paramString); + return this.statement.executeQuery(paramString); + } + + @Override + public int executeUpdate(String paramString) throws SQLException { + setSql(paramString); + if (isInTxcTransaction()) { + SQLType sqlType = ExecutePaser.parse(this); + int execute = this.statement.executeUpdate(paramString); + ExecutePaser.after(this, sqlType); + return execute; + } + + return this.statement.executeUpdate(paramString); + } + + @Override + public int executeUpdate(String paramString, int paramInt) throws SQLException { + setSql(paramString); + if (isInTxcTransaction()) { + ExecutePaser.parse(this); + + } + return this.statement.executeUpdate(paramString, paramInt); + } + + @Override + public int executeUpdate(String paramString, int[] paramArrayOfInt) throws SQLException { + setSql(paramString); + if (isInTxcTransaction()) { + ExecutePaser.parse(this); + } + return this.statement.executeUpdate(paramString, paramArrayOfInt); + } + + @Override + public int executeUpdate(String paramString, String[] paramArrayOfString) throws SQLException { + setSql(paramString); + if (isInTxcTransaction()) { + ExecutePaser.parse(this); + + } + return this.statement.executeUpdate(paramString, paramArrayOfString); + } + + // 不支持 + @Override + public void closeOnCompletion() + { + throw new RuntimeException("Unsupported"); + } + + @Override + public boolean isCloseOnCompletion() + { + throw new RuntimeException("Unsupported"); + } + + // 完全不动直接代理 + @Override + public void cancel() throws SQLException { + this.statement.cancel(); + } + + @Override + public void clearWarnings() throws SQLException { + this.statement.clearWarnings(); + } + + @Override + public void close() throws SQLException { + this.statement.close(); + } + + @Override + public void addBatch(String paramString) throws SQLException { + this.statement.addBatch(paramString); + } + + @Override + public void clearBatch() throws SQLException { + this.statement.clearBatch(); + } + + @Override + public Connection getConnection() throws SQLException { + return this.statement.getConnection(); + } + + @Override + public int getFetchDirection() throws SQLException { + return this.statement.getFetchDirection(); + } + + @Override + public int getFetchSize() throws SQLException { + return this.statement.getFetchSize(); + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + return this.statement.getGeneratedKeys(); + } + + @Override + public int getMaxFieldSize() throws SQLException { + return this.statement.getMaxFieldSize(); + } + + @Override + public int getMaxRows() throws SQLException { + return this.statement.getMaxRows(); + } + + @Override + public boolean getMoreResults() throws SQLException { + return this.statement.getMoreResults(); + } + + @Override + public boolean getMoreResults(int paramInt) throws SQLException { + return this.statement.getMoreResults(paramInt); + } + + @Override + public int getQueryTimeout() throws SQLException { + return this.statement.getQueryTimeout(); + } + + @Override + public ResultSet getResultSet() throws SQLException { + return this.statement.getResultSet(); + } + + @Override + public int getResultSetConcurrency() throws SQLException { + return this.statement.getResultSetConcurrency(); + } + + @Override + public int getResultSetHoldability() throws SQLException { + return this.statement.getResultSetHoldability(); + } + + @Override + public int getResultSetType() throws SQLException { + return this.statement.getResultSetType(); + } + + @Override + public int getUpdateCount() throws SQLException { + return this.statement.getUpdateCount(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return this.statement.getWarnings(); + } + + @Override + public boolean isClosed() throws SQLException { + return this.statement.isClosed(); + } + + @Override + public boolean isPoolable() throws SQLException { + return this.statement.isPoolable(); + } + + @Override + public void setCursorName(String paramString) throws SQLException { + this.statement.setCursorName(paramString); + } + + @Override + public void setEscapeProcessing(boolean paramBoolean) throws SQLException { + this.statement.setEscapeProcessing(paramBoolean); + } + + @Override + public void setFetchDirection(int paramInt) throws SQLException { + this.statement.setFetchDirection(paramInt); + } + + @Override + public void setFetchSize(int paramInt) throws SQLException { + this.statement.setFetchSize(paramInt); + } + + @Override + public void setMaxFieldSize(int paramInt) throws SQLException { + this.statement.setMaxFieldSize(paramInt); + } + + @Override + public void setMaxRows(int paramInt) throws SQLException { + this.statement.setMaxRows(paramInt); + } + + @Override + public void setPoolable(boolean paramBoolean) throws SQLException { + this.statement.setPoolable(paramBoolean); + } + + @Override + public void setQueryTimeout(int paramInt) throws SQLException { + this.statement.setQueryTimeout(paramInt); + } + + @Override + public boolean isWrapperFor(Class paramClass) throws SQLException { + return this.statement.isWrapperFor(paramClass); + } + + @Override + public T unwrap(Class paramClass) throws SQLException { + return this.statement.unwrap(paramClass); + } + + @Override + public AbstractTxcConnection getTxcDBConnection() { + return abstractTxcConnection; + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/AbstractParser.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/AbstractParser.java new file mode 100644 index 000000000..986af3b9d --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/AbstractParser.java @@ -0,0 +1,185 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.codingapi.tx.datasource.relational.txc.TableMetaUtils; +import com.codingapi.tx.datasource.relational.txc.TxcPreparedStatement; +import com.codingapi.tx.datasource.relational.txc.TxcStatement; +import org.apache.commons.collections.CollectionUtils; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +/** + * @author jsy. + */ + +public abstract class AbstractParser{ + + public CommitInfo parse(TxcStatement txcStatement) throws SQLException { + CommitInfo commitInfo = new CommitInfo(); + + String sql = txcStatement.getSql(); + T sqlParseStatement = (T) new MySqlStatementParser(sql).parseStatement(); + + //设置sqltype + commitInfo.setSqlType(getSqlType()); + + //设置where + commitInfo.setWhere(getWhere(sqlParseStatement)); + + //sql + commitInfo.setSql(sql); + + + + if (txcStatement instanceof TxcPreparedStatement) { + commitInfo.setSqlParams(((TxcPreparedStatement) txcStatement).getParamsList()); + commitInfo.setWhereParams( + getWhereParams(((TxcPreparedStatement) txcStatement).getParamsList(), sqlParseStatement)); + } + + //解析之前的值 + commitInfo.setOriginalValue(getOriginValue(commitInfo.getWhereParams(), sqlParseStatement, txcStatement.getConnection())); + + //解析之后的值 + commitInfo.setPresentValue(getPresentValue(commitInfo.getSqlParams(), sqlParseStatement)); + + return commitInfo; + + + + } + + protected abstract List getWhereParams(List sqlParamsList, T parseSqlStatement); + + protected abstract String getWhere(T parseSqlStatement); + + //从当前sql取出值 + public abstract TxcTable getPresentValue(List sqlParamsList, T parseSqlStatement); + + //从数据库取出值 + public TxcTable getOriginValue(List whereParamsList, T parseSqlStatement, Connection connection) + throws SQLException { + TxcTable txcTable = new TxcTable(); + txcTable.setTableName(getTableName(parseSqlStatement)); + + // 组装sql + String primaryKeyName = TableMetaUtils + .getTableMetaInfo(connection, getTableName(parseSqlStatement)).getPrimaryKeyName(); + String selectSql = selectSql(parseSqlStatement, primaryKeyName); + + PreparedStatement preparedStatement = connection.prepareStatement(selectSql); + + if (CollectionUtils.isNotEmpty(whereParamsList)) { + // 设置条件 + for (int i = 1; i <= whereParamsList.size(); i++) { + preparedStatement.setObject(i, whereParamsList.get(i - 1)); + } + } + + // 执行查询sql + ResultSet resultSet = preparedStatement.executeQuery(); + + List txcLines = ResultConvertUtils.convertWithPrimary(resultSet, primaryKeyName, getSqlType()); + + // convert + txcTable.setLine(txcLines); + return txcTable; + } + + + public abstract SQLType getSqlType(); + + + +// +// public int getTypeByClass(Object x) { +// if (x == null) { +// return Types.OTHER; +// } +// +// Class clazz = x.getClass(); +// if (clazz == Byte.class) { +// return Types.TINYINT; +// } +// +// if (clazz == Short.class) { +// return Types.SMALLINT; +// } +// +// if (clazz == Integer.class) { +// return Types.INTEGER; +// } +// +// if (clazz == Long.class) { +// +// return Types.BIGINT; +// } +// +// if (clazz == String.class) { +// return Types.VARCHAR; +// } +// +// if (clazz == BigDecimal.class) { +// return Types.DECIMAL; +// } +// +// if (clazz == Float.class) { +// return Types.FLOAT; +// } +// +// if (clazz == Double.class) { +// return Types.DOUBLE; +// } +// +// if (clazz == java.sql.Date.class || clazz == java.util.Date.class) { +// return Types.DATE; +// } +// +// if (clazz == java.sql.Timestamp.class) { +// return Types.TIMESTAMP; +// } +// +// if (clazz == java.sql.Time.class) { +// return Types.TIME; +// } +// +// if (clazz == Boolean.class) { +// return Types.BOOLEAN; +// } +// +// if (clazz == byte[].class) { +// return JdbcParameter.TYPE.BYTES; +// } +// +// if (x instanceof InputStream) { +// return JdbcParameter.TYPE.BinaryInputStream; +// } +// +// if (x instanceof Reader) { +// return JdbcParameter.TYPE.CharacterInputStream; +// } +// +// if (x instanceof Clob) { +// return Types.CLOB; +// } +// +// if (x instanceof NClob) { +// return Types.NCLOB; +// } +// +// if (x instanceof Blob) { +// return Types.BLOB; +// } +// return Types.OTHER; +// +// } + + protected abstract String selectSql(T parseSqlStatement, String primaryKeyName); + + protected abstract String getTableName(T parseSqlStatement); + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/CommitInfo.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/CommitInfo.java new file mode 100644 index 000000000..497bd04d3 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/CommitInfo.java @@ -0,0 +1,106 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import com.google.common.collect.Lists; + +import java.util.List; + + +public class CommitInfo implements Cloneable { + /** + * 更新之前 的行数据 + */ + private TxcTable originalValue = new TxcTable(); + /** + * 更新后的行数据 + */ + private TxcTable presentValue = new TxcTable(); + + /** + * Where 条件 + */ + private String where = ""; + + private List whereParams = Lists.newArrayList(); + /** + * 更新类型 UPDTAE or DELETE or insert + */ + private SQLType sqlType = null; + /** + * 业务执行sql + */ + private String sql = ""; + + /** + * sql的属性值,对应PreparedStatement存在 + */ + private List sqlParams = Lists.newArrayList(); + + private String schemaName; + + + + public TxcTable getOriginalValue() { + return originalValue; + } + + public void setOriginalValue(TxcTable originalValue) { + this.originalValue = originalValue; + } + + public TxcTable getPresentValue() { + return presentValue; + } + + public void setPresentValue(TxcTable presentValue) { + this.presentValue = presentValue; + } + + public String getWhere() { + return where; + } + + public void setWhere(String where) { + this.where = where; + } + + public List getWhereParams() { + return whereParams; + } + + public void setWhereParams(List whereParams) { + this.whereParams = whereParams; + } + + public SQLType getSqlType() { + return sqlType; + } + + public void setSqlType(SQLType sqlType) { + this.sqlType = sqlType; + } + + + public String getSql() { + return sql; + } + + public void setSql(String sql) { + this.sql = sql; + } + + public List getSqlParams() { + return sqlParams; + } + + public void setSqlParams(List sqlParams) { + this.sqlParams = sqlParams; + } + + public String getSchemaName() { + return schemaName; + } + + public void setSchemaName(String schemaName) { + this.schemaName = schemaName; + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/DeleteParser.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/DeleteParser.java new file mode 100644 index 000000000..1afbb2506 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/DeleteParser.java @@ -0,0 +1,69 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.ListUtils; + +import java.util.List; + +/** + * @author jsy. + */ +public class DeleteParser extends AbstractParser { + private static DeleteParser instance = null; + + public static DeleteParser getInstance() { + if (instance == null) { + synchronized (DeleteParser.class) { + if (instance == null) { + instance = new DeleteParser(); + } + } + } + return instance; + } + + @Override + protected List getWhereParams(List sqlParamsList, MySqlDeleteStatement parseSqlStatement) { + if (CollectionUtils.isNotEmpty(sqlParamsList)) { + return sqlParamsList; + } + return ListUtils.EMPTY_LIST; + } + + @Override + protected String getWhere(MySqlDeleteStatement parseSqlStatement) { + return SqlUtils.toSQLString(parseSqlStatement.getWhere()); + } + + @Override + public TxcTable getPresentValue(List sqlParamsList, MySqlDeleteStatement parseSqlStatement) { + return null; + } + + + @Override + public SQLType getSqlType() { + return SQLType.DELETE; + } + + @Override + protected String selectSql(MySqlDeleteStatement mySqlUpdateStatement, String primaryKeyName) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("SELECT * "); + + // + stringBuffer.append(" from ").append(mySqlUpdateStatement.getTableName().getSimpleName()).append(" where "); + // + stringBuffer.append(SqlUtils.toSQLString(mySqlUpdateStatement.getWhere())); + return stringBuffer.toString(); + } + + @Override + protected String getTableName(MySqlDeleteStatement parseSqlStatement) { + return parseSqlStatement.getTableName().getSimpleName(); + } + + + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/ExecutePaser.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/ExecutePaser.java new file mode 100644 index 000000000..8a2729b3e --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/ExecutePaser.java @@ -0,0 +1,124 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.codingapi.tx.datasource.relational.txc.TableMetaInfo; +import com.codingapi.tx.datasource.relational.txc.TableMetaUtils; +import com.codingapi.tx.datasource.relational.txc.TxcStatement; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +/** + * [类描述] + * + * @author caican + */ +public class ExecutePaser { + + private static final Logger logger = LoggerFactory.getLogger(ExecutePaser.class); + + + + public static SQLType parse(TxcStatement txcStatement) { + long start = System.currentTimeMillis(); + SQLType sqlType = SQLType.SELECT; + try { + TxcRuntimeContext txcRuntimeContext = txcStatement.getTxcDBConnection().getTxcRuntimeContext(); + + //解析sql + String sql = txcStatement.getSql(); + SQLStatement sqlParseStatement = new MySqlStatementParser(sql).parseStatement(); + + CommitInfo commitInfo = null; + if (sqlParseStatement instanceof MySqlUpdateStatement) { + commitInfo = UpdateParser.getInstance().parse(txcStatement); + txcRuntimeContext.getInfo().add(commitInfo); + sqlType = SQLType.UPDATE; + } else if(sqlParseStatement instanceof MySqlInsertStatement){ + commitInfo = InsertParser.getInstance().parse(txcStatement); + txcRuntimeContext.getInfo().add(commitInfo); + sqlType = SQLType.INSERT; + } else if(sqlParseStatement instanceof MySqlDeleteStatement) { + commitInfo = DeleteParser.getInstance().parse(txcStatement); + txcRuntimeContext.getInfo().add(commitInfo); + sqlType = SQLType.DELETE; + } + + if (commitInfo != null && commitInfo.getSchemaName() == null) { + String dbName = TableMetaUtils.getDbNameFromUrl(txcStatement.getConnection().getMetaData().getURL()); + commitInfo.setSchemaName(dbName); + } + + } catch (Exception e) { + logger.error("parse sql error", e); + } finally { + long cost = System.currentTimeMillis() - start; + if (sqlType != SQLType.SELECT || cost > 50) { + logger.info("解析 sql:{}, cost:{}ms", txcStatement.getSql(), cost); + } + } + return sqlType; + } + + public static void after(TxcStatement txcStatement, SQLType sqlType) { + + try { + if (sqlType == SQLType.INSERT) { + TxcRuntimeContext txcRuntimeContext = txcStatement.getTxcDBConnection().getTxcRuntimeContext(); + List commitInfos = txcRuntimeContext.getInfo(); + if (commitInfos.size() == 0) { + return; + } + + CommitInfo commitInfo = commitInfos.get(commitInfos.size() - 1); + + List line = commitInfo.getPresentValue().getLine(); + if (line.size() > 1) { + logger.error("不支持多条插入sql"); + return; + } + + TxcLine txcLine = line.get(0); + + setPrimaryValue(txcStatement, commitInfo, txcLine); + + } + } catch (SQLException e) { + logger.error("execute parser after error", e); + } + } + + private static void setPrimaryValue(TxcStatement txcStatement, CommitInfo commitInfo, + TxcLine txcLine) throws SQLException { + TableMetaInfo tableMetaInfo = TableMetaUtils + .getTableMetaInfo(txcStatement.getConnection(), commitInfo.getPresentValue().getTableName()); + String autoIncrementPrimaryKey = tableMetaInfo.getAutoIncrementPrimaryKey(); + if (StringUtils.isBlank(autoIncrementPrimaryKey)) { + String primaryKeyName = tableMetaInfo.getPrimaryKeyName(); + txcLine.setPrimaryKey(primaryKeyName); + + for (TxcField txcField : txcLine.getFields()) { + if (txcField.getName().equals(primaryKeyName)) { + txcLine.setPrimaryValue(txcField.getValue()); + return; + } + } + + } else { + txcLine.setPrimaryKey(autoIncrementPrimaryKey); + ResultSet resultSet = txcStatement.getConnection().prepareStatement("select last_insert_id() as id") + .executeQuery(); + while (resultSet.next()) { + txcLine.setPrimaryValue(resultSet.getObject("id")); + } + } + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/InsertParser.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/InsertParser.java new file mode 100644 index 000000000..ac0bc5fcd --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/InsertParser.java @@ -0,0 +1,92 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.statement.SQLInsertStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlInsertStatement; +import org.apache.commons.collections.CollectionUtils; + +import java.sql.Connection; +import java.util.List; + +/** + * @author jsy. + */ +public class InsertParser extends AbstractParser{ + + private static InsertParser instance = null; + + public static InsertParser getInstance() { + if (instance == null) { + synchronized (InsertParser.class) { + if (instance == null) { + instance = new InsertParser(); + } + } + } + return instance; + } + @Override + protected List getWhereParams(List sqlParamsList, + MySqlInsertStatement parseSqlStatement) { + return null; + } + + @Override + protected String getWhere(MySqlInsertStatement parseSqlStatement) { + return null; + } + + @Override + public TxcTable getPresentValue(List sqlParamsList, MySqlInsertStatement parseSqlStatement) { + + TxcTable txcTable = new TxcTable(); + txcTable.setTableName(parseSqlStatement.getTableName().getSimpleName()); + List line = txcTable.getLine(); + + List valuesList = parseSqlStatement.getValuesList(); + List columns = parseSqlStatement.getColumns(); + + + for (SQLInsertStatement.ValuesClause valuesClause : valuesList) { + List values = valuesClause.getValues(); + TxcLine txcLine = new TxcLine(); + for (int i = 0; i < columns.size(); i++) { + TxcField txcField = new TxcField(); + txcField.setName(SqlUtils.toSQLString(columns.get(i)).replace("\'", "").replace("`", "").trim()); + if (CollectionUtils.isNotEmpty(sqlParamsList)) { + txcField.setValue(sqlParamsList.get(i)); + } else { + txcField.setValue(SqlUtils.toSQLString(values.get(i))); + } + txcLine.getFields().add(txcField); + } + line.add(txcLine); + } + + + + return txcTable; + } + + @Override + public TxcTable getOriginValue(List whereParamsList, MySqlInsertStatement parseSqlStatement, + Connection connection) { + return null; + } + + + @Override + public SQLType getSqlType() { + return SQLType.INSERT; + } + + @Override + protected String selectSql(MySqlInsertStatement parseSqlStatement, String primaryKeyName) { + throw new RuntimeException("不支持的类型"); + } + + @Override + protected String getTableName(MySqlInsertStatement parseSqlStatement) { + return parseSqlStatement.getTableName().getSimpleName(); + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/ResultConvertUtils.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/ResultConvertUtils.java new file mode 100644 index 000000000..6b61481ff --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/ResultConvertUtils.java @@ -0,0 +1,148 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import com.google.common.collect.Lists; +import org.apache.commons.lang.StringUtils; + +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; + +/** + * @author jsy. + */ +public class ResultConvertUtils { + + + + public static List convertWithPrimary(ResultSet resultSet, String primaryKeyName, SQLType sqlType) throws SQLException { + List txcLines = Lists.newArrayList(); + ResultSetMetaData metaData = resultSet.getMetaData(); + + + while (resultSet.next()) { + TxcLine txcLine = new TxcLine(); + for (int i = 1; i <= metaData.getColumnCount(); i++) { + + if (i == metaData.getColumnCount() && StringUtils.equalsIgnoreCase(metaData.getColumnName(i), primaryKeyName) && sqlType == SQLType.UPDATE) { + + txcLine.setPrimaryKey(metaData.getColumnName(i)); + txcLine.setPrimaryValue(getDataByType(i, metaData.getColumnType(i), resultSet)); + } else { + TxcField txcField = new TxcField(); + + txcField.setName(metaData.getColumnName(i)); + txcField.setType(metaData.getColumnType(i)); + txcField.setValue(getDataByType(i, metaData.getColumnType(i), resultSet)); + + + txcLine.getFields().add(txcField); + } + + } + txcLines.add(txcLine); + + } + return txcLines; + } + + //https://www.cis.upenn.edu/~bcpierce/courses/629/jdkdocs/guide/jdbc/getstart/mapping.doc.html + private static Object getDataByType(int index, int columnType, ResultSet resultSet) throws SQLException { + + if (columnType == Types.BIT) { + return resultSet.getByte(index); + } + + if (columnType == Types.TINYINT) { + return resultSet.getByte(index); + + } + + if (columnType == Types.SMALLINT) { + return resultSet.getShort(index); + } + + + if (columnType == Types.INTEGER) { + return resultSet.getInt(index); + } + + if (columnType == Types.BIGINT) { + return resultSet.getLong(index); + } + + if (columnType == Types.FLOAT) { + return resultSet.getFloat(index); + + } + + if (columnType == Types.DOUBLE) { + return resultSet.getDouble(index); + + } + + if (columnType == Types.NUMERIC) { + return resultSet.getInt(index); + + } + + if (columnType == Types.DECIMAL) { + return resultSet.getBigDecimal(index); + } + + + if (columnType == Types.CHAR) { + return resultSet.getString(index); +// return resultSet.getCharacterStream(index); + } + + if (columnType == Types.VARCHAR) { + return resultSet.getString(index); + } + + + if (columnType == Types.LONGNVARCHAR) { + return resultSet.getString(index); + } + + if (columnType == Types.DATE) { + return resultSet.getDate(index); + } + + if (columnType == Types.TIME) { + return resultSet.getTime(index); + } + + if (columnType == Types.NCHAR) { + return resultSet.getNString(index); +// return resultSet.getNCharacterStream(index); + } + + if (columnType == Types.NVARCHAR) { + return resultSet.getNString(index); + } + + if (columnType == Types.OTHER) { + return resultSet.getObject(index); + } + + if (columnType == Types.BLOB) { + return resultSet.getBlob(index); + } + + if (columnType == Types.BOOLEAN) { + return resultSet.getBoolean(index); + } + + + if (columnType == Types.ARRAY) { + return resultSet.getArray(index); + } + + if (columnType == Types.TIMESTAMP) { + return resultSet.getTimestamp(index); + } + return resultSet.getObject(index); + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/SQLType.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/SQLType.java new file mode 100644 index 000000000..8f38be110 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/SQLType.java @@ -0,0 +1,12 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +/** + * + */ +public enum SQLType { + SELECT(), + UPDATE(), + INSERT(), + DELETE(), + UNKNOW(); +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/SqlUtils.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/SqlUtils.java new file mode 100644 index 000000000..18da1c3a5 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/SqlUtils.java @@ -0,0 +1,16 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLObject; + +/** + * @author jsy. + */ +public class SqlUtils { + + private static final String dbType = "mysql"; + + public static String toSQLString(SQLObject sqlObject) { + return SQLUtils.toSQLString(sqlObject); + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcField.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcField.java new file mode 100644 index 000000000..466b67a6a --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcField.java @@ -0,0 +1,50 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * @author caisirius + */ +public class TxcField { + private String name; + + // DiffUtils 比对时忽略此字段 + @JsonIgnore + private int type; + private Object value; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public String getSqlName() { + return "`" + name + "`"; + } + + + @Override + public String toString() { + return String.format("[%s,%s]", new Object[]{this.name, String.valueOf(this.value)}); + } +} \ No newline at end of file diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcLine.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcLine.java new file mode 100644 index 000000000..70d29de9a --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcLine.java @@ -0,0 +1,46 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author caisirius + */ +public class TxcLine { + + private List fields = new ArrayList(); + + // DiffUtils 比对时忽略此字段 + @JsonIgnore + private Object primaryKey; + + @JsonIgnore + private Object primaryValue; + + public List getFields() { + return fields; + } + + public void setFields(List fields) { + this.fields = fields; + } + + public Object getPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(Object primaryKey) { + this.primaryKey = primaryKey; + } + + public Object getPrimaryValue() { + return primaryValue; + } + + public void setPrimaryValue(Object primaryValue) { + this.primaryValue = primaryValue; + } +} \ No newline at end of file diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcRuntimeContext.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcRuntimeContext.java new file mode 100644 index 000000000..54a069a10 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcRuntimeContext.java @@ -0,0 +1,73 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author caisirius + */ +public class TxcRuntimeContext { + private static final Logger logger = LoggerFactory.getLogger(TxcRuntimeContext.class); + + /** + * 事务组Id 对应于txc的 xid + */ + public String groupId; + /** + * 分支事务Id lcn里叫 kid + */ + public String branchId; + /** + * 提交信息 + */ + private List info = new ArrayList(); + + public int status; + /** + * 分支所在IP + */ + public String server; + + public List getInfo() { + return info; + } + + public void setInfo(List info) { + this.info = info; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getBranchId() { + return branchId; + } + + public void setBranchId(String branchId) { + this.branchId = branchId; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } +} \ No newline at end of file diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcTable.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcTable.java new file mode 100644 index 000000000..570ce4317 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcTable.java @@ -0,0 +1,74 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author caisirius + */ +public class TxcTable { + + public String schemaName; + public String tableName; + public String alias; + + private List line = new ArrayList(); + + + @Override + public String toString() { + StringBuilder localStringBuilder = new StringBuilder(); + for (int i = 0; i < this.line.size(); i++) { + + for (TxcField field : this.line.get(i).getFields()) { + + switch (field.getType()) { + case -15: + case -9: + case -6: + case -5: + case 1: + case 2: + case 4: + case 12: + case 2003: + localStringBuilder.append(field.getValue()).append(','); + default: + } + } + } + return localStringBuilder.toString(); + } + + public String getSchemaName() { + return schemaName; + } + + public void setSchemaName(String schemaName) { + this.schemaName = schemaName; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public List getLine() { + return line; + } + + public void setLine(List line) { + this.line = line; + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/UpdateParser.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/UpdateParser.java new file mode 100644 index 000000000..217de50b6 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/UpdateParser.java @@ -0,0 +1,124 @@ +package com.codingapi.tx.datasource.relational.txc.parser; + +import com.alibaba.druid.sql.ast.expr.SQLValuableExpr; +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem; +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.ListUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * @author jsy. + */ +public class UpdateParser extends AbstractParser { + private static final Logger logger = LoggerFactory.getLogger(UpdateParser.class); + + + private static UpdateParser instance = null; + + public static UpdateParser getInstance() { + if (instance == null) { + synchronized (UpdateParser.class) { + if (instance == null) { + instance = new UpdateParser(); + } + } + } + return instance; + } + + @Override + protected List getWhereParams(List sqlParamsList, SQLUpdateStatement parseSqlStatement) { + if (CollectionUtils.isNotEmpty(sqlParamsList)) { + int size = 0; + + for (SQLUpdateSetItem sqlUpdateSetItem :parseSqlStatement.getItems()) { + if (sqlUpdateSetItem.getValue() instanceof SQLVariantRefExpr) { + size++; + } + } + return sqlParamsList.subList(size, sqlParamsList.size()); + } + return ListUtils.EMPTY_LIST; + + } + + + @Override + protected String getWhere(SQLUpdateStatement parseSqlStatement) { + return SqlUtils.toSQLString(parseSqlStatement.getWhere()); + } + + @Override + public TxcTable getPresentValue(List sqlParamsList, SQLUpdateStatement parseSqlStatement) { + + TxcTable txcTable = new TxcTable(); + txcTable.setTableName(parseSqlStatement.getTableName().getSimpleName()); + + TxcLine txcLine = new TxcLine(); + List items = parseSqlStatement.getItems(); + + int variantExpr = 0; + for (int i = 0; i < items.size(); i++) { + SQLUpdateSetItem sqlUpdateSetItem = items.get(i); + TxcField txcField = new TxcField(); + String cloumnName = SqlUtils.toSQLString(sqlUpdateSetItem.getColumn()).replace("\'", "").replace("`", "").trim(); + txcField.setName(cloumnName); + if (sqlUpdateSetItem.getValue() instanceof SQLVariantRefExpr) { + txcField.setValue(sqlParamsList.get(variantExpr++)); + } else if (sqlUpdateSetItem.getValue() instanceof SQLValuableExpr){ + txcField.setValue(SqlUtils.toSQLString(items.get(i).getValue())); + } else { + logger.info("不支持复杂的sql,{}", sqlUpdateSetItem.getClass().toString()); + throw new RuntimeException("不支持复杂的sql"); + } + + txcLine.getFields().add(txcField); + } + txcTable.getLine().add(txcLine); + + return txcTable; + } + + + + + @Override + public SQLType getSqlType() { + return SQLType.UPDATE; + } + + + @Override + protected String selectSql(SQLUpdateStatement mySqlUpdateStatement, String primaryKeyName) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("SELECT "); + + + List items = mySqlUpdateStatement.getItems(); + for (SQLUpdateSetItem sqlUpdateSetItem : items) { + + stringBuffer.append(SqlUtils.toSQLString(sqlUpdateSetItem.getColumn())).append(","); + + } + + stringBuffer.append(primaryKeyName); + + + + stringBuffer.append(" from ").append(mySqlUpdateStatement.getTableName().getSimpleName()).append(" where "); + + stringBuffer.append(SqlUtils.toSQLString(mySqlUpdateStatement.getWhere())); + return stringBuffer.toString(); + } + + @Override + protected String getTableName(SQLUpdateStatement parseSqlStatement) { + return parseSqlStatement.getTableName().getSimpleName(); + } + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/AbstractRollback.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/AbstractRollback.java new file mode 100644 index 000000000..cce3ebc18 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/AbstractRollback.java @@ -0,0 +1,41 @@ +package com.codingapi.tx.datasource.relational.txc.rollback; + +import com.codingapi.tx.datasource.relational.txc.parser.CommitInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +/** + * @author jsy. + */ +public abstract class AbstractRollback { + private Logger logger = LoggerFactory.getLogger(AbstractRollback.class); + + + public void rollback(CommitInfo commitInfo, Connection connection) throws SQLException { + + //check + boolean flag = canRollback(commitInfo, connection); + + + //rollback + if (flag) { + logger.info("rollback for sql:{}", commitInfo.getSql()); + List preparedStatements = assembleRollbackSql(commitInfo, connection); + + for (PreparedStatement preparedStatement : preparedStatements) { + preparedStatement.execute(); + } + logger.info("rollback sql success"); + } + } + + protected abstract List assembleRollbackSql(CommitInfo commitInfo, Connection connection) + throws SQLException; + + protected abstract boolean canRollback(CommitInfo commitInfo, Connection connection) throws SQLException; +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/DeleteRollback.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/DeleteRollback.java new file mode 100644 index 000000000..f421c1797 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/DeleteRollback.java @@ -0,0 +1,89 @@ +package com.codingapi.tx.datasource.relational.txc.rollback; + +import com.codingapi.tx.datasource.relational.txc.parser.CommitInfo; +import com.codingapi.tx.datasource.relational.txc.parser.TxcField; +import com.codingapi.tx.datasource.relational.txc.parser.TxcLine; +import com.google.common.collect.Lists; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jsy. + */ +public class DeleteRollback extends AbstractRollback { + + private Logger logger = LoggerFactory.getLogger(DeleteRollback.class); + + private static DeleteRollback instance = null; + + public static DeleteRollback getInstance() { + if (instance == null) { + synchronized (DeleteRollback.class) { + if (instance == null) { + instance = new DeleteRollback(); + } + } + } + return instance; + } + + @Override + protected List assembleRollbackSql(CommitInfo commitInfo, Connection connection) + throws SQLException { + + ArrayList preparedStatements = Lists.newArrayList(); + String tableName = commitInfo.getOriginalValue().getTableName(); + + for (TxcLine txcLine : commitInfo.getOriginalValue().getLine()) { + List txcFields = txcLine.getFields(); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("insert into ").append(tableName).append("("); + + for (int i = 0; i < txcFields.size(); i++) { + if (i == txcFields.size() - 1) { + stringBuilder.append(txcFields.get(i).getSqlName()).append(")"); + } else { + stringBuilder.append(txcFields.get(i).getSqlName()).append(","); + } + } + stringBuilder.append(" value ").append("("); + + for (int i = 0; i < txcFields.size(); i++) { + if (i == txcFields.size() - 1) { + stringBuilder.append("?").append(")"); + } else { + stringBuilder.append("?").append(","); + } + } + String sql = stringBuilder.toString(); + PreparedStatement preparedStatement = connection.prepareStatement(sql); + + for (int i = 1; i <= txcFields.size(); i++) { + preparedStatement.setObject(i, txcFields.get(i - 1).getValue()); + } + preparedStatements.add(preparedStatement); + } + + + return preparedStatements; + } + + @Override + protected boolean canRollback(CommitInfo commitInfo, Connection connection) throws SQLException { + + + if ( commitInfo.getOriginalValue().getLine().size() == 0) { + logger.error("未新影响行数,不回滚"); + return false; + } + + return true; + + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/DiffUtils.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/DiffUtils.java new file mode 100644 index 000000000..9b5bc94dd --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/DiffUtils.java @@ -0,0 +1,106 @@ +package com.codingapi.tx.datasource.relational.txc.rollback; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import java.io.IOException; +import java.io.StringReader; +import java.math.BigDecimal; + +/** + * @author jsy. + */ + +public class DiffUtils { + private static final ObjectMapper objectMapper = new ObjectMapper(); + + static { + objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true); + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addSerializer(BigDecimal.class, new JsonSerializer() { + @Override + public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) + throws IOException, JsonProcessingException { + gen.writeString(value.setScale(2).toString()); + } + }); + + simpleModule.addSerializer(StringReader.class, new JsonSerializer() { + @Override + public void serialize(StringReader value, JsonGenerator gen, SerializerProvider serializers) + throws IOException, JsonProcessingException { + gen.writeString(read(value)); + } + }); + + objectMapper.registerModule(simpleModule); + } + + public static boolean diff(Object oldDifDto, Object curDifDto) { + try { + String old = objectMapper.writeValueAsString(oldDifDto); + + String cur = objectMapper.writeValueAsString(curDifDto); + JsonNode oldJsonNode = objectMapper.readTree(old); + JsonNode curJsonNode = objectMapper.readTree(cur); + + if (oldJsonNode.equals(curJsonNode)) { + return true; + } + + return false; + } catch (Exception e) { + return false; + } + + } + + + public static ObjectMapper getObjectMapper() { + return objectMapper; + } + + + public static String read(StringReader stringReader) { + StringBuilder stringBuilder = new StringBuilder(); + try { + stringReader.reset(); + int c; + while ((c = stringReader.read()) != -1) { + stringBuilder.append((char)c); + } + return stringBuilder.toString(); + } catch (IOException e) { + return ""; + } + } + + public static void main(String[] args) { +// BigDecimal bigDecimal = new BigDecimal("50.00"); +// BigDecimal bigDecimal1 = new BigDecimal("50"); +// +// try { +// String s = objectMapper.writeValueAsString(bigDecimal); +// String s1 = objectMapper.writeValueAsString(bigDecimal1); +// System.out.println(s); +// System.out.println(s1); +// } catch (JsonProcessingException e) { +// e.printStackTrace(); +// } + + StringReader test = new StringReader("a:2:{s:20:\"php_serialize_option\";s:1:\" \";s:9:\"orderdata\";a:1:{i:0;a:22:{s:11:\"refund_type\";s:1:\"0\";s:8:\"dateline\";s:1:\"0\";s:11:\"mk_order_id\";s:4:\"null\";s:5:\"stype\";s:2:\"16\";s:11:\"sl_nickname\";s:10:\"zhigb_0016\";s:3:\"num\";s:1:\"1\";s:5:\"ptype\";s:2:\"23\";s:5:\"title\";s:55:\"lqq田酞递址秽梳猜欣勾翅#169779075稿件中标\";s:10:\"sl_user_id\";s:8:\"19182259\";s:7:\"link_id\";s:1:\"0\";s:11:\"offer_price\";s:3:\"0.0\";s:5:\"mtype\";s:1:\"0\";s:12:\"product_pkid\";s:9:\"169779075\";s:7:\"data_id\";s:8:\"90567713\";s:7:\"user_id\";s:8:\"19182244\";s:5:\"price\";s:4:\"50.0\";s:11:\"refund_time\";s:1:\"0\";s:8:\"nickname\";s:18:\"靖哥哥的店铺\";s:13:\"refund_amount\";s:3:\"0.0\";s:8:\"order_id\";s:8:\"90567198\";s:8:\"at_price\";s:4:\"50.0\";s:12:\"refund_state\";s:1:\"0\";}}}"); + String read = read(test); + + + System.out.println(read); + } + +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/InsertRollback.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/InsertRollback.java new file mode 100644 index 000000000..332626235 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/InsertRollback.java @@ -0,0 +1,109 @@ +package com.codingapi.tx.datasource.relational.txc.rollback; + +import com.codingapi.tx.datasource.relational.txc.TxcSqlExecutor; +import com.codingapi.tx.datasource.relational.txc.parser.CommitInfo; +import com.codingapi.tx.datasource.relational.txc.parser.TxcField; +import com.codingapi.tx.datasource.relational.txc.parser.TxcLine; +import com.google.common.collect.Lists; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +/** + * @author jsy. + */ +public class InsertRollback extends AbstractRollback{ + + private Logger logger = LoggerFactory.getLogger(InsertRollback.class); + + private static InsertRollback instance = null; + + public static InsertRollback getInstance() { + if (instance == null) { + synchronized (InsertRollback.class) { + if (instance == null) { + instance = new InsertRollback(); + } + } + } + return instance; + } + + + @Override + protected List assembleRollbackSql(CommitInfo commitInfo, Connection connection) + throws SQLException { + + TxcLine txcLine = commitInfo.getPresentValue().getLine().get(0); + String tableName = commitInfo.getPresentValue().getTableName(); + String sql = "delete from " + tableName + " where " + txcLine.getPrimaryKey() + "= ?"; + PreparedStatement preparedStatement = connection.prepareStatement(sql); + + preparedStatement.setObject(1, txcLine.getPrimaryValue()); + return Lists.newArrayList(preparedStatement); + } + + + private PreparedStatement assembleQuerySql(TxcLine txcLine, String tableName, Connection connection) + throws SQLException { + List txcFields = txcLine.getFields(); + //查询db数据 + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("select "); + + for (int j = 0; j < txcFields.size(); j++) { + if (j != txcFields.size() - 1) { + stringBuffer.append(txcFields.get(j).getSqlName()).append(","); + } else { + stringBuffer.append(txcFields.get(j).getSqlName()); + } + } + + stringBuffer.append(" from ").append(tableName).append(" where ").append(txcLine.getPrimaryKey()).append("= ?"); + + String sql = stringBuffer.toString(); + PreparedStatement preparedStatement = connection.prepareStatement(sql); + preparedStatement.setObject(1, txcLine.getPrimaryValue()); + return preparedStatement; + } + + + + @Override + protected boolean canRollback(CommitInfo commitInfo, Connection connection) throws SQLException { + List txcLines = commitInfo.getPresentValue().getLine(); + + if (txcLines.size() > 1) { + logger.error("insert操作,影响行数大于1,不支持回滚"); + return false; + } + + if ( txcLines.size() == 0) { + logger.error("未新影响行数,不回滚"); + return false; + } + + TxcLine txcLine = txcLines.get(0); + + // 查询db数据 + PreparedStatement preparedStatement = assembleQuerySql(txcLine, commitInfo.getPresentValue().getTableName(), connection); + List dbValue = TxcSqlExecutor.executeQuery(preparedStatement); + + boolean diff = DiffUtils.diff(txcLines, dbValue); + if (!diff) { + try { + logger.error("数据不一致,不支持回滚操作, before:{},after:{}", + DiffUtils.getObjectMapper().writeValueAsString(txcLines), + DiffUtils.getObjectMapper().writeValueAsString(dbValue)); + } catch (Exception e) { + logger.error("error", e); + } + return false; + } + return true; + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackDataSource.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackDataSource.java new file mode 100644 index 000000000..09b910fdb --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackDataSource.java @@ -0,0 +1,32 @@ +package com.codingapi.tx.datasource.relational.txc.rollback; + +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +/** + * @author jsy. + */ +@Component +public class TxcRollbackDataSource { + + private Map dataSourceMap = new HashMap<>(); + + public void setDataSourceMap(Map dataSourceMap) { + this.dataSourceMap = dataSourceMap; + } + + public Connection getConnectionByDbName(String dbName) throws SQLException { + + DataSource dataSource = dataSourceMap.get(dbName); + if (dataSource == null) { + throw new SQLException("datasource do not exist, name: " + dbName); + } + + return dataSourceMap.get(dbName).getConnection(); + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackService.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackService.java new file mode 100644 index 000000000..19ec2793a --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackService.java @@ -0,0 +1,16 @@ +package com.codingapi.tx.datasource.relational.txc.rollback; + + +import com.codingapi.tx.datasource.relational.txc.parser.CommitInfo; + +/** + * @author jsy. + */ +public interface TxcRollbackService { + + + /**执行回滚 + * @param commitInfo + */ + void rollback(CommitInfo commitInfo); +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackServiceImpl.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackServiceImpl.java new file mode 100644 index 000000000..bae64aa7b --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackServiceImpl.java @@ -0,0 +1,54 @@ +package com.codingapi.tx.datasource.relational.txc.rollback; + +import com.codingapi.tx.datasource.relational.txc.parser.CommitInfo; +import com.codingapi.tx.datasource.relational.txc.parser.SQLType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * @author jsy. + */ +@Component +public class TxcRollbackServiceImpl implements TxcRollbackService { + + private Logger logger = LoggerFactory.getLogger(TxcRollbackServiceImpl.class); + + @Autowired + private TxcRollbackDataSource rollbackDataSource; + + @Override + public void rollback(CommitInfo commitInfo) { + // 每次需要新获取一个连接 + Connection connection = null; + try { + connection = rollbackDataSource.getConnectionByDbName(commitInfo.getSchemaName()); + if (commitInfo.getSqlType() == SQLType.UPDATE) { + UpdateRollback.getInstance().rollback(commitInfo, connection); + } + + if (commitInfo.getSqlType() == SQLType.INSERT) { + InsertRollback.getInstance().rollback(commitInfo, connection); + } + + if (commitInfo.getSqlType() == SQLType.DELETE) { + DeleteRollback.getInstance().rollback(commitInfo, connection); + } + } catch (Exception e) { + logger.error("rollback error, sql:{}", commitInfo.getSql(), e); + } finally { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + logger.error("close error", e); + } + } + + } +} diff --git a/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/UpdateRollback.java b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/UpdateRollback.java new file mode 100644 index 000000000..5785ff892 --- /dev/null +++ b/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/UpdateRollback.java @@ -0,0 +1,109 @@ +package com.codingapi.tx.datasource.relational.txc.rollback; + +import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; +import com.codingapi.tx.datasource.relational.txc.parser.CommitInfo; +import com.codingapi.tx.datasource.relational.txc.parser.TxcField; +import com.codingapi.tx.datasource.relational.txc.parser.TxcLine; +import com.codingapi.tx.datasource.relational.txc.parser.TxcTable; +import com.codingapi.tx.datasource.relational.txc.parser.UpdateParser; +import com.google.common.collect.Lists; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jsy. + */ + +public class UpdateRollback extends AbstractRollback{ + private Logger logger = LoggerFactory.getLogger(UpdateRollback.class); + + + private static UpdateRollback instance = null; + + public static UpdateRollback getInstance() { + if (instance == null) { + synchronized (UpdateRollback.class) { + if (instance == null) { + instance = new UpdateRollback(); + } + } + } + return instance; + } + + + + + @Override + protected List assembleRollbackSql(CommitInfo commitInfo, Connection connection) + throws SQLException { + + ArrayList preparedStatements = Lists.newArrayList(); + String tableName = commitInfo.getOriginalValue().getTableName(); + + for (TxcLine txcLine : commitInfo.getOriginalValue().getLine()) { + List txcFields = txcLine.getFields(); + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("update ").append(tableName).append(" ").append("set "); + + for (int i = 0; i < txcFields.size(); i++) { + if (i == txcFields.size() - 1) { + stringBuilder.append(txcFields.get(i).getSqlName()).append("=").append("?"); + } else { + stringBuilder.append(txcFields.get(i).getSqlName()).append("=").append("?").append(","); + } + } + String sql = stringBuilder.append(" where ").append(txcLine.getPrimaryKey()).append("=?").toString(); + PreparedStatement preparedStatement = connection.prepareStatement(sql); + + for (int j = 1; j <= txcFields.size(); j++) { + preparedStatement.setObject(j, txcFields.get(j - 1).getValue()); + } + + preparedStatement.setObject(1 + txcFields.size(), txcLine.getPrimaryValue()); + preparedStatements.add(preparedStatement); + } + + return preparedStatements; + + } + + @Override + protected boolean canRollback(CommitInfo commitInfo, Connection connection) throws SQLException { + String sql = commitInfo.getSql(); + SQLUpdateStatement sqlParseStatement = (SQLUpdateStatement) new MySqlStatementParser(sql).parseStatement(); + + TxcTable dbValue = UpdateParser.getInstance() + .getOriginValue(commitInfo.getWhereParams(), sqlParseStatement, connection); + + if ( commitInfo.getOriginalValue().getLine().size() == 0) { + logger.error("未影响行数,不回滚"); + return false; + } + + for (TxcLine txcLine : dbValue.getLine()) { + boolean diff = DiffUtils.diff(commitInfo.getPresentValue().getLine().get(0), txcLine); + if (!diff) { + try { + logger.error("数据不一致,不支持回滚操作, before:{},after:{}", + DiffUtils.getObjectMapper().writeValueAsString(commitInfo.getPresentValue().getLine().get(0)), + DiffUtils.getObjectMapper().writeValueAsString(txcLine)); + } catch (Exception e) { + logger.error("error", e); + } + return false; + } + } + + return true; + } + +}