基于Spring Cloud Netflix的TCC柔性事务和EDA事件驱动示例
Solar
Spring Cloud为开发者提供了快速构建分布式系统中的一些常见工具,如分布式配置中心,服务发现与注册中心,智能路由,服务熔断及降级,消息总线,分布式追踪的解决方案等。
本次实战以模拟下单流程为背景,结合Spring Cloud Netflix和分布式事务解决方案中Try Confirm Cancel模式与基于事件驱动的服务架构作为实战演示。
开发环境
- Docker 1.13.1
- Docker Compose 1.11.1
- Docker MySQL 5.7.17
- Docker RabbitMQ 3.6.6
- Java8 with JCE
- Spring Cloud Camden.SR6
系统结构
Try Confirm Cancel 补偿模式
本实例遵循的是Atomikos公司对微服务的分布式事务所提出的RESTful TCC解决方案。
RESTful TCC模式分3个阶段执行
- Trying阶段主要针对业务系统检测及作出预留资源请求,若预留资源成功,则返回确认资源的链接与过期时间。
- Confirm阶段主要是对业务系统的预留资源作出确认,要求TCC服务的提供方要对确认预留资源的接口实现幂等性,若Confirm成功则返回204,资源超时则证明已经被回收且返回404。
- Cancel阶段主要是在业务执行错误或者预留资源超时后执行的资源释放操作,Cancel接口是一个可选操作,因为要求TCC服务的提供方实现自动回收的功能,所以即便是不认为进行Cancel,系统也会自动回收资源。
Event Driven Architecture 基于事件驱动架构
本实例中的order-ms与membership-ms之间的通信是基于事件驱动的。当订单被成功创建并且付款成功之后,该订单的部分信息将会发往membership-ms以进行积分的增加。
从系统层面看,order-ms在EDA中是属于Publisher角色,自然而然地membership-ms就是Subscriber。
Publisher中的事件状态转换如下:
- NEW —> PENDING —> DONE
- NEW —> PENDING —> FAILED / NO_ROUTE / NOT_FOUND / ERROR
Subscriber中的事件状态转换如下:
- NEW —> DONE
- NEW —> FAILED / NOT_FOUND / ERROR
部分功能介绍:
- Publisher发送消息之前先将消息落地,目的是防止消息的错误发布(业务数据被回滚而消息却发布至Broker)。
- Publisher会周期性地扫描NEW状态的消息,并发布至Broker。
- 启用mandatory与publisher confirms机制,在消息被持久化至磁盘后将会收到basic.ack,此时可选择将消息转换为DONE或者是直接将其删除。
- Publisher将消息发布至Broker后会将其状态由NEW更新为PENDING,PENDING状态的事件将会由另一定时器扫描在当前时钟的3秒之前发布,但是却并未得到basic.ack的事件,并重新发布至Broker。意在消除在单实例的情况下因crash而导致消息状态丢失的边缘情况。
- Subscriber的消息幂等性。
基础组件
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的安装) - 为了达到开箱即用,选用公开仓库Github或者GitOsc。
- 本项目中有两个自定义注解
@com.github.prontera.Delay
控制方法的延时返回时间;@com.github.prontera.RandomlyThrowsException
随机抛出异常,人为地制造异常。
默认的远程配置如下
solar: delay: time-in-millseconds: 0 exception: enabled: false factor: 7
这些自定义配置正是控制方法返回的时延,随机异常的因子等。
我在服务order
,product
,account
和tcc
中的所有Controller上都添加了以上两个注解,当远程配置的更新时候,可以手工刷新/refresh
或通过webhook等方法自动刷新本地配置. 以达到模拟微服务繁忙或熔断等情况。
监控服务
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向服务
account
与product
发起预留资源请求,并且记录入库。 - 确认订单. 确认订单时根据订单ID从库中获取订单,并获取预留资源确认的URI,交由服务
tcc
统一进行确认,如果发生冲突即记录入库,等待人工处理。
membership
用于订单付款成功后,对下单用户的积分进行增加操作。该服务与订单服务是基于消息驱动以进行通信,达到事务的最终一致性。
Swagger UI
下图为product
服务的Swagger接口文档,根据下文的服务字典可知,本接口文档可通过http://localhost:8040/swagger-ui.html
进行访问. order
,account
和tcc
的文档访问方式亦是如出一撤。
运行
Docker Compose运行
在项目根路径下执行脚本build.sh
,该脚本会执行Maven的打包操作,并会迭代目录下的*-compose.yml
进行容器构建。
构建完成后需要按照指定的顺序启动,需要注意的一点是容器内服务的启动是需要备留预热时间,并非Docker容器启动后容器内的所有服务就能马上启动起来,要注意区分容器的启动和容器内的服务的启动,建议配合docker-compse logs来观察启动情况。而且容器之间的服务是有依赖的,如account-ms
和product-ms
此类业务服务的启动是会快速失败于config-ms
的失联。所以建议按照以下顺序启动Docker容器,并且在一组Docker容器服务完全启动后,再启动下一组的Docker容器。
- 启动MySQL,RabbitMQ等基础组件docker-compose -f infrastructure-compose.yml up -d
- 启动Eureka Server与Config Serverdocker-compose -f basic-ms-compose.yml up -d
- 启动监控服务docker-compose -f monitor-ms-compose.yml up -d
- 启动业务服务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 > 其他业务微服务(account-ms, product-ms, order-ms, tcc-coordinator-ms等)
示例
根据附表中的服务字典,我们通过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仓库的相关验证等参数即可。
服务字典
鉴于Spring Boot Actuator的端点所带来的两面性,除了可以增加spring-boot-starter-security
来获得强度较弱的HTTP Basic认证外,我们还可以修改management.port
和management.context-path
来提高攻击成本. 是的,我对每一个服务都修改了以上两个属性,并且兼容了Eureka Server,Hystrix Dashboard,Spring Boot Admin,使这些监控服务仍能正确工作. 因为对以上两个参数修改,我们的监控路径有所变化,如下表
原文:Java架构笔记
免费Java高级资料需要自己领取,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G。
传送门: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q
基于Spring Cloud Netflix的TCC柔性事务和EDA事件驱动示例的更多相关文章
- Spring Cloud Netflix概览和架构设计
Spring Cloud简介 Spring Cloud是基于Spring Boot的一整套实现微服务的框架.他提供了微服务开发所需的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策 ...
- Spring Cloud Netflix之Euraka Server注册中心
Spring Cloud简介 Spring Cloud是基于Spring Boot的一套实现微服务架构的生态组件.生态组件中包含Spring Cloud NetFlix,Spring Cloud Fe ...
- Spring Cloud netflix 概览和架构设计
pring Cloud是基于Spring Boot的一整套实现微服务的框架.他提供了微服务开发所需的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集群状态管理等 ...
- 基于Spring Cloud和Netflix OSS 构建微服务-Part 1
前一篇文章<微服务操作模型>中,我们定义了微服务使用的操作模型.这篇文章中,我们将开始使用Spring Cloud和Netflix OSS实现这一模型,包含核心部分:服务发现(Servic ...
- 基于Spring Cloud和Netflix OSS构建微服务,Part 2
在上一篇文章中,我们已使用Spring Cloud和Netflix OSS中的核心组件,如Eureka.Ribbon和Zuul,部分实现了操作模型(operations model),允许单独部署的微 ...
- Spring Cloud Alibaba | 微服务分布式事务之Seata
Spring Cloud Alibaba | 微服务分布式事务之Seata 本篇实战所使用Spring有关版本: SpringBoot:2.1.7.RELEASE Spring Cloud:Green ...
- 基于Spring cloud Ribbon和Eureka实现客户端负载均衡
前言 本案例将基于Spring cloud Ribbon和Eureka实现客户端负载均衡,其中Ribbon用于实现客户端负载均衡,Eureka主要是用于服务注册及发现: 传统的服务端负载均衡 常见的服 ...
- 基于Spring Cloud的微服务入门教程
(本教程的原地址发布在本人的简书上:http://www.jianshu.com/p/947d57d042e7,若各位看官有什么问题或不同看法请在这里或简书留言,谢谢!) 本人也是前段时间才开始接触S ...
- Spring Cloud Netflix Eureka源码导读与原理分析
Spring Cloud Netflix技术栈中,Eureka作为服务注册中心对整个微服务架构起着最核心的整合作用,因此对Eureka还是有很大的必要进行深入研究. 本文主要分为四部分,一是对项目构建 ...
随机推荐
- opencv::BackgroundSubtraction基本原理
背景消除 BS算法 - 图像分割(GMM – 高斯混合模型) - 机器学习(KNN –K个最近邻) BackgroundSubtractor (父类) - BackgroundSubtractorMO ...
- ABAP动态自建表维护程序Dynamin Process
以前经常会遇到批量上传或修改数据到自建表的需求,所以在想是否可以做一个动态的程序,所有的自建表都可以用这个动态程序来维护. 于是就打算试着写动态的程序. 程序的要求:动态显示自建表ALV 动态下载Ex ...
- 模仿UIApplication创建单例
UIApplicationMain: 1.创建UIApplication--应用程序唯一标识:可设置状态栏.识别联网状态.设置数字.打电话.发邮件.发短信.打开网页 2.创建UIApplication ...
- layui js 常用语句语法
烂笔头: layui组件使用 注意layui的版本. 在head里需要引入css/js文件. 出现 form.verify,form.val is not a function的错误信息时,注意版本, ...
- [20190909]完善vim的bccacl插件.txt
[20190909]完善vim的bccacl插件.txt http://blog.itpub.net/267265/viewspace-2140886/http://blog.itpub.net/26 ...
- 最短时间(几秒内)利用C#往SQLserver数据库一次性插入10万条数据
用途说明: 公司要求做一个数据导入程序,要求将Excel数据,大批量的导入到数据库中,尽量少的访问数据库,高性能的对数据库进行存储.于是在网上进行查找,发现了一个比较好的解决方案,就是采用SqlBul ...
- nfs共享文件系统
NFS服务简介 NFS 就是 Network FileSystem 的缩写,最早之前是由sun 这家公司所发展出来的. 它最大的功能就是可以透过网络,让不同的机器.不同的操作系统.可以彼此分享个别的档 ...
- 【CobaltStrike】对CobaltStrike内置功能模块的了解
对CobaltStrike内置功能模块的了解 0x00 右键功能列表 Interact 打开beacon Access dump hashes 获取hash Elevate 提权 Golden Tic ...
- Incorrect datetime value: '' for column 'examDate' at row 1
出问题的程序:user.setCreateTime(new Date()); 控制台图片一张,问题是:Incorrect datetime value: '' for column 'createTi ...
- thinkphp3.2生成二维码
public function createCode() { Vendor('phpqrcode.phpqrcode'); $object = new \QRcode(); $url = 'http: ...