Spring Cloud实战 - TCC分布式事务

本文通过使用Spring Cloud和Docker构建了一个常见的Microservice体系.

Spring Cloud为开发者提供了快速构建分布式系统中的一些常见工具, 如分布式配置中心, 服务发现与注册中心, 智能路由, 服务熔断及降级, 消息总线等.

而Spring Cloud Sleuth为Spring Cloud提供了分布式追踪方案, 可视化地分析服务调用链路和服务间的依赖关系

本次实战以模拟下单流程作为实战演示, 使用Try-Confirm-Cancel即TCC模式为分布式事务提供最终一致性.

完整的代码示例已经上传至Github, 喜欢的话可以给我一颗star, 这样能激励我写出更好的文章

Try Confirm Cancel补偿模式

本实例遵循的是Atomikos公司对微服务的分布式事务所提出的RESTful TCC解决方案

RESTful TCC模式分3个阶段执行

  1. Trying阶段主要针对业务系统检测及作出预留资源请求, 若预留资源成功, 则返回确认资源的链接与过期时间
  2. Confirm阶段主要是对业务系统的预留资源作出确认, 要求TCC服务的提供方要对确认预留资源的接口实现幂等性, 若Confirm成功则返回204, 资源超时则证明已经被回收且返回404
  3. Cancel阶段主要是在业务执行错误或者预留资源超时后执行的资源释放操作, Cancel接口是一个可选操作, 因为要求TCC服务的提供方实现自动回收的功能, 所以即便是不认为进行Cancel, 系统也会自动回收资源

对RESTful TCC事务更为详细的解释可以点击这里进行阅读

系统结构

基础组件

Zuul Gateway

Zuul在本实例中仅作为路由所使用, 配置降低Ribbon的读取与连接超时上限

Eureka H.A.

多个对等Eureka节点组成高可用集群, 并将注册列表的自我保护的阈值适当降低

Config Server

  • 如果远程配置中有密文{cipher}*, 那么该密文的解密将会延迟至客户端启动的时候. 因此客户端需要配置AES的对称密钥encrypt.key, 并且客户端所使用的JRE需要安装Java 8 JCE, 否则将会抛出Illegal key size相关的异常.
    (本例中Docker Compose构建的容器已经安装了JCE, 如果远程配置文件没有使用{cipher}*也不必进行JCE的安装)

    spring:
      cloud:
        config:
          server:
            git:
              uri: 'https://git.oschina.net/witless/conf-repo.git'
              clone-on-start: true
            encrypt:
              enabled: false
      application:
        name: 'config-server'
    
  • 为了达到开箱即用, 选用公开仓库Github或者GitOsc

  • 本项目中有两个自定义注解
    @com.github.prontera.Delay 控制方法的延时返回时间

    @com.github.prontera.RandomlyThrowsException 随机抛出异常, 人为地制造异常

    默认的远程配置如下

    solar:
      delay:
        time-in-millseconds: 0
      exception:
        enabled: false
        factor: 7
    

    这些自定义配置正是控制方法返回的时延, 随机异常的因子等

    我在服务order, product, accounttcc中的所有Controller上都添加了以上两个注解, 当远程配置的更新时候, 可以手工刷新/refresh或通过webhook等方法自动刷新本地配置. 以达到模拟微服务繁忙或熔断等情况.

RabbitMQ

原本作为可靠性事件投递的Broker, 如今被TCC模式所替代. 可为日后的Spring Cloud Steam或Spring Cloud Bus的集成作为基础组件而保留

监控服务

Spring Boot Admin

此应用提供了管理Spring Boot服务的简单UI, 下图是在容器中运行时的服务健康检测页

Hystrix Dashboard

提供近实时依赖的统计和监控面板, 以监测服务的超时, 熔断, 拒绝, 降级等行为

Zipkin Server

Zipkin是一款开源的分布式实时数据追踪系统, 其主要功能是聚集来自各个异构系统的实时监控数据, 用来追踪微服务架构下的系统时延问题. 下图是对order服务的请求进行追踪的情况

