java:spring cloud使用tcc-transaction实现分布式事务

news2024/11/24 22:26:48

# 安装tcc-transaction server和dashboard

参考这篇文章【https://changmingxie.github.io/zh-cn/docs/ops/server/deploy-alone.html】里面有mysql的建表脚本,先将数据库建好。
下载tcc-transaction

cd /chz/install/tcc-transaction
wget https://github.com/changmingxie/tcc-transaction/releases/download/2.1.0/tcc-transaction-server.zip
wget https://github.com/changmingxie/tcc-transaction/releases/download/2.1.0/tcc-transaction-dashboard.zip

解压

unzip tcc-transaction-server.zip -d server
unzip tcc-transaction-dashboard.zip -d dashboard

配置tcc-transaction-server

cd /chz/install/tcc-transaction/server
vim application.properties
<<<< 内容如下:
server:
  port: 12332
  servlet:
    context-path: /${spring.application.name}
spring:
  application:
    name: tcc-transaction-server
  tcc:
    storage:
      storage-type: memory
      max-attempts: 1
    recovery:
      quartz-clustered: true
      quartz-data-source-url: jdbc:mysql://192.168.44.228:3306/TCC_SERVER?useSSL=false&allowPublicKeyRetrieval=true
      quartz-data-source-driver: com.mysql.jdbc.Driver
      quartz-data-source-user: root
      quartz-data-source-password: root
    registry:
      registry-types:
      - direct
      cluster-name: default
    remoting:
      listen-port: 2332
      request-process-thread-queue-capacity: 1024
logging:
  level:
    root: info
>>>>

启动tcc-transaction-server

cd /chz/install/tcc-transaction/server
bin/startup.sh

配置tcc-transaction-dashboard

cd /chz/install/tcc-transaction/dashboard
vim application.properties
<<<< 修改后的内容如下
server:
  servlet:
    context-path: /${spring.application.name}
  port: 22332
logging:
  level:
    root: info
spring:
  application:
    name: tcc-transaction-dashboard
  resources:
    static-locations: classpath:templates/
    chain:
      cache: false
  freemarker:
    enabled: true
    cache: false
    charset: UTF-8
    suffix: .html
    check-template-location: true
    template-loader-path: classpath:/templates/
  tcc:
    dashboard:
      userName: admin
      password: 123456
      connection-mode: server
    registry:
      registry-type: direct
      registry-role: dashboard
      direct:
        server-addresses-for-dashboard: 192.168.44.228:12332
    recovery:
      recovery-enabled: false
      quartz-clustered: true
      quartz-data-source-url: jdbc:mysql://192.168.44.228:3306/TCC_SERVER?useSSL=false&allowPublicKeyRetrieval=true
      quartz-data-source-driver: com.mysql.jdbc.Driver
      quartz-data-source-user: root
      quartz-data-source-password: root
feign:
  path: /tcc-transaction-server
>>>>

配置tcc-transaction-dashboard的启动文件

cd /chz/install/tcc-transaction/dashboard
vim bin/startup.sh
<<<< 在文件里面【JAVA_OPT】附件添加这几句
JAVA_OPT="${JAVA_OPT} --add-opens=java.base/java.lang=ALL-UNNAMED"
JAVA_OPT="${JAVA_OPT} --add-opens=java.base/java.util=ALL-UNNAMED"
JAVA_OPT="${JAVA_OPT} --add-opens=java.base/java.text=ALL-UNNAMED"
>>>>

启动tcc-transaction-dashboard

cd /chz/install/tcc-transaction/dashboard
bin/startup.sh

访问【http://192.168.44.228:22332/tcc-transaction-dashboard】试试,用户名和密码是【admin/123456】

# 下面开始写测试程序

【pom.xml】

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.3.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>2.3.12.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.mengyun</groupId>
    <artifactId>tcc-transaction-http</artifactId>
    <version>2.1.0</version>
</dependency>
<dependency>
    <groupId>org.mengyun</groupId>
    <artifactId>tcc-transaction-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>1.4.1</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.11</version>
