mysql autoenlist默认_Hmily:高性能异步分布式事务TCC框架详解

Hmily框架特性

无缝集成Spring,Spring boot start。

无缝集成Dubbo,SpringCloud,Motan等rpc框架。

多种事务日志的存储方式(redis,mongdb,mysql等)。

多种不同日志序列化方式(Kryo,protostuff,hession)。

事务自动恢复。

支持内嵌事务的依赖传递。

代码零侵入,配置简单灵活。

Hmily为什么这么高性能?

1.采用disruptor进行事务日志的异步读写(disruptor是一个无锁,无GC的并发编程框架)

packagecom.hmily.tcc.core.disruptor.publisher;importcom.hmily.tcc.common.bean.entity.TccTransaction;importcom.hmily.tcc.common.enums.EventTypeEnum;importcom.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory;importcom.hmily.tcc.core.coordinator.CoordinatorService;importcom.hmily.tcc.core.disruptor.event.HmilyTransactionEvent;importcom.hmily.tcc.core.disruptor.factory.HmilyTransactionEventFactory;importcom.hmily.tcc.core.disruptor.handler.HmilyConsumerDataHandler;importcom.hmily.tcc.core.disruptor.translator.HmilyTransactionEventTranslator;importcom.lmax.disruptor.BlockingWaitStrategy;importcom.lmax.disruptor.IgnoreExceptionHandler;importcom.lmax.disruptor.RingBuffer;importcom.lmax.disruptor.dsl.Disruptor;importcom.lmax.disruptor.dsl.ProducerType;importorg.springframework.beans.factory.DisposableBean;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importjava.util.concurrent.Executor;importjava.util.concurrent.LinkedBlockingQueue;importjava.util.concurrent.ThreadPoolExecutor;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicInteger;/*** event publisher.

*

*@authorxiaoyu(Myth)*/@Componentpublic class HmilyTransactionEventPublisher implementsDisposableBean {private Disruptordisruptor;private finalCoordinatorService coordinatorService;

@Autowiredpublic HmilyTransactionEventPublisher(finalCoordinatorService coordinatorService) {this.coordinatorService =coordinatorService;

}/*** disruptor start.

*

*@parambufferSize this is disruptor buffer size.

*@paramthreadSize this is disruptor consumer thread size.*/

public void start(final int bufferSize, final intthreadSize) {

disruptor= new Disruptor<>(new HmilyTransactionEventFactory(), bufferSize, r ->{

AtomicInteger index= new AtomicInteger(1);return new Thread(null, r, "disruptor-thread-" +index.getAndIncrement());

}, ProducerType.MULTI,newBlockingWaitStrategy());final Executor executor = new ThreadPoolExecutor(threadSize, threadSize, 0, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(),

HmilyThreadFactory.create("hmily-log-disruptor", false),newThreadPoolExecutor.AbortPolicy());

HmilyConsumerDataHandler[] consumers= newHmilyConsumerDataHandler[threadSize];for (int i = 0; i < threadSize; i++) {

consumers[i]= newHmilyConsumerDataHandler(executor, coordinatorService);

}

disruptor.handleEventsWithWorkerPool(consumers);

disruptor.setDefaultExceptionHandler(newIgnoreExceptionHandler());

disruptor.start();

}/*** publish disruptor event.

*

*@paramtccTransaction {@linkplaincom.hmily.tcc.common.bean.entity.TccTransaction }

*@paramtype {@linkplainEventTypeEnum}*/

public void publishEvent(final TccTransaction tccTransaction, final inttype) {final RingBuffer ringBuffer =disruptor.getRingBuffer();

ringBuffer.publishEvent(newHmilyTransactionEventTranslator(type), tccTransaction);

}

@Overridepublic voiddestroy() {

disruptor.shutdown();

}

}

在这里bufferSize 的默认值是4094 * 4,用户可以根据自行的情况进行配置。

HmilyConsumerDataHandler[] consumers = newHmilyConsumerDataHandler[threadSize];for (int i = 0; i < threadSize; i++) {

consumers[i]= newHmilyConsumerDataHandler(executor, coordinatorService);

}

disruptor.handleEventsWithWorkerPool(consumers);