业务服务

首次启动时通过Flyway自动初始化数据库

对spring cloud config server采用fail fast策略, 一旦远程配置服务无法连接则无法启动业务服务

account

用于获取用户信息, 用户注册, 修改用户余额, 预留余额资源, 确认预留余额, 撤销预留余额

product

用于获取产品信息, 变更商品库存, 预留库存资源, 确认预留库存, 撤销预留库存

tcc coordinator

TCC资源协调器, 其职责如下

  • 对所有参与者发起Confirm请求
  • 无论是协调器发生的错误还是调用参与者所产生的错误, 协调器都必须有自动恢复重试功能, 尤其是在确认的阶段, 以防止网络抖动的情况

order

order服务是本项目的入口, 尽管所提供的功能很简单

  • 下单. 即生成预订单, 为了更好地测试TCC功能, 在下单时就通过Feign向服务accountproduct发起预留资源请求, 并且记录入库
  • 确认订单. 确认订单时根据订单ID从库中获取订单, 并获取预留资源确认的URI, 交由服务tcc统一进行确认, 如果发生冲突即记录入库, 等待人工处理

与其他服务进行通讯, 我们选择使用Feign

/**
 * @author Zhao Junjian
 */
@FeignClient(name = TccClient.SERVICE_ID, fallback = TccClientFallback.class)
public interface TccClient {
    /**
     * eureka service name
     */
    String SERVICE_ID = "tcc";
    /**
     * api prefix
     */
    String API_PATH = "/api/v1/coordinator";

    @RequestMapping(value = API_PATH + "/confirmation", method = RequestMethod.PUT, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE})
    void confirm(@RequestBody TccRequest request);

    @RequestMapping(value = API_PATH + "/cancellation", method = RequestMethod.PUT, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE})
    void cancel(@RequestBody TccRequest request);

}

Swagger UI

Swagger的目标是为REST APIs 定义一个标准的, 与语言无关的接口, 使人和计算机在看不到源码或者看不到文档或者不能通过网络流量检测的情况下能发现和理解各种服务的功能. 当服务通过Swagger定义, 消费者就能与远程的服务互动通过少量的实现逻辑. 类似于低级编程接口, Swagger去掉了调用服务时的很多猜测.

运行

Docker Compose运行

在项目根路径下执行脚本build.sh, 该脚本会执行Maven的打包操作, 并会迭代目录下的*-compose.yml进行容器构建

构建完成后需要按照指定的顺序启动

  1. 启动MySQL, RabbitMQ等基础组件

    ➜  solar git:(feature/cleanup) ✗ docker-compose -f infrastructure-compose.yml up -d
    
  2. 启动Eureka Server与Config Server

    ➜  solar git:(feature/cleanup) ✗ docker-compose -f basic-ms-compose.yml up -d
    
  3. 启动监控服务

    ➜  solar git:(feature/cleanup) ✗ docker-compose -f monitor-ms-compose.yml up -d
    
  4. 启动业务服务

    ➜  solar git:(feature/cleanup) ✗ docker-compose -f business-ms-compose.yml up -d
    

IDE运行

因为程序本身按照Docker启动, 所以对于hostname需要在hosts文件中设置正确才能正常运行

## solar
127.0.0.1 eureka1
127.0.0.1 eureka2
127.0.0.1 rabbitmq
127.0.0.1 zipkin_server
127.0.0.1 solar_mysql
127.0.0.1 gitlab

根据依赖关系, 程序最好按照以下的顺序执行

docker mysql > docker rabbitmq > eureka server > config server > zipkin server > 其他微服务

示例

根据附表中的服务字典, 我们通过Zuul或Swagge对order服务进行预订单生成操作

POST http://localhost:7291/order/api/v1/orders
Content-Type: application/json;charset=UTF-8

{
  "product_id": 7,
  "user_id": 1
}

成功后我们将得到预订单的结果