</dependency>

【application.yaml】

server.port: 8082
spring.application.name: myTcc
server.servlet.context-path: /myTcc

spring.main.allow-bean-definition-overriding: true

spring.tcc.storage.storage-type: remoting
spring.tcc.storage.domain: TCC:FEIGN:CAPITAL

spring.tcc.recovery.recovery-enabled: false
spring.tcc.registry.registry-type: direct
spring.tcc.registry.cluster-name: default
spring.tcc.registry.direct.server-addresses: 192.168.44.228:2332

【logback.xml】

<configuration debug="false" xmlns="http://ch.qos.logback/xml/ns/logback"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback
               https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level ::: %msg%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

【Test1Controller.java】

package com.chz.myTcc.controller;

@Slf4j
@RestController
@RequestMapping("/test1")
public class Test1Controller {

    @Autowired
    private Test2FeignClient test2FeignClient;

    @Autowired
    private Test3FeignClient test3FeignClient;

    @RequestMapping(value = "/test1Success", method = RequestMethod.GET)
    @Compensable(confirmMethod = "testConfirm", cancelMethod = "testCancel")
    public String test1Success(@RequestParam("p1")String p1)
    {
        // 这个方法是测试全部正常的情况
        log.info("Test1Controller::test1Success");
        test2FeignClient.test1Success(p1);
        test3FeignClient.test1Success(p1);
        return "test1Success: success";
    }

    @RequestMapping(value = "/test1Fail", method = RequestMethod.GET)
    @Compensable(confirmMethod = "testConfirm", cancelMethod = "testCancel")
    public String test1Fail(@RequestParam("p1")String p1)
    {
        // 这个方法测试子调用全部正常,本方法抛出异常会不会全部回滚
        log.info("Test1Controller::test1Fail");
        test2FeignClient.test1Success(p1);
        test3FeignClient.test1Success(p1);
        throw new RuntimeException("test1Fail");
    }

    @RequestMapping(value = "/test2Fail", method = RequestMethod.GET)
    @Compensable(confirmMethod = "testConfirm", cancelMethod = "testCancel")
    public String test2Fail(@RequestParam("p1")String p1)
    {
        // 本方法测试子调用异常,但本方法忽略异常的情况下子方法会做何处理
        log.info("Test1Controller::test2Fail");
        test2FeignClient.test1Success(p1);
        test3FeignClient.test1Fail(p1);
        return "test2Fail: success";
    }

    public void testConfirm(@RequestParam("p1")String p1)
    {
        log.info("Test1Controller::testConfirm");
    }

    public void testCancel(@RequestParam("p1")String p1)
    {
        log.info("Test1Controller::testCancel");
    }
}

【Test2Controller.java】

package com.chz.myTcc.controller;

@Slf4j
@RestController
@RequestMapping("/test2")
public class Test2Controller
{
    @RequestMapping(value = "/test1Success", method = RequestMethod.GET)
    @Compensable(confirmMethod = "testConfirm", cancelMethod = "testCancel")
    public String test1Success(@RequestParam("p1")String p1)
    {
        log.info("Test2Controller::test1Success");
        return "test1: success";
    }

    @RequestMapping(value = "/test1Fail", method = RequestMethod.GET)
    @Compensable(confirmMethod = "testConfirm", cancelMethod = "testCancel")
    public String test1Fail(@RequestParam("p1")String p1)
    {
        log.info("Test2Controller::test1Fail");
        throw new RuntimeException("test1Fail");
    }

    public void testConfirm(@RequestParam("p1")String p1)
    {
        log.info("Test2Controller::testConfirm");
    }

    public void testCancel(@RequestParam("p1")String p1)
    {
        log.info("Test2Controller::testCancel");
    }
}

【Test3Controller.java】

package com.chz.myTcc.controller;