这里是采用多个消费者去处理队列里面的任务。

2.异步执行confrim,cancel方法。

packagecom.hmily.tcc.core.service.handler;importcom.hmily.tcc.common.bean.context.TccTransactionContext;importcom.hmily.tcc.common.bean.entity.TccTransaction;importcom.hmily.tcc.common.enums.TccActionEnum;importcom.hmily.tcc.core.concurrent.threadpool.HmilyThreadFactory;importcom.hmily.tcc.core.service.HmilyTransactionHandler;importcom.hmily.tcc.core.service.executor.HmilyTransactionExecutor;importorg.aspectj.lang.ProceedingJoinPoint;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importjava.util.concurrent.Executor;importjava.util.concurrent.LinkedBlockingQueue;importjava.util.concurrent.ThreadPoolExecutor;importjava.util.concurrent.TimeUnit;/*** this is transaction starter.

*

*@authorxiaoyu*/@Componentpublic class StarterHmilyTransactionHandler implementsHmilyTransactionHandler {private static final int MAX_THREAD = Runtime.getRuntime().availableProcessors() << 1;private finalHmilyTransactionExecutor hmilyTransactionExecutor;private final Executor executor = new ThreadPoolExecutor(MAX_THREAD, MAX_THREAD, 0, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(),

HmilyThreadFactory.create("hmily-execute", false),newThreadPoolExecutor.AbortPolicy());

@Autowiredpublic StarterHmilyTransactionHandler(finalHmilyTransactionExecutor hmilyTransactionExecutor) {this.hmilyTransactionExecutor =hmilyTransactionExecutor;

}

@Overridepublic Object handler(final ProceedingJoinPoint point, finalTccTransactionContext context)throwsThrowable {

Object returnValue;try{

TccTransaction tccTransaction=hmilyTransactionExecutor.begin(point);try{//execute try

returnValue =point.proceed();

tccTransaction.setStatus(TccActionEnum.TRYING.getCode());

hmilyTransactionExecutor.updateStatus(tccTransaction);

}catch(Throwable throwable) {//if exception ,execute cancel

final TccTransaction currentTransaction =hmilyTransactionExecutor.getCurrentTransaction();

executor.execute(()->hmilyTransactionExecutor

.cancel(currentTransaction));throwthrowable;

}//execute confirm

final TccTransaction currentTransaction =hmilyTransactionExecutor.getCurrentTransaction();

executor.execute(()->hmilyTransactionExecutor.confirm(currentTransaction));

}finally{

hmilyTransactionExecutor.remove();

}returnreturnValue;

}

}

当try方法的AOP切面有异常的时候,采用线程池异步去执行cancel,无异常的时候去执行confrim方法。

这里有人可能会问:那么cancel方法异常,或者confrim方法异常怎么办呢?

答:首先这种情况是非常罕见的,因为你上一面才刚刚执行完try。其次如果出现这种情况,在try阶段会保存好日志,Hmily有内置的调度线程池来进行恢复,不用担心。

有人又会问:这里如果日志保存异常了怎么办?

答:首先这又是一个牛角尖问题,首先日志配置的参数,在框架启动的时候,会要求你配置的。其次,就算在运行过程中日志保存异常,这时候框架会取缓存中的,并不会影响程序正确执行。最后,万一日志保存异常了,系统又在很极端的情况下down机了,恭喜你,你可以去买彩票了,最好的解决办法就是不去解决它。

3.ThreadLocal缓存的使用。

/*** transaction begin.

*

*@parampoint cut point.

*@returnTccTransaction*/

public TccTransaction begin(finalProceedingJoinPoint point) {

LogUtil.debug(LOGGER, ()-> "......hmily transaction!start....");//build tccTransaction

final TccTransaction tccTransaction = buildTccTransaction(point, TccRoleEnum.START.getCode(), null);//save tccTransaction in threadLocal

CURRENT.set(tccTransaction);//publishEvent

hmilyTransactionEventPublisher.publishEvent(tccTransaction, EventTypeEnum.SAVE.getCode());//set TccTransactionContext this context transfer remote

TccTransactionContext context = newTccTransactionContext();//set action is try

context.setAction(TccActionEnum.TRYING.getCode());

context.setTransId(tccTransaction.getTransId());

context.setRole(TccRoleEnum.START.getCode());

TransactionContextLocal.getInstance().set(context);returntccTransaction;

}