{
  "data": {
    "id": 15,
    "create_time": "2017-03-28T18:18:02.206+08:00",
    "update_time": "1970-01-01T00:00:00+08:00",
    "delete_time": "1970-01-01T00:00:00+08:00",
    "user_id": 1,
    "product_id": 7,
    "price": 14,
    "status": "PROCESSING"
  },
  "code": 20000
}

此时我们再确认订单

(如果想测试预留资源的补偿情况, 那么就等15s后过期再发请求, 注意容器与宿主机的时间)

POST http://localhost:7291/order/api/v1/orders/confirmation
Content-Type: application/json;charset=UTF-8

{
  "order_id": 15
}

如果成功确认则返回如下结果

{
  "data": {
    "id": 15,
    "create_time": "2017-03-28T18:18:02.206+08:00",
    "update_time": "2017-03-28T18:21:32.78+08:00",
    "delete_time": "1970-01-01T00:00:00+08:00",
    "user_id": 1,
    "product_id": 7,
    "price": 14,
    "status": "DONE"
  },
  "code": 20000
}

至此就完成了一次TCC事务, 当然你也可以测试超时和冲突的情况, 这里就不再赘述

拓展

使用Gitlab作为远程配置仓库

本例中默认使用Github或GitOsc中的公开仓库, 出于自定义的需要, 我们可以在本地构建Git仓库, 这里选用Gitlab为例.

将以下配置添加至docker compose中的文件中并启动Docker Gitlab容器

gitlab:
    image: daocloud.io/daocloud/gitlab:8.16.7-ce.0
    ports:
        - "10222:22"
        - "80:80"
        - "10443:443"
    volumes:
        - "./docker-gitlab/config/:/etc/gitlab/"
        - "./docker-gitlab/logs/:/var/log/gitlab/"
        - "./docker-gitlab/data/:/var/opt/gitlab/"
    environment:
        - TZ=Asia/Shanghai

将项目的config-repo添加至Gitlab中, 并修改config-ms中git仓库的相关验证等参数即可

结语

更为详细的说明及代码示例已经上传至Github

https://github.com/prontera/spring-cloud-rest-tcc

该项目是采用目前比较流行的SpringBoot/SpringCloud构建微服务电商项目,项目叫 《果然新鲜》,实现一套串联的微服务电商项目。完全符合一线城市微服务电商的需求,对学习微服务电商架构,有非常大的帮助,该项目涵盖从微服务电商需求讨论、数据库设计、技术选型、互联网安全架构、整合SpringCloud各自组件、分布式基础设施等实现一套完整的微服务解决方案。 项目使用分布式微服务框架,涉及后台管理员服务、地址服务、物流服务、广告服务、商品服务、商品类别服务、品牌服务、订单服务 、购物车服务、首页频道服务、公告服务、留言服务、搜索服务、会员服务等。  系统架构图   SpringBoot+SpringCloud+SSM构建微服务电商项目使用SpringCloud Eureka作为注册中心,实现服务治理使用Zuul网关框架管理服务请求入口使用Ribbon实现本地负载均衡器和Feign HTTP客户端调用工具使用Hystrix服务保护框架(服务降、隔离、熔断、限流)使用消息总线Stream RabbitMQ和 Kafka微服务API接口安全控制和单点登录系统CAS+JWT+OAuth2.0分布式基础设施构建分布式任务调度平台XXL-JOB分布式日志采集系统ELK分布式事务解决方案LCN分布式锁解决方案Zookeeper、Redis分布式配置中心(携程Apollo)高并发分布式全局ID生成(雪花算法)分布式Session框架Spring-Session分布式服务追踪与调用链Zipkin项目运营与部署环境分布式设施环境,统一采用Docker安装使用jenkins+docker+k8s实现自动部署微服务API管理ApiSwagger使用GitLab代码管理(GitHub  GitEE)统一采用第三方云数据库使用七牛云服务器对静态资源实现加速 开发环境要求JDK统一要求:JDK1.8Maven统一管理依赖 统一采用Docker环境部署编码统一采用UTF-8开发工具IDEA 或者 Eclipse 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值