@Slf4j
@RestController
@RequestMapping("/test3")
public class Test3Controller
{
    @RequestMapping(value = "/test1Success", method = RequestMethod.GET)
    @Compensable(confirmMethod = "testConfirm", cancelMethod = "testCancel")
    public String test1Success(@RequestParam("p1")String p1)
    {
        log.info("Test3Controller::test1Success");
        return "test1: success";
    }

    @RequestMapping(value = "/test1Fail", method = RequestMethod.GET)
    @Compensable(confirmMethod = "testConfirm", cancelMethod = "testCancel")
    public String test1Fail(@RequestParam("p1")String p1)
    {
        log.info("Test3Controller::test1Fail");
        throw new RuntimeException("test1Fail");
    }

    public void testConfirm(@RequestParam("p1")String p1)
    {
        log.info("Test3Controller::testConfirm");
    }

    public void testCancel(@RequestParam("p1")String p1)
    {
        log.info("Test3Controller::testCancel");
    }
}

【MyExceptionHandler.java】

package com.chz.myTcc.exceptions;

@Slf4j
@RestControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(Exception.class)
    public String handleException(Exception e){
        log.error("MyExceptionHandler::handleException: " + e.getMessage());
        return e.getClass().getSimpleName() + ": " + e.getMessage();
    }
}

【Test2FeignClient.java】

package com.chz.myTcc.feign;

@FeignClient(contextId = "Test2FeignClient", name = "myTcc", url = "http://localhost:8082/myTcc/test2")
public interface Test2FeignClient {

    @EnableTcc
    @RequestMapping(value = "/test1Success", method = RequestMethod.GET)
    String test1Success(@RequestParam("p1")String p1);

    @EnableTcc
    @RequestMapping(value = "/test1Fail", method = RequestMethod.GET)
    String test1Fail(@RequestParam("p1")String p1);
}

注意上面是【@EnableTcc】非常重要,这是将主方法与子调用合并成一个事务进行处理的关键。
【Test3FeignClient.java】

package com.chz.myTcc.feign;

@FeignClient(contextId = "Test3FeignClient", name = "myTcc", url = "http://localhost:8082/myTcc/test3")
public interface Test3FeignClient {

    @EnableTcc
    @RequestMapping(value = "/test1Success", method = RequestMethod.GET)
    String test1Success(@RequestParam("p1")String p1);

    @EnableTcc
    @RequestMapping(value = "/test1Fail", method = RequestMethod.GET)
    String test1Fail(@RequestParam("p1")String p1);
}

注意上面是【@EnableTcc】非常重要,这是将主方法与子调用合并成一个事务进行处理的关键。
【MyTccTest.java】

package com.chz.myTcc;

@SpringBootApplication
@EnableFeignClients
public class MyTccTest {
    public static void main(String[] args) {
        SpringApplication.run(MyTccTest.class, args);
    }
}

启动【MyTccTest.java】

# 下面开始测试

【测试1】:访问【http://localhost:8082/myTcc/test1/test1Success?p1=1】,查看日志如下:
在这里插入图片描述
很正常,跟预期的一样

【测试2】:访问【http://localhost:8082/myTcc/test1/test1Fail?p1=1】,查看日志如下:
在这里插入图片描述
虽然【Test1Controller::test2Fail】调用【Test2Controller::test1Success】和【Test3Controller::test1Success】都成功了,但是【Test1Controller::test1Fail】的最后一句主动抛了Exception,所以三个testCancel被触发全部进行了回滚。

【测试3】:访问【http://localhost:8082/myTcc/test1/test2Fail?p1=1】,查看日志如下:
在这里插入图片描述
虽然【Test1Controller::test1Fail】在访问【Test3Controller::test1Fail】的时候【Test3Controller::test1Fail】抛出了Exception,但是因为【Test1Controller::test1Fail】忽略了该异常,所以最后3个testConfirm被触发全部提交。
正常情况下是不会这样写代码的,正常情况下【Test3Controller::test1Fail】抛出了异常之后【Test1Controller::test1Fail】也是要抛出异常回滚全部事务的。代码这样写只是为了测试而已

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1797574.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