首先要理解,threadLocal保存的发起者一方法的事务信息。这个很重要,不要会有点懵逼。rpc的调用,会形成调用链,进行保存。

/*** add participant.

*

*@paramparticipant {@linkplainParticipant}*/

public void enlistParticipant(finalParticipant participant) {if(Objects.isNull(participant)) {return;

}

Optional.ofNullable(getCurrentTransaction())

.ifPresent(c->{

c.registerParticipant(participant);

updateParticipant(c);

});

}

4.GuavaCache的使用

packagecom.hmily.tcc.core.cache;importcom.google.common.cache.CacheBuilder;importcom.google.common.cache.CacheLoader;importcom.google.common.cache.LoadingCache;importcom.google.common.cache.Weigher;importcom.hmily.tcc.common.bean.entity.TccTransaction;importcom.hmily.tcc.core.coordinator.CoordinatorService;importcom.hmily.tcc.core.helper.SpringBeanUtils;importorg.apache.commons.lang3.StringUtils;importjava.util.Optional;importjava.util.concurrent.ExecutionException;/*** use google guava cache.

*@authorxiaoyu*/

public final classTccTransactionCacheManager {private static final int MAX_COUNT = 10000;private static final LoadingCache LOADING_CACHE =CacheBuilder.newBuilder().maximumWeight(MAX_COUNT)

.weigher((Weigher) (string, tccTransaction) ->getSize())

.build(new CacheLoader() {

@Overridepublic TccTransaction load(finalString key) {returncacheTccTransaction(key);

}

});private static CoordinatorService coordinatorService = SpringBeanUtils.getInstance().getBean(CoordinatorService.class);private static final TccTransactionCacheManager TCC_TRANSACTION_CACHE_MANAGER = newTccTransactionCacheManager();privateTccTransactionCacheManager() {

}/*** TccTransactionCacheManager.

*

*@returnTccTransactionCacheManager*/

public staticTccTransactionCacheManager getInstance() {returnTCC_TRANSACTION_CACHE_MANAGER;

}private static intgetSize() {return (int) LOADING_CACHE.size();

}private static TccTransaction cacheTccTransaction(finalString key) {return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(newTccTransaction());

}/*** cache tccTransaction.

*

*@paramtccTransaction {@linkplainTccTransaction}*/

public void cacheTccTransaction(finalTccTransaction tccTransaction) {

LOADING_CACHE.put(tccTransaction.getTransId(), tccTransaction);

}/*** acquire TccTransaction.

*

*@paramkey this guava key.

*@return{@linkplainTccTransaction}*/

public TccTransaction getTccTransaction(finalString key) {try{returnLOADING_CACHE.get(key);

}catch(ExecutionException e) {return newTccTransaction();

}

}/*** remove guava cache by key.

*@paramkey guava cache key.*/

public void removeByKey(finalString key) {if(StringUtils.isNotEmpty(key)) {

LOADING_CACHE.invalidate(key);

}

}

}

在参与者中,我们使用了ThreadLocal,而在参与者中,我们为什么不使用呢?

其实原因有二点:首先.因为try,和confrim 会不在一个线程里,会造成ThreadLocal失效。当考虑到RPC集群的时候,可能会负载到不同的机器上。

这里有一个细节就是:

private static TccTransaction cacheTccTransaction(finalString key) {return Optional.ofNullable(coordinatorService.findByTransId(key)).orElse(newTccTransaction());

}

当GuavaCache里面没有的时候,会去查询日志返回,这样就保证了对集群环境的支持。以上4点造就了Hmily是一个异步的高性能分布式事务TCC框架的原因。

Hmily如何使用?

