导读:在上篇文章中介绍了分布式事务项目的基本原理和工程组件,我们了解到了分布式事务的理论知识。处于实战的经验,我们将理论知识使用到实际项目中。所以我们将借助idea中maven工程 来实战我们的项目。
回到正文:
在上篇文章中我们已经把需要的准备工作做好了。现在我们需要将如何实现分布式3PC事务提交锁。
先睹为快
首先我们先来体验一下事务提交锁的过程,在本项目中我们将在Windows环境下搭建redis环境和zookeeper环境。下面就是我们只需一段分布式加锁程序的过程。一段执行锁发生异常的行为:
定义事物锁的类型:
我们使用分布式事务锁的时候我们需要提供如下几种类型的锁:
- 1:写锁,WRITE
- 2:读锁,READ
- 3:独占写锁,到时间释放:WRITE_TIME
- 4:强制时间锁,无论获取锁成功,强制时间锁, 到时间时间释放,FORCE_WRITE_TIME
更具上面分析我们将定义一个枚举类来枚举上面所需要的锁。NettyTransactionLockType.java
package com.twjitm.transaction.transaction.enums;
/**
* 事物锁类型
*
* @author twjitm- [Created on 2018-08-27 11:50]
* @jdk java version "1.8.0_77"
*/
public enum NettyTransactionLockType {
/**独自占有锁 */
WRITE,
/**读锁*/
READ,
/** 独自写占有,到时间才会释放锁*/
WRITE_TIME,
/** 独自写占有,无论获取锁成功,强制时间锁, 到时间时间释放*/
FORCE_WRITE_TIME,
;
}
有了锁的枚举,那么我们将来实现事务实体的行为,试想一下。一个事务的执行过程。由如下行为:
- 1:是否能够创建锁
- 2:是否能够尝试提交锁
- 3:是否能够正式提交锁
- 4:提交过程是否发生异常
- 5:发生异常按照不同粒度回滚锁
- 6:回滚锁是否发生异常
- 7:是否释放锁
- 8:释放锁是否发生异常
- 9:补偿提交锁
特别说明:事务、事务实体、和事务锁不是一个概念。不要混淆了。
根据上面说的我们来定义这些事务实体行为接口。来细化事务的每个阶段。NettyTransactionEntityInterface.java 接口
package com.twjitm.transaction.transaction.entity;
import com.twjitm.transaction.lock.NettyTransactionLockInterface;
import com.twjitm.transaction.transaction.enums.NettyTransactionCommitResult;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
/**
* @author twjitm- [Created on 2018-08-27 10:16]
* @jdk java version "1.8.0_77"
*/
public interface NettyTransactionEntityInterface {
/**
* 事物提交后需要执行的逻辑,提交这个事物主要
* 的业务逻辑
*
* @throws NettyTransactionException
*/
void commit() throws NettyTransactionException;
/**
* 回滚,事务在执行过程中发生异常后需要回滚操作的逻辑
*
* @throws NettyTransactionException
*/
void rollback() throws NettyTransactionException;
/**
* 尝试性提交
*/
NettyTransactionCommitResult tryCommit() throws NettyTransactionException;
/**
* 是否可以创建锁
*
* @return
*/
boolean createNettyTransactionLock(long seconds) throws NettyTransactionException;
/**
* 释放锁
*
* @return
*/
void releaseNettyTransactionLock();
/**
* 强制释放锁
*
* @return
*/
void forceReleaseNettyTransactionLock();
String getInfo();
/**
* 是否需要执行
*
* @return
*/
boolean needCommit();
/**
* 获取锁内容
*
* @return
*/
NettyTransactionLockInterface getNettyTransactionLockInterface();
}
一个事务实体需要由事务的操作。所以我们定义个事务实体来实现事务的行为操作。所以需要实现事务行为接口,由于系统中既对redis实现方式的支持也对zookeeper实现的方式支持。所以我们需要定义两种类型的事务实体。首先我们来描述一下redis实现的事务实体。
AbstractNettyTransactionEntity.java
package com.twjitm.transaction.transaction.entity;
import com.twjitm.transaction.lock.NettyTransactionLock;
import com.twjitm.transaction.lock.NettyTransactionLockInterface;
import com.twjitm.transaction.lock.NettyTransactionReadLock;
import com.twjitm.transaction.service.redis.NettyTransactionRedisService;
import com.twjitm.transaction.transaction.enums.NettyTransactionEntityCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionLockType;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
import java.util.BitSet;
/**
* 基于redis实现分布式事物锁
* 抽象事物实体类,所有的事物锁实体必须继承
* 本抽象类,实现自己的commit后的方法和rollback方法等用户实现方法
* <p>
* 这些方法不是创建锁或者回滚锁方法,而是实现的是业务方法。需要注意的是
* 不能再次方法中创建和锁字段一样的字段,否者造成数据一致性错误
*
* @author twjitm- [Created on 2018-08-27 11:48]
* @jdk java version "1.8.0_77"
*/
public abstract class AbstractNettyTransactionEntity implements NettyTransactionEntityInterface {
/**
* 进度设置集合 主要用于rollback
*/
private BitSet progressBitSet;
/**
* 事务锁
*/
private NettyTransactionLockInterface nettyTransactionLock;
/**
* 锁类型
*/
private NettyTransactionLockType nettyTransactionLockType;
/**
* 锁的正向标志(主要用于读取的时候)
*/
private boolean rejectFlag = false;
public AbstractNettyTransactionEntity(NettyTransactionEntityCause cause,
String key,
NettyTransactionRedisService redisService) {
this.progressBitSet = new BitSet();
this.nettyTransactionLock = new NettyTransactionLock(key, redisService, cause);
this.nettyTransactionLockType = NettyTransactionLockType.WRITE;
}
/**
* 抽象分布式事物实体
*
* @param cause 事物产生原因
* @param key 事物key
* @param redisService 事物支持的redis服务
* @param nettyTransactionLockType 事物锁类型
*/
public AbstractNettyTransactionEntity(NettyTransactionEntityCause cause,
String key,
NettyTransactionRedisService redisService,
NettyTransactionLockType nettyTransactionLockType) {
this.progressBitSet = new BitSet();
if (nettyTransactionLockType.equals(NettyTransactionLockType.READ)) {
this.nettyTransactionLock = new NettyTransactionReadLock(key, redisService, cause);
this.nettyTransactionLockType = NettyTransactionLockType.READ;
} else {
this.nettyTransactionLock = new NettyTransactionLock(key, redisService, cause);
this.nettyTransactionLockType = NettyTransactionLockType.WRITE;
}
}
public AbstractNettyTransactionEntity(NettyTransactionEntityCause cause,
String key,
NettyTransactionRedisService redisService,
NettyTransactionLockType nettyTransactionLockType, int lockTime) {
this.progressBitSet = new BitSet();
if (nettyTransactionLockType.equals(NettyTransactionLockType.READ)) {
this.nettyTransactionLock = new NettyTransactionReadLock(key, redisService, cause);
this.nettyTransactionLockType = NettyTransactionLockType.READ;
}
//独占锁
else if (nettyTransactionLockType.equals(NettyTransactionLockType.FORCE_WRITE_TIME)) {
this.nettyTransactionLock = new NettyTransactionLock(key, redisService, cause, lockTime, true);
} else {
//非独占锁
this.nettyTransactionLock = new NettyTransactionLock(key, redisService, cause, lockTime, false);
}
this.nettyTransactionLockType = nettyTransactionLockType;
}
/**
* 是否能够创建事物锁
*
* @param seconds
* @return
* @throws NettyTransactionException
*/
@Override
public boolean createNettyTransactionLock(long seconds) throws NettyTransactionException {
boolean result = nettyTransactionLock.create(seconds);
if (rejectFlag) {
result = !result;
}
return result;
}
public void setRejectFlag(boolean rejectFlag) {
this.rejectFlag = rejectFlag;
}
/**
* 释放锁
*/
@Override
public void releaseNettyTransactionLock() {
if (this.nettyTransactionLockType.equals(NettyTransactionLockType.FORCE_WRITE_TIME) || this.nettyTransactionLockType.equals(NettyTransactionLockType.WRITE_TIME)) {
return;
}
this.nettyTransactionLock.destroy();
}
/**
* 记录事务提交的进度,用于回滚操作。
* 根据进度进行不同程度的回滚
*
* @param step
*/
public void setTransactionCommitProgress(int step) {
if (progressBitSet != null) {
progressBitSet.set(step);
}
}
/**
* 检查事物锁所处于的进度状态
*
* @param step
* @return
*/
public boolean checkTransactionCommitProgress(int step) {
return this.progressBitSet.get(step);
}
@Override
public String getInfo() {
return this.nettyTransactionLock.getInfo();
}
/**
* 强制释放锁
*/
@Override
public void forceReleaseNettyTransactionLock() {
this.nettyTransactionLock.destroy();
}
@Override
public boolean needCommit() {
return !this.nettyTransactionLockType.equals(NettyTransactionLockType.READ);
}
@Override
public NettyTransactionLockInterface getNettyTransactionLockInterface() {
return this.nettyTransactionLock;
}
public BitSet getProgressBitSet() {
return progressBitSet;
}
}
代码中的描述一样。所有的具体事务类型都必须要继承此类。保证每个事务都具有基本的操作。而更具实现类来具体实现每个事务产生的不同结果做不同处理。
下面我们来描述一下使用zookeeper的方式来实现分布式事务实体。同样事务锁也具有相同的行为。
package com.twjitm.transaction.transaction.entity;
import com.twjitm.transaction.lock.NettyTransactionLockInterface;
import com.twjitm.transaction.lock.NettyTransactionZkLock;
import com.twjitm.transaction.service.zookeeper.NettyTransactionZookeeperService;
import com.twjitm.transaction.transaction.enums.NettyTransactionEntityCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionLockType;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
import java.util.BitSet;
/**
* 基于zookeeper 实现的分布式事物锁,、
* <p>
* zookeeper 分布式锁仅仅支持独占锁模式
*
* @author twjtim- [Created on 2018-08-29 16:18]
* @jdk java version "1.8.0_77"
*/
public abstract class AbstractNettyTransactionZkEntity implements NettyTransactionEntityInterface {
/**
* 进度设置集合 主要用于rollback
*/
private BitSet progressBitSet;
/**
* 事务锁
*/
private NettyTransactionLockInterface nettyTransactionLock;
/**
* 锁类型
*/
private NettyTransactionLockType nettyTransactionLockType;
/**
* 构建一个zookeeper类型的独占锁实体对象
*
* @param cause
* @param key
* @param zookeeperService
*/
public AbstractNettyTransactionZkEntity(NettyTransactionEntityCause cause,
String key,
NettyTransactionZookeeperService
zookeeperService) {
this.progressBitSet = new BitSet();
this.nettyTransactionLock = new NettyTransactionZkLock(key,
zookeeperService, cause);
this.nettyTransactionLockType = NettyTransactionLockType.WRITE;
}
/**
* 创建一个锁
*
* @param seconds
* @return
* @throws NettyTransactionException
*/
@Override
public boolean createNettyTransactionLock(long seconds) throws NettyTransactionException {
return this.nettyTransactionLock.create(seconds);
}
/**
* 记录事务提交的进度,用于回滚操作。
* 根据进度进行不同程度的回滚
*
* @param step
*/
public void setTransactionCommitProgress(int step) {
if (progressBitSet != null) {
progressBitSet.set(step);
}
}
/**
* 检查事物锁所处于的进度状态
*
* @param step
* @return
*/
public boolean checkTransactionCommitProgress(int step) {
return this.progressBitSet.get(step);
}
/**
* 释放一个锁请求
*/
@Override
public void releaseNettyTransactionLock() {
this.nettyTransactionLock.destroy();
}
@Override
public void forceReleaseNettyTransactionLock() {
this.nettyTransactionLock.destroy();
}
@Override
public String getInfo() {
return this.nettyTransactionLock.getInfo() + this.nettyTransactionLockType.name();
}
@Override
public boolean needCommit() {
return !this.nettyTransactionLockType.equals(NettyTransactionLockType.READ);
}
@Override
public NettyTransactionLockInterface getNettyTransactionLockInterface() {
return this.nettyTransactionLock;
}
}
功能和redis实现的方式是一样的,只不过底层支持的方式不太一样而已。
定义完事务实体后,我们来描述一下事务。事务实体是提供事务行为的描述,而事务本身的属性还需要在事务本身上进行定义。所以我们来定义一个抽象的事务。在定义抽象事物之前我们也要描述事物的具体操作。
有如下定义:
- 0:创建
- 1:尝试提交
- 2:提交
- 3:回滚
- 4:释放
同样我们定义一个事务接口来描述上面的抽象事物的行为。NettyTransactionInterface
package com.twjitm.transaction.transaction;
import com.twjitm.transaction.transaction.enums.NettyTransactionCause;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
/**
* @author twjitm- [Created on 2018-08-27 10:07]
* @jdk java version "1.8.0_77"
*/
public interface NettyTransactionInterface {
/** 激活,构造*/
int ACTIVE = 0;
/** 尝试提交*/
int TRYCOMMITED = 1;
/** 正式提交*/
int COMMITED = 2;
/** 正式回滚*/
int ROLLEDBACK = 3;
/**
* 事务提交
* @throws NettyTransactionException
*/
void commit() throws NettyTransactionException;
/**
* 事务回滚
* @throws NettyTransactionException
*/
void rollback() throws NettyTransactionException;
/**
* 是否可以提交
* @return
*/
boolean canCommit();
/**
* 尝试性提交
*/
void tryCommit() throws NettyTransactionException;
/**
* 获取事务原因
* @return
*/
NettyTransactionCause getCause();
/**
* 是否可以创建锁
* @return
*/
boolean createNettyTransactionLock() throws NettyTransactionException;
/**
* 释放锁
* @return
*/
void releaseNettyTransactionLock();
}
在接口中我们还做了抽象事物状态的定义。描述事物执行到某个阶段。接下来我们需要定义一个抽象类来实现这个接口
package com.twjitm.transaction.transaction;
import com.twjitm.transaction.transaction.entity.NettyTransactionEntityInterface;
import com.twjitm.transaction.transaction.enums.NettyTransactionCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionCommitResult;
import java.util.ArrayList;
import java.util.List;
/**
* @author twjitm- [Created on 2018-08-27 10:13]
* @jdk java version "1.8.0_77"
*/
public abstract class AbstractNettyTransaction implements NettyTransactionInterface {
/**
* 当前执行状态
*/
protected int state;
/**
* 事务实体 可以批量提交事物
*/
public List<NettyTransactionEntityInterface> entities;
/**
* 事务原因
*/
private NettyTransactionCause cause;
/**
* 游戏事务提交结果
*/
protected NettyTransactionCommitResult transactionTryCommitResult;
public AbstractNettyTransaction(NettyTransactionCause cause) {
this.cause = cause;
this.entities = new ArrayList<>();
transactionTryCommitResult = NettyTransactionCommitResult.SUCCESS;
this.state = ACTIVE;
}
public void addEntity(NettyTransactionEntityInterface entity) {
entities.add(entity);
}
@Override
public NettyTransactionCause getCause() {
return cause;
}
@Override
public boolean canCommit() {
return transactionTryCommitResult.equals(NettyTransactionCommitResult.SUCCESS);
}
public NettyTransactionCommitResult getTransactionTryCommitResult() {
return transactionTryCommitResult;
}
}
真正的事物描述是把抽象类中没有实现的接口都实现了。具有全部功能的NettyTransaction
package com.twjitm.transaction.transaction;
import com.twjitm.transaction.config.GlobalConstants;
import com.twjitm.transaction.transaction.entity.NettyTransactionEntityInterface;
import com.twjitm.transaction.transaction.enums.NettyTransactionCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionCommitResult;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
import com.twjitm.transaction.utils.TimeUtil;
/**
* @author twjitm- [Created on 2018-08-27 10:39]
* @jdk java version "1.8.0_77"
*/
public class NettyTransaction extends AbstractNettyTransaction {
/**
* 创建事务锁等待时间
*/
private long waitTime;
/**
* 创建事务实体:
*
* @param cause 事务产生原因
* @param waitTime 等待时间
*/
public NettyTransaction(NettyTransactionCause cause, long waitTime) {
super(cause);
this.waitTime = waitTime;
}
/**
* 创建事务实体:
*
* @param cause 事务产生原因
*/
public NettyTransaction(NettyTransactionCause cause) {
super(cause);
//默认锁最长等待时间,防止网络延迟,服务器宕机等情况。
this.waitTime = GlobalConstants.Lock.TRAINSTACTION_LOCK_KEY_MAX_LIFE;
}
@Override
public void commit() throws NettyTransactionException {
if (state != TRYCOMMITED) {
throw new NettyTransactionException();
}
this.state = COMMITED;
for (NettyTransactionEntityInterface entity : entities) {
if (!entity.needCommit()) {
continue;
}
entity.commit();
}
}
@Override
public void rollback() throws NettyTransactionException {
state = ROLLEDBACK;
for (NettyTransactionEntityInterface entity : entities) {
entity.rollback();
}
}
@Override
public void tryCommit() throws NettyTransactionException {
if (state != ACTIVE) {
throw new NettyTransactionException();
}
this.state = TRYCOMMITED;
for (NettyTransactionEntityInterface entity : entities) {
if (!entity.needCommit()) {
continue;
}
//重复提交没有成功
NettyTransactionCommitResult transactionCommitResult = entity.tryCommit();
if (!transactionCommitResult.equals(NettyTransactionCommitResult.SUCCESS)) {
this.transactionTryCommitResult = transactionCommitResult;
break;
}
}
}
/**
* 是否可以创建一个分布式事物锁
*
* @return
* @throws NettyTransactionException
*/
@Override
public boolean createNettyTransactionLock() throws NettyTransactionException {
if (state != ACTIVE) {
throw new NettyTransactionException();
}
long startSecond = TimeUtil.getSeconds();
boolean createFlag;
if (waitTime > 0) {
while (true) {
long currSeconds = TimeUtil.getSeconds();
createFlag = createNettyTransactionLock(currSeconds);
if (createFlag) {
break;
}
try {
Thread.sleep(TimeUtil.SECOND);
} catch (Throwable e) {
}
currSeconds = TimeUtil.getSeconds();
if (startSecond + waitTime < currSeconds) {
createFlag = false;
break;
}
}
} else {
startSecond = TimeUtil.getSeconds();
createFlag = createNettyTransactionLock(startSecond);
}
return createFlag;
}
private boolean createNettyTransactionLock(long currSeconds) throws NettyTransactionException {
boolean createFlag = false;
for (NettyTransactionEntityInterface entity : entities) {
try {
createFlag = entity.createNettyTransactionLock(currSeconds);
} catch (Exception e) {
throw new NettyTransactionException(e.getMessage());
}
if (!createFlag) {
break;
}
}
return createFlag;
}
/**
* 释放锁
*/
@Override
public void releaseNettyTransactionLock() {
for (NettyTransactionEntityInterface entity : entities) {
entity.releaseNettyTransactionLock();
}
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("transaction ");
buffer.append(getCause());
buffer.append(":");
for (int i = 0; i < entities.size(); i++) {
NettyTransactionEntityInterface entity = entities.get(i);
buffer.append(entity.getInfo());
if (i < entities.size() - 1) {
buffer.append(",");
}
}
return buffer.toString();
}
}
上面就是有关事务、事务实体核心功能的描述,下面我们继续来介绍事务锁的描述和定义,同样的方法我们在事务锁上的操作进行分析,事务锁实体,其实和jdk自带的锁在描述上来说是差不多的。都是为了保护数据而产生的一种行为的定义。
因此我们在项目中的lock包中定义如下类
锁的行为有:
- 1:创建
- 2:注销
- 3:设置内容
根据上面定义的事务锁行为。我们有如下接口的描述NettyTransactionLockInterface.java
package com.twjitm.transaction.lock;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
/**
* 事务锁接口
* <p>
* 本类面向的是锁。主要是为事物实体提供原子操作
* 利用redis 的原子操作实现分布式事物锁,
* 该抽象接口定义了锁的基本操作。
* </p>
*/
public interface NettyTransactionLockInterface {
/**
* 销毁
*/
void destroy();
/**
* 创建
* @return
*/
boolean create(long seconds) throws NettyTransactionException;
/**
* 获取信息
* @return
*/
String getInfo();
/**
* 设置内容
*/
void setContent(String lockContent);
}
还记得我们前面描述的锁的种类吗?我们有写锁,读锁,强占锁,超时锁等,下面我们来实现这些锁的具体实体类。NettyTransactionLock
package com.twjitm.transaction.lock;
import com.twjitm.transaction.service.redis.NettyTransactionRedisService;
import com.twjitm.transaction.transaction.enums.NettyTransactionEntityCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionLockStateEnum;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
import com.twjitm.transaction.utils.TimeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* netty 事物锁,基于redis实现
* <pre>
* 写锁
* </pre>
*
* @author twjitm- [Created on 2018-08-27 12:05]
* @jdk java version "1.8.0_77"
*/
public class NettyTransactionLock implements NettyTransactionLockInterface {
private Logger logger = LoggerFactory.getLogger(NettyTransactionLock.class);
/**
* 事物锁关键字
*/
private String lockKey;
/**
* 事物锁创建需要的redis服务
*/
private NettyTransactionRedisService redisService;
/**
* 事务支出的zookeeper服务器
*
*
*/
/**
* 事物产生实体的原因
*/
private NettyTransactionEntityCause cause;
/**
* 事物锁状态
*/
private NettyTransactionLockStateEnum lockStateEnum;
/**
* 锁时长
*/
private int lockTime;
/**
* 锁强制标识
*/
private boolean forceFlag;
/**
* 锁内容
*/
private String lockContext = "";
/**
* 初始化一个写锁,默认锁时间为系统配置时间。
*
* @param lockKey
* @param redisService
* @param cause
*/
public NettyTransactionLock(String lockKey, NettyTransactionRedisService redisService, NettyTransactionEntityCause cause) {
super();
this.lockKey = lockKey;
this.redisService = redisService;
this.cause = cause;
this.lockStateEnum = NettyTransactionLockStateEnum.INIT;
this.lockTime = TimeUtil.MINUTE_SECOND;
}
/**
* 初始化一个写锁。并且指定锁时间,是否具有强制性。
*
* @param lockKey
* @param redisService
* @param cause
* @param lockTime
* @param forceFlag
*/
public NettyTransactionLock(String lockKey, NettyTransactionRedisService redisService, NettyTransactionEntityCause cause, int lockTime, boolean forceFlag) {
super();
this.lockKey = lockKey;
this.redisService = redisService;
this.cause = cause;
this.lockStateEnum = NettyTransactionLockStateEnum.INIT;
this.lockTime = lockTime;
this.forceFlag = forceFlag;
}
/**
* 初始化一个具有内容的写锁,并且制定锁时间和强制性标志。以及所内容
*
* @param lockKey
* @param redisService
* @param cause
* @param lockTime
* @param forceFlag
* @param lockContext
*/
public NettyTransactionLock(String lockKey, NettyTransactionRedisService redisService, NettyTransactionEntityCause cause, int lockTime, boolean forceFlag, String lockContext) {
super();
this.lockKey = lockKey;
this.redisService = redisService;
this.cause = cause;
this.lockStateEnum = NettyTransactionLockStateEnum.INIT;
this.lockTime = lockTime;
this.forceFlag = forceFlag;
this.lockContext = lockContext;
}
/**
* <p>
* 注销一个锁。
* 锁的注销是有条件的。锁不能再初始化的时候和创建的时候注销。
* 只能这个锁创建成功后才能注销。创建一个锁。将必须使用这个锁。若创建一个锁不进行使用
* 的话,将无法注销这个锁。只能等锁时间过期后才能自动注销锁
* </p>
*/
@Override
public void destroy() {
if (this.lockStateEnum.equals(NettyTransactionLockStateEnum.INIT) ||
this.lockStateEnum.equals(
NettyTransactionLockStateEnum.CREATE)) {
return;
}
boolean destroyFlag = true;
if (!lockContext.equals("".trim())) {
destroyFlag = checkLockContext();
}
String realLockKey = getLockKey(lockKey, cause);
if (destroyFlag) {
boolean delete = redisService.deleteKey(realLockKey);
if (!delete) {
logger.info("居然没有删除掉这个key=" + realLockKey);
}
}
}
/**
* 检测锁内容
*
* @return
*/
private boolean checkLockContext() {
boolean checkFlag = false;
String content = redisService.getString(getLockKey(lockKey, cause));
if (content != null) {
checkFlag = content.equals(this.lockContext);
}
return checkFlag;
}
/**
* 获取锁可以
*
* @param lockKey
* @param cause
* @return
*/
public String getLockKey(String lockKey, NettyTransactionEntityCause cause) {
return lockKey + "#" + cause.getCause();
}
/**
* 创建分布式事物锁,创建一个分布式事物锁的代价是比较高的,
* 应为需要将请求消息发送到对应的redis服务器或者是zookeeper服务器
* 但是当我们逻辑服务器和redis不在同一台服务器的时候,我们需要走网络层
* 连接,相当于开启一个tcp连接通道。这条通道主要是为了我们能够与redis或者zookeeper
* 服务器进行通讯,为可防止单点问题,我们可以将redis做成集群模式,同样zookeeper也
* 一样。当然在这个地方我们默认使用redis实现分布式所务锁,当别的逻辑服务器申请锁的
* 的时候也会进行创建。利用redis的原子性,保证本锁的原子性。
*
* @param seconds
* @return
* @throws NettyTransactionException
*/
@Override
public boolean create(long seconds) throws NettyTransactionException {
this.lockStateEnum = NettyTransactionLockStateEnum.CREATE;
boolean createFlag;
String realLockKey = getLockKey(lockKey, cause);
try {
//设置锁标识
createFlag = redisService.setNxString(realLockKey, lockContext, lockTime);
if (createFlag) {
this.lockStateEnum = NettyTransactionLockStateEnum.SUCCESS;
logger.info("创建锁成功");
redisService.expire(realLockKey, lockTime);
} else {
if (forceFlag) {
this.lockStateEnum = NettyTransactionLockStateEnum.SUCCESS;
redisService.setString(realLockKey, lockContext, lockTime);
redisService.expire(realLockKey, lockTime);
createFlag = true;
logger.info("创建强制锁:" + realLockKey + ",过期时间长度为: " + lockTime);
} else {
createFlag = false;
logger.info("创建锁失败" + realLockKey + ",过期时间为: " + lockTime);
}
}
} catch (Exception e) {
throw new NettyTransactionException("创建锁发生意想不到的错误,请检查");
}
return createFlag;
}
@Override
public String getInfo() {
return lockKey + cause + checkLockContext() + lockTime;
}
@Override
public void setContent(String lockContent) {
this.lockContext = lockContent;
}
}
其实代码中已经有很详细的介绍了。每个部分的功能都可以在注释中可以看到。正如事务锁的创建。代码做了详细的描述。在这就省略了。
读锁
package com.twjitm.transaction.lock;
import com.twjitm.transaction.service.redis.NettyTransactionRedisService;
import com.twjitm.transaction.transaction.enums.NettyTransactionEntityCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionLockStateEnum;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* netty 游戏事物锁,基于redis实现的分布式游戏事务锁
* <pre>
* 读锁
* </pre>
*
* @author twjitm - [Created on 2018-08-27 12:14]
* @jdk java version "1.8.0_77"
*/
public class NettyTransactionReadLock implements NettyTransactionLockInterface {
private Logger logger = LoggerFactory.getLogger(NettyTransactionReadLock.class);
/**
* 事物锁key
*/
private String lockKey;
/**
* 锁提供的redis
*/
private NettyTransactionRedisService redisService;
/**
* 事物锁参数原因
*/
private NettyTransactionEntityCause cause;
/**
* 分布式读锁状态
*/
private NettyTransactionLockStateEnum lockState;
/**
* 分布式读锁内容
*/
private String lockContext;
public NettyTransactionReadLock(String lockKey, NettyTransactionRedisService redisService, NettyTransactionEntityCause cause) {
super();
this.lockKey = lockKey;
this.redisService = redisService;
this.cause = cause;
this.lockState = NettyTransactionLockStateEnum.INIT;
}
/**
* 注销一个锁
*/
@Override
public void destroy() {
if (this.lockState == NettyTransactionLockStateEnum.INIT || this.lockState == NettyTransactionLockStateEnum.CREATE) {
return;
}
boolean exists = redisService.exists(getLockKey(lockKey, cause));
if (exists && !StringUtils.isEmpty(lockContext)) {
exists = this.checkLockContext();
if (exists) {
redisService.deleteKey(getLockKey(lockKey, cause));
}
}
}
@Override
public boolean create(long seconds) throws NettyTransactionException {
this.lockState = NettyTransactionLockStateEnum.CREATE;
//检测值是否存在
boolean exists = redisService.exists(getLockKey(lockKey, cause));
//检测内容是否为空
if (exists && !StringUtils.isEmpty(lockContext)) {
exists = this.checkLockContext();
}
return exists;
}
private boolean checkLockContext() {
boolean checkFlag = false;
String realLockKey = getLockKey(lockKey, cause);
String content = redisService.getString(realLockKey);
if (!StringUtils.isEmpty(content)) {
logger.info("read content realLockKey:" + realLockKey);
checkFlag = content.equals(this.lockContext);
}
return checkFlag;
}
/**
* 获取锁可以
*
* @param lockKey
* @param cause
* @return
*/
public String getLockKey(String lockKey, NettyTransactionEntityCause cause) {
return lockKey + "#" + cause.getCause();
}
@Override
public String getInfo() {
return this.lockKey + this.cause + this.lockContext;
}
@Override
public void setContent(String lockContent) {
this.lockContext = lockContent;
}
}
基于zookeeper实现的写锁。NettyTransactionZkLock
package com.twjitm.transaction.lock;
import com.twjitm.transaction.service.zookeeper.NettyTransactionZookeeperService;
import com.twjitm.transaction.transaction.enums.NettyTransactionEntityCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionLockStateEnum;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 基于zookeeper分布式事务锁实体,zookeeper 实现分布式锁
* 基于zookeeper实现分布式锁存在的缺点:
* 由于zookeeper天生的特性,我们在创建节点的时候最好创建临时节点
* 防止长期占用锁,造成死锁。由于未知原因,可能程序释放锁失败。
*
* @author twjitm- [Created on 2018-08-29 14:52]
*/
public class NettyTransactionZkLock implements NettyTransactionLockInterface {
private Logger logger = LoggerFactory.getLogger(NettyTransactionZkLock.class);
/**
* 事物锁关键字
*/
private String lockKey;
/**
* 事物锁创建需要的zookeeper服务
*/
private NettyTransactionZookeeperService zookeeperService;
/**
* 事物锁参数原因
*/
private NettyTransactionEntityCause cause;
/**
* 事物锁装填
*/
private NettyTransactionLockStateEnum lockStateEnum;
/**
* 分布式读锁内容
*/
private String lockContext="";
public NettyTransactionZkLock(String lockKey, NettyTransactionZookeeperService
zookeeperService,
NettyTransactionEntityCause cause) {
super();
this.lockKey = lockKey;
this.zookeeperService = zookeeperService;
this.cause = cause;
this.lockContext="";
}
public NettyTransactionZkLock(String lockKey,
NettyTransactionZookeeperService zookeeperService,
NettyTransactionEntityCause cause,
NettyTransactionLockStateEnum lockState) {
super();
this.lockKey = lockKey;
this.zookeeperService = zookeeperService;
this.cause = cause;
this.lockStateEnum = lockState;
}
public NettyTransactionZkLock(String lockKey, NettyTransactionZookeeperService
zookeeperService,
NettyTransactionEntityCause cause,
NettyTransactionLockStateEnum lockState, String
lockContext) {
super();
this.lockKey = lockKey;
this.zookeeperService = zookeeperService;
this.cause = cause;
this.lockStateEnum = lockState;
this.lockContext = lockContext;
}
/**
* 注销一个锁
*/
@Override
public void destroy() {
//这两种状态不能注销锁
if (this.lockStateEnum.equals(NettyTransactionLockStateEnum.INIT) ||
this.lockStateEnum.equals(
NettyTransactionLockStateEnum.CREATE)) {
return;
}
String realLockKey = getLockKey(lockKey, cause);
boolean delete = zookeeperService.deleteNode(realLockKey);
if (!delete) {
logger.info("居然没有删除掉这个key=" + realLockKey);
}
}
/**
* 创建锁节点
*
* @param lockKey
* @param cause
* @return
*/
public String getLockKey(String lockKey, NettyTransactionEntityCause cause) {
return lockKey + "_" + cause.getCause();
}
/**
* 创建锁
*
* @param
* @return
* @throws NettyTransactionException
*/
@Override
public boolean create(long seconds) throws NettyTransactionException {
this.lockStateEnum = NettyTransactionLockStateEnum.CREATE;
boolean createFlag;
String realKey = getLockKey(lockKey, cause);
//创建节点
createFlag = zookeeperService.createNode(realKey, lockContext);
if (createFlag) {
this.lockStateEnum = NettyTransactionLockStateEnum.SUCCESS;
logger.info("创建锁成功" + this.getInfo());
} else{
logger.info("获得锁失败" + this.getInfo());
}
return createFlag;
}
@Override
public String getInfo() {
return this.lockKey + cause.getCause() + this.lockStateEnum.name() +
this.lockContext;
}
@Override
public void setContent(String lockContent) {
this.lockContext = lockContent;
}
}
到此,有关事物锁的定义就完成。基础组件定义完成之后我们需要对外提供事务锁服务。这是我们系统所具有的意义。对外提供分布式事务锁,是我们的核心功能。上诉的功能执行为了实现这个分布式事务锁功能的必备组件,因此我们定义一个服务接口。
package com.twjitm.transaction.service.transaction;
import com.twjitm.transaction.transaction.entity.AbstractNettyTransactionEntity;
import com.twjitm.transaction.transaction.entity.AbstractNettyTransactionZkEntity;
import com.twjitm.transaction.transaction.enums.NettyTransactionCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionCommitResult;
/**
* 事物对外提供的服务
* 可以批量提交事物
*
* @author twjitm- [Created on 2018-08-27 15:42]
* @jdk java version "1.8.0_77"
*/
public interface NettyTransactionService {
/**
* redis 模式提交事务
* @param cause
* @param abstractGameTransactionEntity
* @return
*/
NettyTransactionCommitResult commitTransaction(NettyTransactionCause cause, AbstractNettyTransactionEntity... abstractGameTransactionEntity);
/**
* redis 模式提交事务
* @param cause
* @param waitTime
* @param abstractGameTransactionEntity
* @return
*/
NettyTransactionCommitResult commitTransaction(NettyTransactionCause cause, long waitTime, AbstractNettyTransactionEntity... abstractGameTransactionEntity);
/**
* zookeeper 模式来提交事物锁
*
* @param cause
* @param abstractNettyTransactionZkEntities
* @return
*/
NettyTransactionCommitResult commitTransaction(NettyTransactionCause cause, AbstractNettyTransactionZkEntity... abstractNettyTransactionZkEntities);
}
接口定义的几个方法都是具有相同的意义。都是提交事物。只不过一个是redis实现的,一个是zookeeper实现的。接口定义好了我们需要实现接口中的方法。下面是本系统的最核心的关键之处。
package com.twjitm.transaction.service.transaction;
import com.twjitm.transaction.transaction.NettyTransaction;
import com.twjitm.transaction.transaction.entity.AbstractNettyTransactionEntity;
import com.twjitm.transaction.transaction.entity.AbstractNettyTransactionZkEntity;
import com.twjitm.transaction.transaction.entity.NettyTransactionEntityInterface;
import com.twjitm.transaction.transaction.enums.NettyTransactionCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionCommitResult;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
* 事物提交服务
*
* @author twjitm- [Created on 2018-08-27 15:44]
* @jdk java version "1.8.0_77"
*/
@Service
public class NettyTransactionServiceImpl implements NettyTransactionService {
Logger logger = LoggerFactory.getLogger(NettyTransactionServiceImpl.class);
@Override
public NettyTransactionCommitResult commitTransaction(NettyTransactionCause transactionCause, AbstractNettyTransactionEntity... abstractGameTransactionEntity) {
NettyTransaction transaction = new NettyTransaction(transactionCause);
return commitTransaction(transaction, abstractGameTransactionEntity);
}
@Override
public NettyTransactionCommitResult commitTransaction(NettyTransactionCause gameTransactionCause, long waitTime, AbstractNettyTransactionEntity... abstractGameTransactionEntity) {
NettyTransaction transaction = new NettyTransaction(gameTransactionCause, waitTime);
return commitTransaction(transaction, abstractGameTransactionEntity);
}
@Override
public NettyTransactionCommitResult commitTransaction(NettyTransactionCause cause, AbstractNettyTransactionZkEntity... abstractNettyTransactionZkEntities) {
NettyTransaction transaction = new NettyTransaction(cause);
return commitTransaction(transaction, abstractNettyTransactionZkEntities);
}
/**
* 二阶段和三阶段的区别
* http://www.hollischuang.com/archives/681
*
* @param transaction 事务
* @param abstractGameTransactionEntities 事务实体集和
* @return 事务执行返回结果
*/
private NettyTransactionCommitResult commitTransaction(NettyTransaction transaction, NettyTransactionEntityInterface... abstractGameTransactionEntities) {
NettyTransactionCommitResult tryCommitResult = NettyTransactionCommitResult.SUCCESS;
for (NettyTransactionEntityInterface entityInterface : abstractGameTransactionEntities) {
transaction.addEntity(entityInterface);
}
try {
//如果能够创建分布式服务器锁
if (transaction.createNettyTransactionLock()) {
logger.info("成功获得锁: " + transaction.toString());
logger.info("尝试提交锁: " + transaction.toString());
transaction.tryCommit();
if (transaction.canCommit()) {
logger.info("正式提交锁: " + transaction.toString());
transaction.commit();
logger.info("提交锁成功: " + transaction.toString());
} else {
logger.info("重复提交锁: " + transaction.toString());
tryCommitResult = transaction.getTransactionTryCommitResult();
logger.info("重复提交锁失败: " + transaction.toString());
}
} else {
logger.info("获得锁失败: " + transaction.toString());
tryCommitResult = NettyTransactionCommitResult.LOCK_ERROR;
}
} catch (Exception e) {
logger.info("提交锁发生异常: " + transaction.toString());
try {
logger.info("开始回滚锁: " + transaction.toString());
transaction.rollback();
logger.info("回滚锁成功: " + transaction.toString());
} catch (NettyTransactionException e1) {
e1.printStackTrace();
logger.info("回滚锁发生异常: " + transaction.toString());
}
//异常事务原因
tryCommitResult = NettyTransactionCommitResult.COMMON_ERROR;
if (e instanceof NettyTransactionException) {
NettyTransactionException exception = (NettyTransactionException) e;
NettyTransactionCommitResult tempGameTransactionTryCommitResult =
exception.getResult();
if (tempGameTransactionTryCommitResult != null) {
tryCommitResult = tempGameTransactionTryCommitResult;
}
}
} finally {
//释放锁
logger.info("释放锁开始: " + transaction.toString());
transaction.releaseNettyTransactionLock();
logger.info("释放锁成功: " + transaction.toString());
}
return tryCommitResult;
}
}
由于篇幅原因,我们就不统统描述全部代码了。其实代码已经做了详细的介绍。相信有点java基础的同学都能够看明白。只不过此工程描述的是一种思想而已。
到此核心代码基本介绍完。那我们就来测试一下。前面在先睹为快的地方的时候我们已经看到了。下面我们来详细说明一下如何使用分布式事务锁框架。
如何使用
互斥锁
首先我们定义一个实体。MutexEntity需要继承AbstractNettyTransactionEntity实现为实现的方法。
package com.twjitm.transaction.entity;
import com.twjitm.transaction.service.redis.NettyTransactionRedisService;
import com.twjitm.transaction.transaction.entity.AbstractNettyTransactionEntity;
import com.twjitm.transaction.transaction.enums.NettyTransactionCommitResult;
import com.twjitm.transaction.transaction.enums.NettyTransactionEntityCause;
import com.twjitm.transaction.transaction.exception.NettyTransactionException;
import java.util.BitSet;
/**
* 互斥锁测试 实体
*
* @author twjtim- [Created on 2018-08-27 18:12]
* @jdk java version "1.8.0_77"
*/
public class MutexEntity extends AbstractNettyTransactionEntity {
NettyTransactionRedisService redisService;
String testKey;
public MutexEntity(NettyTransactionEntityCause cause, String key,
NettyTransactionRedisService redisService) {
super(cause, key, redisService);
this.redisService = redisService;
this.testKey = key;
}
@Override
public void commit() throws NettyTransactionException {
redisService.setString("mutex_test", "twjitm");
throw new NullPointerException();
}
@Override
public void rollback() throws NettyTransactionException {
BitSet bitset = getProgressBitSet();
for (int i = 0; i < bitset.size(); i++) {
//@TODO 不同粒度回滚
}
redisService.deleteKey("mutex_test");
}
@Override
public NettyTransactionCommitResult tryCommit() throws NettyTransactionException {
return NettyTransactionCommitResult.SUCCESS;
}
}
在测试中我们模拟在提交锁阶段抛出一个空指针异常,在回滚阶段我们来做一些操作。
测试类
package com.twjitm.transaction.mtex;
import com.twjitm.transaction.entity.MutexEntity;
import com.twjitm.transaction.service.redis.impl.NettyTransactionRedisServiceImpl;
import com.twjitm.transaction.service.transaction.NettyTransactionServiceImpl;
import com.twjitm.transaction.spring.TestSpring;
import com.twjitm.transaction.transaction.enums.NettyTransactionCause;
import com.twjitm.transaction.transaction.enums.NettyTransactionCommitResult;
import com.twjitm.transaction.transaction.enums.NettyTransactionEntityCause;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.Assert;
/**
* 互斥锁测试
*
* @author twjitm- [Created on 2018-08-28 10:31]
*/
public class TestMutex {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = TestSpring.initSpring();
NettyTransactionRedisServiceImpl nettyTransactionRedisService =
(NettyTransactionRedisServiceImpl) applicationContext.getBean
("nettyTransactionRedisService");
NettyTransactionServiceImpl nettyTransactionService = (NettyTransactionServiceImpl) applicationContext.getBean("nettyTransactionServiceImpl");
NettyTransactionEntityCause cause = new NettyTransactionEntityCause("mutex");
MutexEntity mutexEntity = new MutexEntity(cause, "mutex", nettyTransactionRedisService);
NettyTransactionCause transactionCause = new NettyTransactionCause("mutex");
NettyTransactionCommitResult result =
nettyTransactionService.commitTransaction(transactionCause, mutexEntity);
System.out.println(result.getResult());
Assert.isTrue(true,"");
}
}
用一个简单的main函数启动系统,测试功能。
执行结果:
下面我们通过一个流程图来简单总结本系统
项目源码开源道我的GitHub,有需要的同学可以下载。欢迎star