Netty游戏服务器实战开发(8):利用redis或者zookeeper实现3pc分布式事务锁(二)。支撑腾讯系列某手游百万级流量公测

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_23086307/article/details/82978107

导读:在上篇文章中介绍了分布式事务项目的基本原理和工程组件,我们了解到了分布式事务的理论知识。处于实战的经验,我们将理论知识使用到实际项目中。所以我们将借助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

分布式事务锁源码

阅读更多

扫码向博主提问

twjitm

博客专家

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • java
  • spring
  • mybatis
  • hibernate
  • javaweb
去开通我的Chat快问
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页