(https://github.com/yu199195/hmily/tree/master/hmily-tcc-demo)

首先因为之前的包命名问题,框架包并没有上传到maven中心仓库,固需要使用者自己拉取代码,编译deploy到自己的私服。

1.dubbo用户

在你的Api接口项目引入

com.hmily.tcc

hmily-tcc-annotation

{you version}

在你的服务提供者项目引入

com.hmily.tcc

hmily-tcc-dubbo

{you version}

配置启动bean

当然配置属性很多,这里只给出了demo,具体可以参考这个类:

packagecom.hmily.tcc.common.config;importcom.hmily.tcc.common.enums.RepositorySupportEnum;importlombok.Data;/*** hmily config.

*

*@authorxiaoyu*/@Datapublic classTccConfig {/*** Resource suffix this parameter please fill in about is the transaction store path.

* If it's a table store this is a table suffix, it's stored the same way.

* If this parameter is not filled in, the applicationName of the application is retrieved by default*/

privateString repositorySuffix;/*** log serializer.

* {@linkplaincom.hmily.tcc.common.enums.SerializeEnum}*/

private String serializer = "kryo";/*** scheduledPool Thread size.*/

private int scheduledThreadMax = Runtime.getRuntime().availableProcessors() << 1;/*** scheduledPool scheduledDelay unit SECONDS.*/

private int scheduledDelay = 60;/*** retry max.*/

private int retryMax = 3;/*** recoverDelayTime Unit seconds

* (note that this time represents how many seconds after the local transaction was created before execution).*/

private int recoverDelayTime = 60;/*** Parameters when participants perform their own recovery.

* 1.such as RPC calls time out

* 2.such as the starter down machine*/

private int loadFactor = 2;/*** repositorySupport.

* {@linkplainRepositorySupportEnum}*/

private String repositorySupport = "db";/*** disruptor bufferSize.*/

private int bufferSize = 4096 * 2 * 2;/*** this is disruptor consumerThreads.*/

private int consumerThreads = Runtime.getRuntime().availableProcessors() << 1;/*** db config.*/

privateTccDbConfig tccDbConfig;/*** mongo config.*/

privateTccMongoConfig tccMongoConfig;/*** redis config.*/

privateTccRedisConfig tccRedisConfig;/*** zookeeper config.*/

privateTccZookeeperConfig tccZookeeperConfig;/*** file config.*/

privateTccFileConfig tccFileConfig;

}

2.SpringCloud用户

需要引入

com.hmily.tcc

hmily-tcc-springcloud

{you version}

配置启动bean 如上。

3.Motan用户

需要引入

com.hmily.tcc

hmily-tcc-motan

{you version}

配置启动bean 如上。

hmily-spring-boot-start

那这个就更容易了,只需要根据你的RPC框架去引入不同的jar包。

如果你是dubbo用户,那么引入

com.hmily.tcc

hmily-tcc-spring-boot-starter-dubbo

${your version}

如果你是SpringCloud用户,那么引入

com.hmily.tcc

hmily-tcc-spring-boot-starter-springcloud

${your version}

如果你是Motan用户,那么引入

com.hmily.tcc

hmily-tcc-spring-boot-starter-motan

${your version}

然后在你的yml里面进行如下配置:

hmily:

tcc :

serializer : kryo

recoverDelayTime : 128

retryMax : 3

scheduledDelay : 128

scheduledThreadMax : 10

repositorySupport : db

tccDbConfig :

driverClassName : com.mysql.jdbc.Driver

url : jdbc:mysql://192.168.1.98:3306/tcc?useUnicode=true&characterEncoding=utf8

username : root

password : 123456

#repositorySupport : redis

#tccRedisConfig:

#masterName: mymaster

#sentinel : true

#sentinelUrl : 192.168.1.91:26379;192.168.1.92:26379;192.168.1.93:26379

#password : foobaredbbexONE123

# repositorySupport : zookeeper

# host : 92.168.1.73:2181

# sessionTimeOut : 100000

# rootPath : /tcc

# repositorySupport : mongodb

# mongoDbUrl : 192.168.1.68:27017

# mongoDbName : happylife

# mongoUserName : xiaoyu

# mongoUserPwd : 123456

# repositorySupport : file

# path : /account

# prefix : account

就这么简单,然后就可以在接口方法上加上@Tcc注解,进行愉快的使用了。当然因为篇幅问题,很多东西只是简单的描述,尤其是逻辑方面的。

下面是github地址:https://github.com/yu199195/hmily

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值