Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add saga annotation samples #553

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions saga/local-saga-annotation-sample/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Seata Saga 注解模式 demo 工程

[Seata](https://github.com/seata/seata) 是一款开源的分布式事务解决方案,提供高性能和简单易用的分布式事务服务。

## demo 说明

基于 Seata Saga注解模式,演示分布式事务的提交和回滚;

本demo中一个分布式事务内会有2个Saga事务参与者,分别是: [InventoryAction](src/main/java/io/seata/samples/saga/action/InventoryAction.java)
和 [BalanceAction](src/main/java/io/seata/samples/saga/action/BalanceAction.java)
;分布式事务提交则两者均提交,分布式事务回滚则两者均回滚;

这2个Saga参与者均是 本地bean(非远程RPC服务),两个参与都有一个reduce方法,表示库存扣减或余额扣减,还有一个compensateReduce方法,表示补偿扣减操作。

- InventoryAction 接口定义如下:

```java
@LocalService
public interface InventoryAction {

/**
* reduce
*
* @param count
* @return
*/
@SagaTransactional(name = "InventoryAction", compensationMethod = "compensateReduce", isDelayReport = true, useCommonFence = true)
boolean reduce(String businessKey, int count);

/**
* increase
*
* @return
*/
boolean compensateReduce(String businessKey);
}
```

## demo 运行指南

#### step 1 使用 Docker 部署 Seata Server (1.5.0)

$ docker run --name seata-server -p 8091:8091 -p 7091:7091 seataio/seata-server:1.5.0


#### step 2 启动 Saga Demo

运行 [LocalSagaTransactionStarter](src/main/java/io/seata/samples/saga/starter/LocalSagaTransactionStarter.java)
, 启动 demo工程;



内含Saga 事务提交场景和Saga事务回滚场景的演示;

39 changes: 39 additions & 0 deletions saga/local-saga-annotation-sample/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2019 Seata.io Group.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.seata</groupId>
<artifactId>seata-samples-saga</artifactId>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>local-saga-annotation-sample</artifactId>
<packaging>jar</packaging>
<name>local-saga-annotation-sample ${project.version}</name>

<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 1999-2021 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.samples.saga;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.AbstractApplicationContext;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
* The type Application keeper.
*/
public class ApplicationKeeper {

private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationKeeper.class);

private final ReentrantLock LOCK = new ReentrantLock();
private final Condition STOP = LOCK.newCondition();

/**
* Instantiates a new Application keeper.
*
* @param applicationContext the application context
*/
public ApplicationKeeper(AbstractApplicationContext applicationContext) {
addShutdownHook(applicationContext);
}

private void addShutdownHook(final AbstractApplicationContext applicationContext) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
try {
applicationContext.close();
LOGGER.info("ApplicationContext " + applicationContext + " is closed.");
} catch (Exception e) {
LOGGER.error("Failed to close ApplicationContext", e);
}

try {
LOCK.lock();
STOP.signal();
} finally {
LOCK.unlock();
}
}
}));
}

/**
* Keep.
*/
public void keep() {
synchronized (LOCK) {
try {
LOGGER.info("Application is keep running ... ");
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 1999-2021 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.samples.saga.action;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.saga.annotation.api.SagaTransactional;
import io.seata.spring.annotation.LocalService;

import java.math.BigDecimal;
import java.util.Map;

/**
* Balance Actions
*/
@LocalService
public interface BalanceAction {

/**
* reduce
*
* @param businessKey
* @param amount
* @param params
* @return
*/
@SagaTransactional(name = "BalanceAction" , compensationMethod = "compensateReduce", isDelayReport = true, useCommonFence = false)
boolean reduce(BusinessActionContext actionContext, String businessKey, BigDecimal amount, Map<String, Object> params);

/**
* compensateReduce
*
* @param actionContext
* @return
*/
boolean compensateReduce(BusinessActionContext actionContext);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 1999-2021 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.samples.saga.action;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.saga.annotation.api.SagaTransactional;
import io.seata.spring.annotation.LocalService;

/**
* Inventory Actions
*/
@LocalService
public interface InventoryAction {

/**
* reduce
*
* @param count
* @return
*/
@SagaTransactional(name = "InventoryAction", compensationMethod = "compensateReduce", isDelayReport = true, useCommonFence = false)
boolean reduce(BusinessActionContext actionContext, String businessKey, int count);

/**
* increase
*
* @return
*/
boolean compensateReduce(BusinessActionContext actionContext);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 1999-2021 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.samples.saga.action;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* The type Result holder.
*
* @author zhangsen
*/
public class ResultHolder {

private static Map<String, String> actionOneResults = new ConcurrentHashMap<String, String>();

private static Map<String, String> actionTwoResults = new ConcurrentHashMap<String, String>();

/**
* Set action one result.
*
* @param txId the tx id
* @param result the result
*/
public static void setActionOneResult(String txId, String result) {
actionOneResults.put(txId, result);
}

/**
* Get action one result string.
*
* @param txId the tx id
* @return the string
*/
public static String getActionOneResult(String txId) {
return actionOneResults.get(txId);
}

/**
* Set action two result.
*
* @param txId the tx id
* @param result the result
*/
public static void setActionTwoResult(String txId, String result) {
actionTwoResults.put(txId, result);
}

/**
* Get action two result string.
*
* @param txId the tx id
* @return the string
*/
public static String getActionTwoResult(String txId) {
return actionTwoResults.get(txId);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 1999-2021 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.samples.saga.action.impl;

import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.samples.saga.action.BalanceAction;
import io.seata.samples.saga.action.ResultHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.Map;

/**
* @author lorne.cl
*/
public class BalanceActionImpl implements BalanceAction {

private static final Logger LOGGER = LoggerFactory.getLogger(BalanceActionImpl.class);

@Override
public boolean reduce(BusinessActionContext actionContext, String businessKey, BigDecimal amount, Map<String, Object> params) {
if(params != null) {
Object throwException = params.get("throwException");
if (throwException != null && "true".equals(throwException.toString())) {
throw new RuntimeException("reduce balance failed");
}
}
LOGGER.info("reduce balance succeed, amount: " + amount + ", businessKey:" + businessKey);
ResultHolder.setActionOneResult(actionContext.getXid(), "T");
return true;
}

@Override
public boolean compensateReduce(BusinessActionContext actionContext) {
String xid = actionContext.getXid();
System.out.println("Balance compensate Reduce, xid:" + xid);
ResultHolder.setActionOneResult(xid, "R");
return true;
}
}
Loading