-- the table to store GlobalSession data
drop table `global_table`;
create table `global_table` (
`xid` varchar(128) not null,
`transaction_id` bigint,
`status` tinyint not null,
`application_id` varchar(64),
`transaction_service_group` varchar(64),
`transaction_name` varchar(128),
`timeout` int,
`begin_time` bigint,
`application_data` varchar(2000),
`gmt_create` datetime,
`gmt_modified` datetime,
primary key (`xid`),
key `idx_gmt_modified_status` (`gmt_modified`, `status`),
key `idx_transaction_id` (`transaction_id`)
);
-- the table to store BranchSession data
drop table `branch_table`;
create table `branch_table` (
`branch_id` bigint not null,
`xid` varchar(128) not null,
`transaction_id` bigint ,
`resource_group_id` varchar(128),
`resource_id` varchar(256) ,
`lock_key` varchar(256) ,
`branch_type` varchar(8) ,
`status` tinyint,
`client_id` varchar(64),
`application_data` varchar(2000),
`gmt_create` datetime,
`gmt_modified` datetime,
primary key (`branch_id`),
key `idx_xid` (`xid`)
);
-- the table to store lock data
drop table `lock_table`;
create table `lock_table` (
`row_key` varchar(128) not null,
`xid` varchar(128),
`transaction_id` long ,
`branch_id` long,
`resource_id` varchar(256) ,
`table_name` varchar(64) ,
`pk` varchar(128) ,
`gmt_create` datetime ,
`gmt_modified` datetime,
primary key(`row_key`)
);
## transaction log store, only used in seata-server
store {
## store mode: file、db、redis
mode = "db"
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata_server"
user = "root"
password = ""
minConn = 5
maxConn = 30
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}
lock {
## the data row lock store mode: local_db、memory or db
mode = "db"
memory{
## store lock in memory of server
}
db{
## use db of server to store lock, the db is ${store.db.url}
lock-table= "lock_table"
}
}
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "zk"
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
username = ""
password = ""
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3
type = "file"
file {
name = "file.conf"
}
}
Linux/Unix/Mac
sh seata-server.sh $LISTEN_PORT $STORE_MODE $IP(此参数可选)
Windows
cmd seata-server.bat $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA $IP(此参数可选)
# =========================Seata Config===============================
seata.enabled = true
seata.enable-auto-data-source-proxy = true
seata.application-id = hisaggre-api
seata.tx-service-group = pms
seata.client.rm-report-success-enable = true
seata.client.rm-table-meta-check-enable = false
seata.client.rm-report-retry-count = 5
seata.client.rm-async-commit-buffer-limit = 10000
seata.client.rm.lock.lock-retry-internal = 10
seata.client.rm.lock.lock-retry-times = 30
seata.client.rm.lock.lock-retry-policy-branch-rollback-on-conflict = true
seata.client.tm-commit-retry-count = 3
seata.client.tm-rollback-retry-count = 3
seata.client.undo.undo-data-validation = true
seata.client.undo.undo-log-serialization = jackson
seata.client.undo.undo-log-table = undo_log
seata.client.log.exceptionRate = 100
seata.client.support.spring.datasource-autoproxy = true
seata.service.vgroup-mapping.pms = default
seata.service.enable-degrade = false
seata.service.disable-global-transaction = false
seata.service.grouplist.default = 127.0.0.1:8091
seata.transport.shutdown.wait = 3
seata.transport.thread-factory.boss-thread-prefix = NettyBoss
seata.transport.thread-factory.worker-thread-prefix = NettyServerNIOWorker
seata.transport.thread-factory.server-executor-thread-prefix = NettyServerBizHandler
seata.transport.thread-factory.share-boss-worker = false
seata.transport.thread-factory.client-selector-thread-prefix = NettyClientSelector
seata.transport.thread-factory.client-selector-thread-size = 1
seata.transport.thread-factory.client-worker-thread-prefix = NettyClientWorkerThread
seata.transport.type = TCP
seata.transport.server = NIO
seata.transport.heartbeat = true
seata.transport.serialization = seata
seata.transport.compressor = none
seata.transport.enable-client-batch-send-request = true
seata.registry.file.name = file.conf
seata.registry.type = zk
seata.config.file.name = file.conf
seata.config.type = file
core build.gradle 增加以下依赖
compile("io.seata:seata-spring-boot-starter:1.3.0")
compile("com.101tec:zkclient:0.10")
XidInterceptor
import io.seata.core.context.RootContext;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class XidInterceptor implements HandlerInterceptor {
/**
* 在请求处理之前进行调用(Controller方法调用之前)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String xid = httpRequest.getHeader("xid");
if (StringUtils.isNotEmpty(xid)) {
RootContext.bind(xid);
}
return true;
}
}
@Configuration
@EnableSpringDataWebSupport
public class WebConfig implements WebMvcConfigurer {
省略一些代码..................
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册TestInterceptor拦截器
InterceptorRegistration registration = registry.addInterceptor(new XidInterceptor());
registration.addPathPatterns("/**"); //所有路径都被拦截
registration.excludePathPatterns( //添加不拦截路径
"你的登陆路径", //登录
"/**/*.html", //html静态资源
"/**/*.js", //js静态资源
"/**/*.css", //css静态资源
"/**/*.woff",
"/**/*.ttf"
);
}
}
HystrixConcurrencyStrategy
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import io.seata.core.context.RootContext;
import java.util.concurrent.Callable;
public class XidHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
@Override
public Callable wrapCallable(Callable callable) {
return new XidAwareCallable<>(callable, RootContext.getXID());
}
static class XidAwareCallable implements Callable {
private final Callable delegate;
private final String xid;
public XidAwareCallable(Callable callable, String xid) {
this.delegate = callable;
this.xid = xid;
}
@Override
public T call() throws Exception {
try {
RootContext.bind(xid);
return delegate.call();
} finally {
}
}
}
}
HystrixConfig
import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.xingren.v.logging.annotations.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
@Slf4j
public class HystrixConfig {
@Bean
public HystrixCommandAspect hystrixAspect() {
return new HystrixCommandAspect();
}
@PostConstruct
public void init() {
try {
HystrixConcurrencyStrategy target = new XidHystrixConcurrencyStrategy();
HystrixConcurrencyStrategy strategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (strategy instanceof XidHystrixConcurrencyStrategy) {
// Welcome to singleton hell...
return;
}
HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins
.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance()
.getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance()
.getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()
.getPropertiesStrategy();
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(target);
HystrixPlugins.getInstance()
.registerCommandExecutionHook(commandExecutionHook);
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
} catch (Exception e) {
log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
}
}
}
以sdk 模式为例:
import com.xingren.org.springframework.security.oauth2.common.OAuth2AccessToken;
import com.xingren.reaper.interceptors.ImplicitAccessTokenInterceptor;
import feign.RequestTemplate;
import io.seata.core.context.RootContext;
import okhttp3.Request;
import okhttp3.Response;
import javax.validation.constraints.NotNull;
import java.io.IOException;
public class XidInterceptor extends ImplicitAccessTokenInterceptor {
public XidInterceptor(@NotNull String baseUrl,
@NotNull String clientId) {
super(baseUrl, clientId);
}
public Response intercept(Chain chain) throws IOException {
String xid = RootContext.getXID();
OAuth2AccessToken token = this.getToken();
Request request = chain.request();
request = request.newBuilder().removeHeader("Authorization").addHeader("Authorization", "Bearer" + token.getValue().toString()).build();
request = request.newBuilder().removeHeader("xid").addHeader("xid",
xid).build();
return chain.proceed(request);
}
public void apply(RequestTemplate requestTemplate) {
OAuth2AccessToken token = this.getToken();
requestTemplate.header("Authorization", new String[]{"Bearer" + token.getValue().toString()});
requestTemplate.header("xid", RootContext.getXID());
}
}
@Bean
public PurchaseConfigClient purchaseConfigClient() {
return new PurchaseConfigClient(_pmsClientConfig(), _pmsHystrixCommandConfig(), _pmsHystrixThreadConfig());
}
private PmsClientConfig _pmsClientConfig() {
PmsClientConfig config = new PmsClientConfig(properties.getHost());
config.addApplicationInterceptor(new XidInterceptor(reaperProperties.getHost(),
reaperProperties.getClientId()));
return config;
}
如果项目本身有数据库代理可能会出现死循环,可以用excludes排除
@SpringBootApplication
@ComponentScan("com.xingren")
@EnableFeignClients("com.xingren")
@EnableAutoDataSourceProxy(excludes = { "org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy" })
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}