This is an automated email from the ASF dual-hosted git repository. zhanglei pushed a commit to branch zhanglei-path-1 in repository https://gitbox.apache.org/repos/asf/servicecomb-website.git
The following commit(s) were added to refs/heads/zhanglei-path-1 by this push: new 41abd9f Update package name error in the document 41abd9f is described below commit 41abd9f85b59e3a264736ea02e10ab2b9ebbff71 Author: Lei Zhang <zhang...@apache.org> AuthorDate: Mon Oct 21 21:16:13 2019 +0800 Update package name error in the document --- _docs/cn/quick-start-dataconsistency.md | 920 ++++++++++++++-------------- _docs/quick-start-dataconsistency.md | 924 ++++++++++++++--------------- _posts/cn/2018-04-04-saga-pack-design.md | 4 +- _posts/cn/2018-04-28-saga_with_cucumber.md | 4 +- 4 files changed, 926 insertions(+), 926 deletions(-) diff --git a/_docs/cn/quick-start-dataconsistency.md b/_docs/cn/quick-start-dataconsistency.md index 8eca802..1f0e105 100755 --- a/_docs/cn/quick-start-dataconsistency.md +++ b/_docs/cn/quick-start-dataconsistency.md @@ -1,460 +1,460 @@ ---- -title: "数据一致性解决方案" -lang: cn -ref: quick-start-dataconsistency -permalink: /cn/docs/quick-start-dataconsistency/ -excerpt: "介绍ServiceComb的Saga数据一致性方案应用demo" -last_modified_at: 2017-09-19T11:50:10-04:00 ---- - -{% include toc %} -模拟一个简单的旅行应用,展示如何使用ServiceComb提供的Saga方案保证微服务间数据一致性。 - -旅行应用包含四个微服务: -- 机票预订服务(flight-booking-service) -- 租车服务(car-rental-service) -- 酒店预订服务(hotel-reservation-service) -- 支付服务(payment-service) - -机票预订、租车、酒店预订服务间无依赖关系,使用自己的数据库,通过HTTP协议通信。 -在以上三个服务的预订成功,支付完成后才能满足一个成功的行程,否则不能成行,Saga自动补偿。 - -![Saga-demo]({{ site.url }}{{ site.baseurl }}/assets/images/saga-demo.png) - -## 运行demo - -注:Demo 集成在 [ServiceComb-Saga](https://github.com/apache/servicecomb-saga) 项目中。 - -1. 准备环境 -- [Oracle JDK 1.8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) -- [Maven 3.x](https://maven.apache.org/install.html) -- [Docker](https://www.docker.com/get-docker) -- [MySQL](https://dev.mysql.com/downloads/) -- [Service Center](https://github.com/apache/servicecomb-service-center) -- [Docker compose](https://docs.docker.com/compose/install/) - -2. 配置服务 - 在各服务的 *microservice.yaml* 文件,设置该服务注册的服务中心 - - ```yaml - APPLICATION_ID: saga - service_description: - name: flight-booking-service - version: 0.0.1 - servicecomb: - service: - registry: - address: http://sc.servicecomb.io:30100 #此处选择使用ServiceComb的Service Center - rest: - address: 0.0.0.0:8080 - handler: - chain: - Consumer: - default: loadbalance - ``` - - 在 *saga-demo/docker-compose.yaml* 设置部署脚本 - ```yaml - version: '2.1' - - services: - service-center: #此处选择使用ServiceComb的Service Center容器镜像 - image: "servicecomb/service-center" - hostname: service-center - ports: - - "30100:30100" - - mysql: #此处选择使用5.7版本的mysql镜像 - image: "mysql/mysql-server:5.7" - hostname: mysql - environment: - - MYSQL_ROOT_PASSWORD=password - - MYSQL_DATABASE=saga - - MYSQL_USER=saga - - MYSQL_PASSWORD=password - ports: - - "3306:3306" - healthcheck: - test: ["CMD-SHELL", "nc -z localhost 3306 &> /dev/null; echo $$?"] - interval: 30s - timeout: 10s - retries: 5 - - car-rental-service: - image: "car-rental-service:0.0.2-SNAPSHOT" - hostname: car - links: - - "service-center:sc.servicecomb.io" - ports: - - "8080:8080" - - flight-booking-service: - image: "flight-booking-service:0.0.2-SNAPSHOT" - hostname: flight - links: - - "service-center:sc.servicecomb.io" - ports: - - "8081:8080" - - hotel-reservation-service: - image: "hotel-reservation-service:0.0.2-SNAPSHOT" - hostname: hotel - links: - - "service-center:sc.servicecomb.io" - ports: - - "8082:8080" - - payment-service: - image: "payment-service:0.0.2-SNAPSHOT" - hostname: payment - links: - - "service-center:sc.servicecomb.io" - ports: - - "8080" - - saga: - image: "saga-spring:0.0.2-SNAPSHOT" - hostname: saga - links: - - "mysql:mysql.servicecomb.io" - - "service-center:sc.servicecomb.io" - - "car-rental-service:car.servicecomb.io" - - "flight-booking-service:flight.servicecomb.io" - - "hotel-reservation-service:hotel.servicecomb.io" - - "payment-service:payment.servicecomb.io" - environment: - - JAVA_OPTS=-Dspring.profiles.active=prd,servicecomb -Dcse.service.registry.address=http://sc.servicecomb.io:30100 - ports: - - "8083:8080" - depends_on: - mysql: - condition: service_healthy - ``` - -3. 在Saga项目的根目录执行编译,制作Saga、机票预订、租车、酒店预订和支付服务镜像 - - ```bash - mvn package -DskipTests -Pdocker -Pdemo - ``` - -4. 在Saga项目的saga-demo目录通过docker-compose一键启动Saga、机票预订、租车、酒店预订和支付服务 - - ```bash - docker-compose up - ``` - -## 验证 - -1. 参照 [Saga API](https://github.com/apache/servicecomb-saga/blob/master/docs/api/api.md) 说明,设定各服务的事务、补偿、依赖和恢复参数,并保存为 *request.json* 文件 - - ```json - { - "policy": "BackwardRecovery", - "requests": [ - { - "id": "request-car", - "type": "rest", - "serviceName": "car-rental-service", - "transaction": { - "method": "post", - "path": "/rentals", - "params": { - "form": { - "customerId": "mike" - } - } - }, - "compensation": { - "method": "put", - "path": "/rentals", - "params": { - "form": { - "customerId": "mike" - } - } - } - }, - { - "id": "request-hotel", - "type": "rest", - "serviceName": "hotel-reservation-service", - "transaction": { - "method": "post", - "path": "/reservations", - "params": { - "form": { - "customerId": "mike" - } - } - }, - "compensation": { - "method": "put", - "path": "/reservations", - "params": { - "form": { - "customerId": "mike" - } - } - } - }, - { - "id": "request-flight", - "type": "rest", - "serviceName": "flight-booking-service", - "transaction": { - "method": "post", - "path": "/bookings", - "params": { - "form": { - "customerId": "mike" - } - } - }, - "compensation": { - "method": "put", - "path": "/bookings", - "params": { - "form": { - "customerId": "mike" - } - } - } - }, - { - "id": "request-payment", - "type": "rest", - "serviceName": "payment-service", - "parents": [ - "request-car", - "request-flight", - "request-hotel" - ], - "transaction": { - "method": "post", - "path": "/payments", - "params": { - "form": { - "customerId": "mike" - } - } - }, - "compensation": { - "method": "put", - "path": "/payments", - "params": { - "form": { - "customerId": "mike" - } - } - } - } - ] - } - ``` - -2. 发送请求到Saga - - ```bash - curl -XPOST -H "Content-Type: text/plain" -d @./request.json http://<localhost.ip:8083>/requests - ``` - - 获取处理结果成功(如果失败,返回相应的错误信息) - ```bash - success - ``` - -3. 查看Saga的事务 - - ```bash - curl -XGET http://<localhost.ip:8083>/events - ``` - - 查询结果 - - ```bash - { - "bcd27f0d-6b82-49b3-8067-b16eba970e55": [ - { - "id": 1, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:21Z", - "type": "SagaStartedEvent", - "contentJson": "{\"policy\": \"BackwardRecovery\", \"requests\": [{\"id\": \"request-car\", \"type\": \"rest\", \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-hotel\", \"type\": \"rest\", \"serviceName\": \"hotel-reservation-servic [...] - }, - { - "id": 2, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:21Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 3, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:21Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 4, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:21Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 5, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:22Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] - }, - { - "id": 6, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:24Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] - }, - { - "id": 7, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:25Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] - }, - { - "id": 8, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:25Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 9, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:25Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \" [...] - }, - { - "id": 10, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:25Z", - "type": "SagaEndedEvent", - "contentJson": "{}" - } - ] - } - ``` - -4. demo中模拟了支付服务帐户余额不足场景,发送请求超过一次将触发补偿操作。再次发送请求,可以在Saga日志中找到补偿事件 - - ```bash - { - "bcd27f0d-6b82-49b3-8067-b16eba970e55": [ - ... - ], - "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9": [ - { - "id": 11, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "SagaStartedEvent", - "contentJson": "{\"policy\": \"BackwardRecovery\", \"requests\": [{\"id\": \"request-car\", \"type\": \"rest\", \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-hotel\", \"type\": \"rest\", \"serviceName\": \"hotel-reservation-servic [...] - }, - { - "id": 12, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 13, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 14, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 15, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] - }, - { - "id": 16, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] - }, - { - "id": 17, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] - }, - { - "id": 18, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 19, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "TransactionAbortedEvent", - "contentJson": "{\"request\": {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \" [...] - }, - { - "id": 20, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "TransactionCompensatedEvent", - "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] - }, - { - "id": 21, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "TransactionCompensatedEvent", - "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] - }, - { - "id": 22, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "TransactionCompensatedEvent", - "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] - }, - { - "id": 23, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "SagaEndedEvent", - "contentJson": "{}" - } - ] - } - ``` - -## 下一步 - -* 了解更多[ServiceComb中的数据最终一致性方案 - part 1](/cn/docs/distributed_saga_1/) - -* 了解更多[ServiceComb中的数据最终一致性方案 - part 2](/cn/docs/distributed_saga_2/) - -* 了解更多[ServiceComb中的数据最终一致性方案 - part 3](/cn/docs/distributed_saga_3/) +--- +title: "数据一致性解决方案" +lang: cn +ref: quick-start-dataconsistency +permalink: /cn/docs/quick-start-dataconsistency/ +excerpt: "介绍ServiceComb的Saga数据一致性方案应用demo" +last_modified_at: 2017-09-19T11:50:10-04:00 +--- + +{% include toc %} +模拟一个简单的旅行应用,展示如何使用ServiceComb提供的Saga方案保证微服务间数据一致性。 + +旅行应用包含四个微服务: +- 机票预订服务(flight-booking-service) +- 租车服务(car-rental-service) +- 酒店预订服务(hotel-reservation-service) +- 支付服务(payment-service) + +机票预订、租车、酒店预订服务间无依赖关系,使用自己的数据库,通过HTTP协议通信。 +在以上三个服务的预订成功,支付完成后才能满足一个成功的行程,否则不能成行,Saga自动补偿。 + +![Saga-demo]({{ site.url }}{{ site.baseurl }}/assets/images/saga-demo.png) + +## 运行demo + +注:Demo 集成在 [ServiceComb-Saga](https://github.com/apache/servicecomb-saga) 项目中。 + +1. 准备环境 +- [Oracle JDK 1.8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) +- [Maven 3.x](https://maven.apache.org/install.html) +- [Docker](https://www.docker.com/get-docker) +- [MySQL](https://dev.mysql.com/downloads/) +- [Service Center](https://github.com/apache/servicecomb-service-center) +- [Docker compose](https://docs.docker.com/compose/install/) + +2. 配置服务 + 在各服务的 *microservice.yaml* 文件,设置该服务注册的服务中心 + + ```yaml + APPLICATION_ID: saga + service_description: + name: flight-booking-service + version: 0.0.1 + servicecomb: + service: + registry: + address: http://sc.servicecomb.io:30100 #此处选择使用ServiceComb的Service Center + rest: + address: 0.0.0.0:8080 + handler: + chain: + Consumer: + default: loadbalance + ``` + + 在 *saga-demo/docker-compose.yaml* 设置部署脚本 + ```yaml + version: '2.1' + + services: + service-center: #此处选择使用ServiceComb的Service Center容器镜像 + image: "servicecomb/service-center" + hostname: service-center + ports: + - "30100:30100" + + mysql: #此处选择使用5.7版本的mysql镜像 + image: "mysql/mysql-server:5.7" + hostname: mysql + environment: + - MYSQL_ROOT_PASSWORD=password + - MYSQL_DATABASE=saga + - MYSQL_USER=saga + - MYSQL_PASSWORD=password + ports: + - "3306:3306" + healthcheck: + test: ["CMD-SHELL", "nc -z localhost 3306 &> /dev/null; echo $$?"] + interval: 30s + timeout: 10s + retries: 5 + + car-rental-service: + image: "car-rental-service:0.0.2-SNAPSHOT" + hostname: car + links: + - "service-center:sc.servicecomb.io" + ports: + - "8080:8080" + + flight-booking-service: + image: "flight-booking-service:0.0.2-SNAPSHOT" + hostname: flight + links: + - "service-center:sc.servicecomb.io" + ports: + - "8081:8080" + + hotel-reservation-service: + image: "hotel-reservation-service:0.0.2-SNAPSHOT" + hostname: hotel + links: + - "service-center:sc.servicecomb.io" + ports: + - "8082:8080" + + payment-service: + image: "payment-service:0.0.2-SNAPSHOT" + hostname: payment + links: + - "service-center:sc.servicecomb.io" + ports: + - "8080" + + saga: + image: "saga-spring:0.0.2-SNAPSHOT" + hostname: saga + links: + - "mysql:mysql.servicecomb.io" + - "service-center:sc.servicecomb.io" + - "car-rental-service:car.servicecomb.io" + - "flight-booking-service:flight.servicecomb.io" + - "hotel-reservation-service:hotel.servicecomb.io" + - "payment-service:payment.servicecomb.io" + environment: + - JAVA_OPTS=-Dspring.profiles.active=prd,servicecomb -Dcse.service.registry.address=http://sc.servicecomb.io:30100 + ports: + - "8083:8080" + depends_on: + mysql: + condition: service_healthy + ``` + +3. 在Saga项目的根目录执行编译,制作Saga、机票预订、租车、酒店预订和支付服务镜像 + + ```bash + mvn package -DskipTests -Pdocker -Pdemo + ``` + +4. 在Saga项目的saga-demo目录通过docker-compose一键启动Saga、机票预订、租车、酒店预订和支付服务 + + ```bash + docker-compose up + ``` + +## 验证 + +1. 参照 [Saga API](https://github.com/apache/servicecomb-saga/blob/master/docs/api/api.md) 说明,设定各服务的事务、补偿、依赖和恢复参数,并保存为 *request.json* 文件 + + ```json + { + "policy": "BackwardRecovery", + "requests": [ + { + "id": "request-car", + "type": "rest", + "serviceName": "car-rental-service", + "transaction": { + "method": "post", + "path": "/rentals", + "params": { + "form": { + "customerId": "mike" + } + } + }, + "compensation": { + "method": "put", + "path": "/rentals", + "params": { + "form": { + "customerId": "mike" + } + } + } + }, + { + "id": "request-hotel", + "type": "rest", + "serviceName": "hotel-reservation-service", + "transaction": { + "method": "post", + "path": "/reservations", + "params": { + "form": { + "customerId": "mike" + } + } + }, + "compensation": { + "method": "put", + "path": "/reservations", + "params": { + "form": { + "customerId": "mike" + } + } + } + }, + { + "id": "request-flight", + "type": "rest", + "serviceName": "flight-booking-service", + "transaction": { + "method": "post", + "path": "/bookings", + "params": { + "form": { + "customerId": "mike" + } + } + }, + "compensation": { + "method": "put", + "path": "/bookings", + "params": { + "form": { + "customerId": "mike" + } + } + } + }, + { + "id": "request-payment", + "type": "rest", + "serviceName": "payment-service", + "parents": [ + "request-car", + "request-flight", + "request-hotel" + ], + "transaction": { + "method": "post", + "path": "/payments", + "params": { + "form": { + "customerId": "mike" + } + } + }, + "compensation": { + "method": "put", + "path": "/payments", + "params": { + "form": { + "customerId": "mike" + } + } + } + } + ] + } + ``` + +2. 发送请求到Saga + + ```bash + curl -XPOST -H "Content-Type: text/plain" -d @./request.json http://<localhost.ip:8083>/requests + ``` + + 获取处理结果成功(如果失败,返回相应的错误信息) + ```bash + success + ``` + +3. 查看Saga的事务 + + ```bash + curl -XGET http://<localhost.ip:8083>/events + ``` + + 查询结果 + + ```bash + { + "bcd27f0d-6b82-49b3-8067-b16eba970e55": [ + { + "id": 1, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:21Z", + "type": "SagaStartedEvent", + "contentJson": "{\"policy\": \"BackwardRecovery\", \"requests\": [{\"id\": \"request-car\", \"type\": \"rest\", \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-hotel\", \"type\": \"rest\", \"serviceName\": \"hotel-reservation-servic [...] + }, + { + "id": 2, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:21Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 3, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:21Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 4, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:21Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 5, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:22Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] + }, + { + "id": 6, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:24Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] + }, + { + "id": 7, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:25Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] + }, + { + "id": 8, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:25Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 9, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:25Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \" [...] + }, + { + "id": 10, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:25Z", + "type": "SagaEndedEvent", + "contentJson": "{}" + } + ] + } + ``` + +4. demo中模拟了支付服务帐户余额不足场景,发送请求超过一次将触发补偿操作。再次发送请求,可以在Saga日志中找到补偿事件 + + ```bash + { + "bcd27f0d-6b82-49b3-8067-b16eba970e55": [ + ... + ], + "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9": [ + { + "id": 11, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "SagaStartedEvent", + "contentJson": "{\"policy\": \"BackwardRecovery\", \"requests\": [{\"id\": \"request-car\", \"type\": \"rest\", \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-hotel\", \"type\": \"rest\", \"serviceName\": \"hotel-reservation-servic [...] + }, + { + "id": 12, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 13, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 14, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 15, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] + }, + { + "id": 16, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] + }, + { + "id": 17, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] + }, + { + "id": 18, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 19, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "TransactionAbortedEvent", + "contentJson": "{\"request\": {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \" [...] + }, + { + "id": 20, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "TransactionCompensatedEvent", + "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] + }, + { + "id": 21, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "TransactionCompensatedEvent", + "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] + }, + { + "id": 22, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "TransactionCompensatedEvent", + "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] + }, + { + "id": 23, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "SagaEndedEvent", + "contentJson": "{}" + } + ] + } + ``` + +## 下一步 + +* 了解更多[ServiceComb中的数据最终一致性方案 - part 1](/cn/docs/distributed_saga_1/) + +* 了解更多[ServiceComb中的数据最终一致性方案 - part 2](/cn/docs/distributed_saga_2/) + +* 了解更多[ServiceComb中的数据最终一致性方案 - part 3](/cn/docs/distributed_saga_3/) diff --git a/_docs/quick-start-dataconsistency.md b/_docs/quick-start-dataconsistency.md index a8a17ec..be2a0f3 100755 --- a/_docs/quick-start-dataconsistency.md +++ b/_docs/quick-start-dataconsistency.md @@ -1,462 +1,462 @@ ---- -title: "Data consistency solution" -lang: en -ref: quick-start-dataconsistency -permalink: /docs/quick-start-dataconsistency/ -excerpt: "Introduce how to use Saga data consistency solution provided by ServiceComb" -last_modified_at: 2017-09-03T10:01:43-04:00 ---- - -{% include toc %} -This demo shows you how to use the Saga solution provided by ServiceComb to ensure the microservice for data consistency. - -With microservice architecture, each of the services may have its own database technology and it's not feasible to ensure all transactions on these services are either committed or rolled back with database. In this demo, we make use of Saga to ensure eventual data consistency among services, the payment is only executed after car rental, flight booking, and hotel-reservation are completed. - -Travel application demo including four services -- flight booking service -- car rental service -- hotel reservation service -- payment service - -![Saga-demo]({{ site.url }}{{ site.baseurl }}/assets/images/saga-demo.png) - -## Running Demo - -Note, demo is in the [ServiceComb-Saga](https://github.com/apache/servicecomb-saga) project. - -1. Prerequisites -- [Oracle JDK 1.8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) -- [Maven 3.x](https://maven.apache.org/install.html) -- [Docker](https://www.docker.com/get-docker) -- [MySQL](https://dev.mysql.com/downloads/) -- [Service Center](https://github.com/apache/servicecomb-service-center) -- [Docker compose](https://docs.docker.com/compose/install/) - -2. Configuration - Set service center address of each microservice in *microservice.yaml* file - - ```yaml - APPLICATION_ID: saga - service_description: - name: flight-booking-service - version: 0.0.1 - servicecomb: - service: - registry: - address: http://sc.servicecomb.io:30100 #choose Service Center provided by ServiceComb - rest: - address: 0.0.0.0:8080 - handler: - chain: - Consumer: - default: loadbalance - ``` - - Set deployment script in *saga-demo/docker-compose.yaml* file - - ```yaml - version: '2.1' - - services: - service-center: #choose Service Center image provided by ServiceComb - image: "servicecomb/service-center" - hostname: service-center - ports: - - "30100:30100" - - mysql: #choose mysql image with 5.7 version - image: "mysql/mysql-server:5.7" - hostname: mysql - environment: - - MYSQL_ROOT_PASSWORD=password - - MYSQL_DATABASE=saga - - MYSQL_USER=saga - - MYSQL_PASSWORD=password - ports: - - "3306:3306" - healthcheck: - test: ["CMD-SHELL", "nc -z localhost 3306 &> /dev/null; echo $$?"] - interval: 30s - timeout: 10s - retries: 5 - - car-rental-service: - image: "car-rental-service:0.0.2-SNAPSHOT" - hostname: car - links: - - "service-center:sc.servicecomb.io" - ports: - - "8080:8080" - - flight-booking-service: - image: "flight-booking-service:0.0.2-SNAPSHOT" - hostname: flight - links: - - "service-center:sc.servicecomb.io" - ports: - - "8081:8080" - - hotel-reservation-service: - image: "hotel-reservation-service:0.0.2-SNAPSHOT" - hostname: hotel - links: - - "service-center:sc.servicecomb.io" - ports: - - "8082:8080" - - payment-service: - image: "payment-service:0.0.2-SNAPSHOT" - hostname: payment - links: - - "service-center:sc.servicecomb.io" - ports: - - "8080" - - saga: - image: "saga-spring:0.0.2-SNAPSHOT" - hostname: saga - links: - - "mysql:mysql.servicecomb.io" - - "service-center:sc.servicecomb.io" - - "car-rental-service:car.servicecomb.io" - - "flight-booking-service:flight.servicecomb.io" - - "hotel-reservation-service:hotel.servicecomb.io" - - "payment-service:payment.servicecomb.io" - environment: - - JAVA_OPTS=-Dspring.profiles.active=prd,servicecomb -Dcse.service.registry.address=http://sc.servicecomb.io:30100 - ports: - - "8083:8080" - depends_on: - mysql: - condition: service_healthy - ``` - -3. Run the following command to create docker images in saga project root folder - - ```bash - mvn package -DskipTests -Pdocker -Pdemo - ``` - -4. Start application up in saga/saga-demo/ with the following command - - ```bash - docker-compose up - ``` - -## Verify services - -1. Use [Saga API](https://github.com/apache/servicecomb-saga/blob/master/docs/api/api.md) to set request content and save it to *request.json* - - ```json - { - "policy": "BackwardRecovery", - "requests": [ - { - "id": "request-car", - "type": "rest", - "serviceName": "car-rental-service", - "transaction": { - "method": "post", - "path": "/rentals", - "params": { - "form": { - "customerId": "mike" - } - } - }, - "compensation": { - "method": "put", - "path": "/rentals", - "params": { - "form": { - "customerId": "mike" - } - } - } - }, - { - "id": "request-hotel", - "type": "rest", - "serviceName": "hotel-reservation-service", - "transaction": { - "method": "post", - "path": "/reservations", - "params": { - "form": { - "customerId": "mike" - } - } - }, - "compensation": { - "method": "put", - "path": "/reservations", - "params": { - "form": { - "customerId": "mike" - } - } - } - }, - { - "id": "request-flight", - "type": "rest", - "serviceName": "flight-booking-service", - "transaction": { - "method": "post", - "path": "/bookings", - "params": { - "form": { - "customerId": "mike" - } - } - }, - "compensation": { - "method": "put", - "path": "/bookings", - "params": { - "form": { - "customerId": "mike" - } - } - } - }, - { - "id": "request-payment", - "type": "rest", - "serviceName": "payment-service", - "parents": [ - "request-car", - "request-flight", - "request-hotel" - ], - "transaction": { - "method": "post", - "path": "/payments", - "params": { - "form": { - "customerId": "mike" - } - } - }, - "compensation": { - "method": "put", - "path": "/payments", - "params": { - "form": { - "customerId": "mike" - } - } - } - } - ] - } - ``` - -2. Set content type to *text/plain*, and send *request.json* to Saga - - ```bash - curl -XPOST -H "Content-Type: text/plain" -d @./request.json http://<localhost.ip:8083>/requests - ``` - - Response(return "success" if execution successfully, otherwise return failed with error info) - - ```bash - success - ``` - -3. Get all the Saga events - - ```bash - curl -XGET http://<localhost.ip:8083>/events - ``` - - Response - - ```bash - { - "bcd27f0d-6b82-49b3-8067-b16eba970e55": [ - { - "id": 1, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:21Z", - "type": "SagaStartedEvent", - "contentJson": "{\"policy\": \"BackwardRecovery\", \"requests\": [{\"id\": \"request-car\", \"type\": \"rest\", \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-hotel\", \"type\": \"rest\", \"serviceName\": \"hotel-reservation-servic [...] - }, - { - "id": 2, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:21Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 3, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:21Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 4, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:21Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 5, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:22Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] - }, - { - "id": 6, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:24Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] - }, - { - "id": 7, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:25Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] - }, - { - "id": 8, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:25Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 9, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:25Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \" [...] - }, - { - "id": 10, - "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", - "creationTime": "2017-09-20T00:30:25Z", - "type": "SagaEndedEvent", - "contentJson": "{}" - } - ] - } - ``` - -4. Sending the request more than once will trigger compensation due to insufficient account balance in payment-service.Do this implement and get Saga events, you can find compensation events in Saga log. - - ```bash - { - "bcd27f0d-6b82-49b3-8067-b16eba970e55": [ - ... - ], - "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9": [ - { - "id": 11, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "SagaStartedEvent", - "contentJson": "{\"policy\": \"BackwardRecovery\", \"requests\": [{\"id\": \"request-car\", \"type\": \"rest\", \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-hotel\", \"type\": \"rest\", \"serviceName\": \"hotel-reservation-servic [...] - }, - { - "id": 12, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 13, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 14, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 15, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] - }, - { - "id": 16, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] - }, - { - "id": 17, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionEndedEvent", - "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] - }, - { - "id": 18, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:30:45Z", - "type": "TransactionStartedEvent", - "contentJson": "{\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" - }, - { - "id": 19, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "TransactionAbortedEvent", - "contentJson": "{\"request\": {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \" [...] - }, - { - "id": 20, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "TransactionCompensatedEvent", - "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] - }, - { - "id": 21, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "TransactionCompensatedEvent", - "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] - }, - { - "id": 22, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "TransactionCompensatedEvent", - "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] - }, - { - "id": 23, - "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", - "creationTime": "2017-09-20T00:31:15Z", - "type": "SagaEndedEvent", - "contentJson": "{}" - } - ] - } - ``` - - -## What's next - -* Learn more from [Eventual Data Consistency Solution in ServiceComb - part 1](/docs/distributed_saga_1/) - -* Learn more from [Eventual Data Consistency Solution in ServiceComb - part 2](/docs/distributed_saga_2/) - -* Learn more from [Eventual Data Consistency Solution in ServiceComb - part 3](/docs/distributed_saga_3/) +--- +title: "Data consistency solution" +lang: en +ref: quick-start-dataconsistency +permalink: /docs/quick-start-dataconsistency/ +excerpt: "Introduce how to use Saga data consistency solution provided by ServiceComb" +last_modified_at: 2017-09-03T10:01:43-04:00 +--- + +{% include toc %} +This demo shows you how to use the Saga solution provided by ServiceComb to ensure the microservice for data consistency. + +With microservice architecture, each of the services may have its own database technology and it's not feasible to ensure all transactions on these services are either committed or rolled back with database. In this demo, we make use of Saga to ensure eventual data consistency among services, the payment is only executed after car rental, flight booking, and hotel-reservation are completed. + +Travel application demo including four services +- flight booking service +- car rental service +- hotel reservation service +- payment service + +![Saga-demo]({{ site.url }}{{ site.baseurl }}/assets/images/saga-demo.png) + +## Running Demo + +Note, demo is in the [ServiceComb-Saga](https://github.com/apache/servicecomb-saga) project. + +1. Prerequisites +- [Oracle JDK 1.8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) +- [Maven 3.x](https://maven.apache.org/install.html) +- [Docker](https://www.docker.com/get-docker) +- [MySQL](https://dev.mysql.com/downloads/) +- [Service Center](https://github.com/apache/servicecomb-service-center) +- [Docker compose](https://docs.docker.com/compose/install/) + +2. Configuration + Set service center address of each microservice in *microservice.yaml* file + + ```yaml + APPLICATION_ID: saga + service_description: + name: flight-booking-service + version: 0.0.1 + servicecomb: + service: + registry: + address: http://sc.servicecomb.io:30100 #choose Service Center provided by ServiceComb + rest: + address: 0.0.0.0:8080 + handler: + chain: + Consumer: + default: loadbalance + ``` + + Set deployment script in *saga-demo/docker-compose.yaml* file + + ```yaml + version: '2.1' + + services: + service-center: #choose Service Center image provided by ServiceComb + image: "servicecomb/service-center" + hostname: service-center + ports: + - "30100:30100" + + mysql: #choose mysql image with 5.7 version + image: "mysql/mysql-server:5.7" + hostname: mysql + environment: + - MYSQL_ROOT_PASSWORD=password + - MYSQL_DATABASE=saga + - MYSQL_USER=saga + - MYSQL_PASSWORD=password + ports: + - "3306:3306" + healthcheck: + test: ["CMD-SHELL", "nc -z localhost 3306 &> /dev/null; echo $$?"] + interval: 30s + timeout: 10s + retries: 5 + + car-rental-service: + image: "car-rental-service:0.0.2-SNAPSHOT" + hostname: car + links: + - "service-center:sc.servicecomb.io" + ports: + - "8080:8080" + + flight-booking-service: + image: "flight-booking-service:0.0.2-SNAPSHOT" + hostname: flight + links: + - "service-center:sc.servicecomb.io" + ports: + - "8081:8080" + + hotel-reservation-service: + image: "hotel-reservation-service:0.0.2-SNAPSHOT" + hostname: hotel + links: + - "service-center:sc.servicecomb.io" + ports: + - "8082:8080" + + payment-service: + image: "payment-service:0.0.2-SNAPSHOT" + hostname: payment + links: + - "service-center:sc.servicecomb.io" + ports: + - "8080" + + saga: + image: "saga-spring:0.0.2-SNAPSHOT" + hostname: saga + links: + - "mysql:mysql.servicecomb.io" + - "service-center:sc.servicecomb.io" + - "car-rental-service:car.servicecomb.io" + - "flight-booking-service:flight.servicecomb.io" + - "hotel-reservation-service:hotel.servicecomb.io" + - "payment-service:payment.servicecomb.io" + environment: + - JAVA_OPTS=-Dspring.profiles.active=prd,servicecomb -Dcse.service.registry.address=http://sc.servicecomb.io:30100 + ports: + - "8083:8080" + depends_on: + mysql: + condition: service_healthy + ``` + +3. Run the following command to create docker images in saga project root folder + + ```bash + mvn package -DskipTests -Pdocker -Pdemo + ``` + +4. Start application up in saga/saga-demo/ with the following command + + ```bash + docker-compose up + ``` + +## Verify services + +1. Use [Saga API](https://github.com/apache/servicecomb-saga/blob/master/docs/api/api.md) to set request content and save it to *request.json* + + ```json + { + "policy": "BackwardRecovery", + "requests": [ + { + "id": "request-car", + "type": "rest", + "serviceName": "car-rental-service", + "transaction": { + "method": "post", + "path": "/rentals", + "params": { + "form": { + "customerId": "mike" + } + } + }, + "compensation": { + "method": "put", + "path": "/rentals", + "params": { + "form": { + "customerId": "mike" + } + } + } + }, + { + "id": "request-hotel", + "type": "rest", + "serviceName": "hotel-reservation-service", + "transaction": { + "method": "post", + "path": "/reservations", + "params": { + "form": { + "customerId": "mike" + } + } + }, + "compensation": { + "method": "put", + "path": "/reservations", + "params": { + "form": { + "customerId": "mike" + } + } + } + }, + { + "id": "request-flight", + "type": "rest", + "serviceName": "flight-booking-service", + "transaction": { + "method": "post", + "path": "/bookings", + "params": { + "form": { + "customerId": "mike" + } + } + }, + "compensation": { + "method": "put", + "path": "/bookings", + "params": { + "form": { + "customerId": "mike" + } + } + } + }, + { + "id": "request-payment", + "type": "rest", + "serviceName": "payment-service", + "parents": [ + "request-car", + "request-flight", + "request-hotel" + ], + "transaction": { + "method": "post", + "path": "/payments", + "params": { + "form": { + "customerId": "mike" + } + } + }, + "compensation": { + "method": "put", + "path": "/payments", + "params": { + "form": { + "customerId": "mike" + } + } + } + } + ] + } + ``` + +2. Set content type to *text/plain*, and send *request.json* to Saga + + ```bash + curl -XPOST -H "Content-Type: text/plain" -d @./request.json http://<localhost.ip:8083>/requests + ``` + + Response(return "success" if execution successfully, otherwise return failed with error info) + + ```bash + success + ``` + +3. Get all the Saga events + + ```bash + curl -XGET http://<localhost.ip:8083>/events + ``` + + Response + + ```bash + { + "bcd27f0d-6b82-49b3-8067-b16eba970e55": [ + { + "id": 1, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:21Z", + "type": "SagaStartedEvent", + "contentJson": "{\"policy\": \"BackwardRecovery\", \"requests\": [{\"id\": \"request-car\", \"type\": \"rest\", \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-hotel\", \"type\": \"rest\", \"serviceName\": \"hotel-reservation-servic [...] + }, + { + "id": 2, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:21Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 3, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:21Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 4, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:21Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 5, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:22Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] + }, + { + "id": 6, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:24Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] + }, + { + "id": 7, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:25Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] + }, + { + "id": 8, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:25Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 9, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:25Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \" [...] + }, + { + "id": 10, + "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", + "creationTime": "2017-09-20T00:30:25Z", + "type": "SagaEndedEvent", + "contentJson": "{}" + } + ] + } + ``` + +4. Sending the request more than once will trigger compensation due to insufficient account balance in payment-service.Do this implement and get Saga events, you can find compensation events in Saga log. + + ```bash + { + "bcd27f0d-6b82-49b3-8067-b16eba970e55": [ + ... + ], + "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9": [ + { + "id": 11, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "SagaStartedEvent", + "contentJson": "{\"policy\": \"BackwardRecovery\", \"requests\": [{\"id\": \"request-car\", \"type\": \"rest\", \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-hotel\", \"type\": \"rest\", \"serviceName\": \"hotel-reservation-servic [...] + }, + { + "id": 12, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 13, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 14, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 15, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] + }, + { + "id": 16, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] + }, + { + "id": 17, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionEndedEvent", + "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] + }, + { + "id": 18, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:30:45Z", + "type": "TransactionStartedEvent", + "contentJson": "{\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}" + }, + { + "id": 19, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "TransactionAbortedEvent", + "contentJson": "{\"request\": {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \" [...] + }, + { + "id": 20, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "TransactionCompensatedEvent", + "contentJson": "{\"request\": {\"id\": \"request-car\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"car-rental-service\", \"transaction\": {\"path\": \"/rentals\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/rentals\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\ [...] + }, + { + "id": 21, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "TransactionCompensatedEvent", + "contentJson": "{\"request\": {\"id\": \"request-hotel\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"hotel-reservation-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"sta [...] + }, + { + "id": 22, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "TransactionCompensatedEvent", + "contentJson": "{\"request\": {\"id\": \"request-flight\", \"type\": \"rest\", \"parents\": [], \"fallback\": {\"type\": \"NOP\"}, \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}, \"retries\": 3}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\ [...] + }, + { + "id": 23, + "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", + "creationTime": "2017-09-20T00:31:15Z", + "type": "SagaEndedEvent", + "contentJson": "{}" + } + ] + } + ``` + + +## What's next + +* Learn more from [Eventual Data Consistency Solution in ServiceComb - part 1](/docs/distributed_saga_1/) + +* Learn more from [Eventual Data Consistency Solution in ServiceComb - part 2](/docs/distributed_saga_2/) + +* Learn more from [Eventual Data Consistency Solution in ServiceComb - part 3](/docs/distributed_saga_3/) diff --git a/_posts/cn/2018-04-04-saga-pack-design.md b/_posts/cn/2018-04-04-saga-pack-design.md index 11e2918..959b84f 100644 --- a/_posts/cn/2018-04-04-saga-pack-design.md +++ b/_posts/cn/2018-04-04-saga-pack-design.md @@ -99,12 +99,12 @@ omega的使用很简单,以一个简化的转账业务为例,同一笔转账 1. 引入依赖 ```xml <dependency> - <groupId>org.apache.servicecomb.saga</groupId> + <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-starter</artifactId> <version>0.1.0</version> </dependency> <dependency> - <groupId>org.apache.servicecomb.saga</groupId> + <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-transport-resttemplate</artifactId> <version>0.1.0</version> </dependency> diff --git a/_posts/cn/2018-04-28-saga_with_cucumber.md b/_posts/cn/2018-04-28-saga_with_cucumber.md index 755dbb6..36807ec 100644 --- a/_posts/cn/2018-04-28-saga_with_cucumber.md +++ b/_posts/cn/2018-04-28-saga_with_cucumber.md @@ -207,7 +207,7 @@ Saga在Cucumber中集成了byteman注入一个超时异常,测试Saga对超时 ```yaml RULE set the saga timeout to 5s - INTERFACE org.apache.servicecomb.saga.omega.context.annotations.SagaStart + INTERFACE org.apache.servicecomb.pack.omega.context.annotations.SagaStart METHOD timeout AT EXIT IF TRUE @@ -215,7 +215,7 @@ Saga在Cucumber中集成了byteman注入一个超时异常,测试Saga对超时 ENDRULE RULE sleep when postBooking until timeout happens - CLASS org.apache.servicecomb.saga.demo.pack.booking.BookingController + CLASS org.apache.servicecomb.pack.demo.pack.booking.BookingController METHOD postBooking AT ENTRY IF TRUE