数据一致性解决方案
模拟一个简单的旅行应用,展示如何使用ServiceComb提供的Saga方案保证微服务间数据一致性。
旅行应用包含四个微服务:
- 机票预订服务(flight-booking-service)
- 租车服务(car-rental-service)
- 酒店预订服务(hotel-reservation-service)
- 支付服务(payment-service)
机票预订、租车、酒店预订服务间无依赖关系,使用自己的数据库,通过HTTP协议通信。 在以上三个服务的预订成功,支付完成后才能满足一个成功的行程,否则不能成行,Saga自动补偿。
运行demo
注:Demo 集成在 ServiceComb-Saga 项目中。
- 准备环境
-
配置服务 在各服务的 microservice.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 设置部署脚本
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
-
在Saga项目的根目录执行编译,制作Saga、机票预订、租车、酒店预订和支付服务镜像
mvn package -DskipTests -Pdocker -Pdemo
-
在Saga项目的saga-demo目录通过docker-compose一键启动Saga、机票预订、租车、酒店预订和支付服务
docker-compose up
验证
-
参照 Saga API 说明,设定各服务的事务、补偿、依赖和恢复参数,并保存为 request.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" } } } } ] }
-
发送请求到Saga
curl -XPOST -H "Content-Type: text/plain" -d @./request.json http://<localhost.ip:8083>/requests
获取处理结果成功(如果失败,返回相应的错误信息)
success
-
查看Saga的事务
curl -XGET http://<localhost.ip:8083>/events
查询结果
{ "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-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-flight\", \"type\": \"rest\", \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}]}" }, { "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\\\": 200,\\n \\\"content\\\": \\\"Flight booked with id f249543b-765e-4e10-9ba3-74fe33d8af83 for customer mike\\\"\\n}\"}}" }, { "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 \\\"statusCode\\\": 200,\\n \\\"content\\\": \\\"Hotel reserved with id f74049a0-3d3d-49b6-a45b-058a409daecf for customer mike\\\"\\n}\"}}" }, { "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,\\n \\\"content\\\": \\\"Car rented with id 937d7364-be07-4d47-9e01-af72290d0478 for customer mike\\\"\\n}\"}}" }, { "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}}, \"response\": {\"body\": \"{\\n \\\"statusCode\\\": 200,\\n \\\"content\\\": \\\"Payment made for customer mike and remaining balance is 200\\\"\\n}\"}}" }, { "id": 10, "sagaId": "bcd27f0d-6b82-49b3-8067-b16eba970e55", "creationTime": "2017-09-20T00:30:25Z", "type": "SagaEndedEvent", "contentJson": "{}" } ] }
-
demo中模拟了支付服务帐户余额不足场景,发送请求超过一次将触发补偿操作。再次发送请求,可以在Saga日志中找到补偿事件
{ "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-service\", \"transaction\": {\"path\": \"/reservations\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/reservations\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-flight\", \"type\": \"rest\", \"serviceName\": \"flight-booking-service\", \"transaction\": {\"path\": \"/bookings\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/bookings\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}, {\"id\": \"request-payment\", \"type\": \"rest\", \"parents\": [\"request-car\", \"request-flight\", \"request-hotel\"], \"serviceName\": \"payment-service\", \"transaction\": {\"path\": \"/payments\", \"method\": \"post\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}, \"compensation\": {\"path\": \"/payments\", \"method\": \"put\", \"params\": {\"form\": {\"customerId\": \"mike\"}}}}]}" }, { "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,\\n \\\"content\\\": \\\"Car rented with id 75f4be4b-0d32-4bbb-b786-7b39c340682a for customer mike\\\"\\n}\"}}" }, { "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 \\\"statusCode\\\": 200,\\n \\\"content\\\": \\\"Hotel reserved with id 7ad11c00-25eb-4b4b-b1fb-b55d4e833f51 for customer mike\\\"\\n}\"}}" }, { "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\\\": 200,\\n \\\"content\\\": \\\"Flight booked with id 1a40d7bf-ee66-46d1-b066-a9973f15176a for customer mike\\\"\\n}\"}}" }, { "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}}, \"response\": {\"body\": \"{\\n \\\"body\\\": \\\"org.apache.servicecomb.saga.core.TransactionFailedException: The remote service payment-service failed to serve the post request to /payments \\n\\tat org.apache.servicecomb.saga.discovery.service.center.ServiceCenterDiscoveryRestTransport.with(ServiceCenterDiscoveryRestTransport.java:81)\\n\\tat org.apache.servicecomb.saga.format.JacksonRestOperation.send(JacksonRestOperation.java:43)\\n\\tat org.apache.servicecomb.saga.format.JacksonRestTransaction.send(JacksonRestTransaction.java:24)\\n\\tat org.apache.servicecomb.saga.core.RequestProcessTask.commit(RequestProcessTask.java:45)\\n\\tat org.apache.servicecomb.saga.core.BackwardRecovery.apply(BackwardRecovery.java:33)\\n\\tat org.apache.servicecomb.saga.core.LoggingRecoveryPolicy.apply(LoggingRecoveryPolicy.java:38)\\n\\tat org.apache.servicecomb.saga.core.TransactionTaskConsumer$OperationCallable.call(TransactionTaskConsumer.java:110)\\n\\tat org.apache.servicecomb.saga.core.TransactionTaskConsumer$OperationCallable.call(TransactionTaskConsumer.java:94)\\n\\tat java.util.concurrent.FutureTask.run(FutureTask.java:266)\\n\\tat java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)\\n\\tat java.util.concurrent.FutureTask.run(FutureTask.java:266)\\n\\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\\n\\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\\n\\tat java.lang.Thread.run(Thread.java:748)\\nCaused by: InvocationException: code=490;msg=CommonExceptionData [message=Cse Internal Bad Request]\\n\\tat org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory.doCreate(ExceptionFactory.java:74)\\n\\tat org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory.convertException(ExceptionFactory.java:119)\\n\\tat org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory.convertConsumerException(ExceptionFactory.java:78)\\n\\tat org.apache.servicecomb.swagger.invocation.Response.createConsumerFail(Response.java:128)\\n\\tat org.apache.servicecomb.swagger.invocation.Response.createFail(Response.java:121)\\n\\tat org.apache.servicecomb.swagger.invocation.AsyncResponse.fail(AsyncResponse.java:41)\\n\\tat org.apache.servicecomb.transport.rest.client.http.VertxHttpMethod.lambda$doMethod$0(VertxHttpMethod.java:67)\\n\\tat io.vertx.core.http.impl.HttpClientRequestBase.handleException(HttpClientRequestBase.java:110)\\n\\tat io.vertx.core.http.impl.HttpClientRequestImpl.handleException(HttpClientRequestImpl.java:50)\\n\\tat io.vertx.core.http.impl.HttpClientRequestBase.timeout(HttpClientRequestBase.java:155)\\n\\tat io.vertx.core.http.impl.HttpClientRequestBase.handleTimeout(HttpClientRequestBase.java:140)\\n\\tat io.vertx.core.http.impl.HttpClientRequestBase.lambda$setTimeout$0(HttpClientRequestBase.java:100)\\n\\tat io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:782)\\n\\tat io.vertx.core.impl.VertxImpl$InternalTimerHandler.handle(VertxImpl.java:753)\\n\\tat io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:316)\\n\\tat io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)\\n\\tat io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:418)\\n\\tat io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:440)\\n\\tat io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:873)\\n\\t... 1 more\\nCaused by: java.util.concurrent.TimeoutException: The timeout period of 30000ms has been exceeded\\n\\t... 11 more\\n\\\"\\n}\"}}" }, { "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,\\n \\\"content\\\": \\\"Car rental cancelled with id 38bc5913-6768-4321-add3-6d26db364c19 for customer mike\\\"\\n}\"}}" }, { "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 \\\"statusCode\\\": 200,\\n \\\"content\\\": \\\"Hotel reservation cancelled with id efcff718-fb74-4b70-a2e2-689ecb42206d for customer mike\\\"\\n}\"}}" }, { "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\\\": 200,\\n \\\"content\\\": \\\"Flight booking cancelled with id 37572464-6f53-4351-aa37-5f88e3f1f872 for customer mike\\\"\\n}\"}}" }, { "id": 23, "sagaId": "2654fa50-71e2-4fc8-afc2-6a5e0d3dafe9", "creationTime": "2017-09-20T00:31:15Z", "type": "SagaEndedEvent", "contentJson": "{}" } ] }