2.分布式事务—下-灵析社区

没晒干的咸鱼

五、Seata 与 Spring Cloud 集成

如上图,Seata 与 Spring Cloud Alibaba 集成代码结构如上图所示。从代码上可以分为三大部分:rest、feign 和 web。AutoConfiguration 结尾的类是 @Configuration 类被 spring.factories 加载,负责创建 package 中所属的 bean。

rest

对应 restTemplate 调用,实现 ClientHttpRequestInterceptor 接口,将当前事务上下文包装到 HttpRequest header 中,加入到拦截器列表中。

feign

对应 openFeign 调用,这部分实现了事务上下文传递,与 Hystrix、Sentinel 、Ribbon 组件集成功能。需要特别注意是 Hystrix 中跨线程的事务上下文传递。这部分代码大量使用了Builder、Wrapper 模式,有兴趣的同学可深入阅读。

web

对应 Spring Web Servlet 中的处理,实现了 HandlerInterceptor 接口。在 preHandle 预处理中取 httpRequest header 中 Seata 的事务上下文并使用 API 绑定到当前线程的事务处理上下文中,这种后续的数据源操作就自动加入到了分布式事务的链路中;在 afterCompletion 中做了当前线程事务上下文的清除,防止事务上下文在线程中污染。

六、如何扩展一个 RPC 框架?

在上一节,我们讲到了 Seata 是如何与 Spring Cloud 相集成的。Seata 目前已经集成了 Spring Cloud、Alibaba Dubbo、Apache Dubbo、Motan、gRPC 和 sofa-RPC 等微服务调用。结合上一节与 Spring Cloud 的集成,扩展一个RPC 框架我们需要做哪些工作呢 ?主要可以分为两大部分:

事务上下文传递

Seata 的事务上下文目前主要包含:xid 和调用服务的分支事务类型。xid 为一个分布式事务的全局唯一标识,类似于 Tracing 中的 traceId,只有将 xid 传递下去才可能加入到分布式事务的链路中。调用服务的分支事务类型主要用于非 AT 模式,例如在一个 TCC 分支事务中不能再嵌套 AT 分支事务。主流的 RPC 框架大多是基于 TCP 协议之上的私有协议封装或者是基于 HTTP 协议。Seata 的事务上下文标识都是简单的字符串,序列化由 RPC 框架直接完成,大多数 RPC 框架实现了 filter 或者 interceptor 接口,通过将 Seata 的事务上下文填充到协议中的 attachment 字段或者 http header 中,就可以简单的完成事务上下文的传递。

事务上下文绑定和清除

在 RPC 的 provider 端收到 consumer 的请求,将事务上下文取出,通过 API 绑定到当前的执行线程中,这样后续的业务处理都纳入到了 Seata 的分布式事务链路中。执行完业务处理后,需要对绑定到当前线程的事务上下文清除掉,防止产生线程事务上下文污染。

具体 API 可参考官网文档:https://seata.io/zh-cn/docs/user/api.html

请大家思考一下如何去与 Dubbo 集成的呢?https://github.com/seata/seata/tree/develop/integration/dubbo

七、Seata 实战

本节将通过一个基于 Spring Cloud 的订单和库存服务进行 Seata 实战。

我们在 沙箱环境 里准备好了一套 seata 的实际应用案例,服务调用结构如下所示:

如图所示,服务链路中包含三个微服务:business(微服务入口)、order(订单服务)和 storage(库存服务)。

业务逻辑

通过 url 调用 business 服务,business 服务会通过 openFeign 的方式分别调用 storage 和 order 服务。每个服务调用后会根据正常(ok)或异常(fail)的返回值决定是否抛出异常,若返回结果不为 ok 那么 business 将抛出异常,触发整个事务回滚,预期数据至 business 方法执行前的数据并且库存总量和与初始值一致。当 order 和 storage 两个服务调用正常,用户可以根据 url 中的 mockException=true 或 false 来注入一个 mock 异常,当注入异常后,期望数据回滚至 business 方法执行前的数据并且库存总量和与初始值一致。

异常模拟

  1. 在 bussiness 中请求超过现有库存,通过参数 count 来指定要扣减的库存数量,当超出库存,将抛出异常进行事务回滚。
  2. 在 bussiness 中请求中加入 mockException=true 参数,触发事务回滚。

启动步骤

以下请求 url 中的 localhost 请根据实际值进行替换。

1.启动 order 和 storage 服务,最后启动 business 服务。服务的注册和发现,Seata 服务注册和发现 在这里使用了 Nacos 启动成功后,可以通过 Nacos 控制台的 sandbox-seata namespace 看到如下服务:

2.访问 business 的url :http://localhost:8080/seata/check 来检查初始化数据

3.通过访问 business 的url:http://localhost:8080/seata/feign?count=5&mockException=true 来触发业务逻辑:

其中 count 代表扣减库存的数量,当库存不足将触发事务回滚;mockException 代表是否触发业务异常,设置为 true 会触发业务异常,并通过分布式事务实现回滚。

4.每次操作完 business 请求后,访问 business 的 url :http://localhost:8080/seata/check 来检查操作后数据。

5.重复以上 3(根据实际需要请求,不必每次请求相同),4 步骤,最后再次通过访问 business 的 url :http://localhost:8080/seata/check 来检查结果数据:

使用总结

1.引入依赖。com.alibaba.cloud:spring-cloud-starter-alibaba-seata 中包含了 io.seata:seata-spring-boot-starter 依赖。若于期望依赖 seata 的版本不一致,可手动排除重引入。com.alibaba.cloud 原生 版本说明。

parent:

<dependencyManagement>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${alibaba.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
    </dependencyManagement>
    
    module:
     <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>
    </dependencies>

2.添加配置文件,具体配置项说明参考 此处。

seata:
   enabled: true
   application-id: business
   tx-service-group: my_test_tx_group
   config:
      type: nacos
      nacos:
         namespace: "sandbox-seata"
         serverAddr: 139.196.203.133:8848
         group: SEATA_GROUP
         username: xxx
         password: xxx
   registry:
      type: nacos
      nacos:
         application: seata-server
         serverAddr: 139.196.203.133:8848
         group: SEATA_GROUP
         namespace: "sandbox-seata"
         username: xxx
         password: xxx

3.在所有业务库中创建 undo_log 表,不同的数据库类型脚本参考 此处 ,示例中已自动完成创建。

4.在需要纳入分布式事务链路的入口 service 方法(保证可使 Spring 切面生效的位置亦可)上添加 @GlobalTransactional 注解。

八、其他

Seata 更多 sample 样例 ,请参考 样例

Seata 社区联系方式,请参见 联系我们

欢迎大家试用阿里云商业化产品 GTS,更高性能,更稳定,全面兼容 Seata。

阅读量:2019

点赞量:0

收藏量:0