webgl_framebuffer_texture

ThreeJS 官方案例学习&#xff08;webgl_framebuffer_texture&#xff09; 1.效果图 2.源码 <template><div><div id"container"></div><div id"selection"><div></div></div></div> </templa…

嵌入式Linux系统编程 — 2.3 标准I/O库:格式化I/O

目录 1 格式化I/O简介 2 格式化输出 2.1 格式化输出函数简介 2.2 格式控制字符串 format 2.3 示例程序 3 格式化输入 3.1 格式化输入简介 3.2 格式控制字符串 format 3.3 示例程序 1 格式化I/O简介 在先前示例代码中&#xff0c;经常使用库函数 printf() 来输出程序中…

操作系统教材第6版——个人笔记6

3.3.4 页面调度 页面调度 当主存空间已满而又需要装入新页时&#xff0c;页式虚拟存储管理必须按照一定的算法把已在主存的一些页调出去 #主存满加新&#xff0c;把已在主存一些页调出选择淘汰页的工作称为页面调度 选择淘汰页的算法称为页面调度算法 页面调度算法设计不当&a…

【递归、搜索与回溯】递归、搜索与回溯准备+递归主题

递归、搜索与回溯准备递归主题 1.递归2.搜索3.回溯与剪枝4.汉诺塔问题5.合并两个有序链表6.反转链表7.两两交换链表中的节点8.Pow(x, n)-快速幂&#xff08;medium&#xff09; 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你…

解决Windows Hosts 文件因为权限无法修改的问题

如何修改 Windows Hosts 文件并添加域名映射 在日常工作中&#xff0c;可能需要修改 Windows 的 hosts 文件&#xff0c;以将特定的域名映射到指定的 IP 地址。本文介绍三种方法来完成这一任务&#xff1a;直接手动编辑 hosts 文件&#xff0c;使用批处理文件自动完成任务&…

哈默纳科Harmonic谐波减速机应用领域有哪些

在制造设备中&#xff0c;精确控制速度与位置的需求日益凸显&#xff0c;这为谐波减速机的广泛应用提供了广阔的舞台。哈默纳科Harmonic谐波减速机以结构紧凑、高精度、高刚度、高可靠性、便于安装维护等优势&#xff0c;在工业机器人和自动化系统中发挥着举足轻重的作用。 一、…

如何调用地方天地图?

我们在《如何申请自己的专属天地图&#xff1f;》一文中&#xff0c;为大家分享了如果申请专属天地图&#xff0c;并在水经微图&#xff08;以下简称“微图”&#xff09;中加载的具体方法。 于是&#xff0c;就有朋友问如何调地方用天地图。 现在&#xff0c;我们就以四川地…

六位一线AI工程师总结大模型应用摸爬滚打一年的心得,网友:全程高能!

六位一线AI工程师和创业者&#xff0c;把在大模型应用开发上摸爬滚打一整年的心得&#xff0c;全&#xff01;分&#xff01;享&#xff01;了&#xff01; &#xff08;奇怪的六一儿童节大礼包出现了&#xff09; 这篇干货长文&#xff0c;一时间成为开发者社区热议的话题。…

2024年几款优秀的SQL IDE优缺点分析

SQL 工具在数据库管理、查询优化和数据分析中扮演着重要角色。 以下是常见的 SQL 工具及其优缺点。 1. SQLynx 优点&#xff1a; 智能代码补全和建议&#xff1a;采用AI技术提供高级代码补全、智能建议和自动错误检测&#xff0c;大幅提高编写和调试SQL查询的效率。跨平台和…

蓝桥杯物联网竞赛_STM32L071_19_输出方波信号(PWM)

国赛考了一个方波&#xff0c;第一次考这个&#xff0c;连个示波器都没有 CUBMX配置&#xff1a; 按上述配置刚好是32MHZ / 32 / 100 10KHZ 理论&#xff1a; 频率&#xff1a;就是一秒钟能产生多少个脉冲&#xff0c;如下图: 这算是一个脉冲&#xff0c;1KHZ说明一秒钟产生…

源码发布Quantlab4.2,Deap因子挖掘|gplearn做不到的咱们也能做。(代码+数据)

原创文章第552篇&#xff0c;专注“AI量化投资、世界运行的规律、个人成长与财富自由"。 又到了星球发布代码的日子&#xff1a; 更新说明&#xff1a;1、Deap做因子挖掘的框架使用。值得说明的是&#xff0c;源码级别&#xff0c;并非产品级&#xff0c;不能指望输入一堆…

降噪是什么意思?视频如何降噪?一文了解全部

在视频制作的过程中&#xff0c;我们经常会遇到噪音问题&#xff0c;这些噪音可能来自拍摄环境、录制设备或其他源头。然而&#xff0c;对于初学者来说&#xff0c;降噪是什么意思&#xff0c;以及如何有效地在视频中进行降噪可能是一些疑惑的问题。本文将深入解释降噪的概念&a…

Mat的lambda方式像素高效遍历(C++11)

Mat的lambda方式像素高效遍历&#xff08;C11&#xff09; 文章目录 Mat的lambda方式像素高效遍历&#xff08;C11&#xff09;前言一、Mat的lambda方式像素高效遍历二、代码实现总结 前言 图像遍历是图像处理中的经典操作&#xff0c;快速高效的进行像素遍历对性能的提升至关…

[AI OpenAI] 提取GPT-4中的概念

总结&#xff1a; 研究人员采用新的可扩展方法&#xff0c;将GPT-4的内部表示分解为1600万个通常可解释的模式&#xff0c;这些模式被称为“特征”&#xff0c;目的是提高语言模型的透明度和可解释性。通过使用稀疏自编码器&#xff0c;研究人员能够识别与特定概念相关的特征&…

《今日科技》是什么级别的期刊?是正规期刊吗?能评职称吗?

问题解答 问&#xff1a;《今日科技》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊 问&#xff1a;《今日科技》是什么级别的&#xff1f; 答&#xff1a;省级。主管单位&#xff1a;浙江省科学技术厅 主办单位&#xff1a;浙江省…

免费分享一套SpringBoot+Vue校园论坛(微博)系统【论文+源码+SQL脚本】,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue校园论坛(微博)系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue校园论坛(微博)系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue校园论坛(微博)系统 Java毕业设计…

Cesium401 (Unauthorized)https://api.cesium.com/v1/assets/2/endpoint未授权问题

目录 前言1.原因分析2.解决问题1.禁用默认的imageryProvider2.禁用图层切换3.移除所有默认图层4.使用自己的地形(可选) 3.最终解决方案4.总结 前言 在初始化Cesium的Viewer以后&#xff0c;Viewer会自动去访问Cesium官网的资源&#xff0c;如果访问不到官网的资源&#xff0c;就…

parseInt函数

貌似遇到问题了&#xff0c;在Java中&#xff0c;parseInt方法是java.lang.Integer类的一个静态方法&#xff0c;它用来将字符串转换为基本数据类型int。如果字符串不能被解析为有效的整数&#xff0c;parseInt会抛出一个NumberFormatException。 原来是取整串转换&#xff0c;…

Lab_ Exploiting an API endpoint using documentation

https://portswigger.net/web-security/learning-paths/api-testing/ 开局一个页面 打开代理和burp&#xff0c;查看功能点&#xff0c;然后看HTTPhistory 然后使用题目已知的账号去登录一下 My account 登录 然后发现 /api 路径 那么我们访问一下 URL/api &#xff0c;发现…

宋仕强论道 之 AI(人工智能)

《宋仕强论道 之 AI&#xff08;人工智能&#xff09;如何赋能新质生产力》于6月3日由新华瞭望网首发后&#xff0c;被环球科技新闻网等多家媒体转载&#xff1b;随后又分别以《宋仕强论道 之 AI如何赋能新质生产力》《宋仕强&#xff1a;AI赋能新质生产力的理论探索与实践路径…