Seata源码分析之Session

狂风中的少年 提交于 2019-12-24 12:53:37

目录

一、GlobalSession

二、GlobalSessionLock

三、BranchSession

四、DefaultLockManager

五、Locker

六、MemoryLocker


一、GlobalSession

GlobalSession是seata协调器DefaultCoordinator管理维护的重要部件,当用户开启全球分布式事务,TM调用begin方法请求至TC,TC则创建GlobalSession实例对象,返回唯一的xid。它实现SessionLifecycle接口,提供begin,changeStatus,changeBranchStatus,addBranch,removeBranch等操作session和branchSession的方法。

public class GlobalSession implements SessionLifecycle, SessionStorable {

    private String xid;

    private long transactionId;

    private volatile GlobalStatus status;

    private String applicationId;

    private String transactionServiceGroup;

    private String transactionName;

    private int timeout;

    private long beginTime;

    private String applicationData;

    private boolean active = true;

    private final ArrayList<BranchSession> branchSessions = new ArrayList<>();
    // 添加branchSession
    public boolean add(BranchSession branchSession) {
        return branchSessions.add(branchSession);
    }

    // 删除branchSession
    public boolean remove(BranchSession branchSession) {
        return branchSessions.remove(branchSession);
    }

    // 是否超时
    public boolean isTimeout() {
        return (System.currentTimeMillis() - beginTime) > timeout;
    }

    @Override
    // session开启,设置状态为Begin
    public void begin() throws TransactionException {
        this.status = GlobalStatus.Begin;
        this.beginTime = System.currentTimeMillis();
        this.active = true;
        for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
            lifecycleListener.onBegin(this);
        }
    }

    // 修改seseion状态
    public void changeStatus(GlobalStatus status) throws TransactionException {
        this.status = status;
        for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
            lifecycleListener.onStatusChange(this, status);
        }
    }

    // 修改branchSession状态
    public void changeBranchStatus(BranchSession branchSession, BranchStatus status)
        throws TransactionException {
        branchSession.setStatus(status);
        for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
            lifecycleListener.onBranchStatusChange(this, branchSession, status);
        }
    }

    @Override
    // 添加branchSession
    public void addBranch(BranchSession branchSession) throws TransactionException {
        for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
            lifecycleListener.onAddBranch(this, branchSession);
        }
        branchSession.setStatus(BranchStatus.Registered);
        add(branchSession);
    }

    @Override
    // 删除branchSession
    public void removeBranch(BranchSession branchSession) throws TransactionException {
        for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
            lifecycleListener.onRemoveBranch(this, branchSession);
        }
        branchSession.unlock();
        remove(branchSession);
    }
}

二、GlobalSessionLock

GlobalSessionLock是GlobalSession中的内部类,它内部持有ReentrantLock对象,利用ReentrantLock的lock和unlock机制。GlobalSession中默认持有一个globalSessionLock对象,并提供lockAndExcute在GlobalSession中的线程安全操作的模板方法。

public class GlobalSession implements SessionLifecycle, SessionStorable {

     private GlobalSessionLock globalSessionLock = new GlobalSessionLock();

     public void lock() throws TransactionException {
        globalSessionLock.lock();
    }

    public void unlock() {
        globalSessionLock.unlock();
    }

    public void lockAndExcute(LockRunnable excuteRunnable) throws TransactionException {
        this.lock();
        try {
            excuteRunnable.run();
        } finally {
            this.unlock();
        }
    }

    public <T> T lockAndExcute(LockCallable<T> lockCallable) throws TransactionException {
        this.lock();
        try {
            return lockCallable.call();
        } finally {
            this.unlock();
        }
    }

    private class GlobalSessionLock {

        private Lock globalSessionLock = new ReentrantLock();

        private static final int GLOBAL_SESSION_LOCK_TIME_OUT_MILLS = 2 * 1000;

        public void lock() throws TransactionException {
            try {
                if (globalSessionLock.tryLock(GLOBAL_SESSION_LOCK_TIME_OUT_MILLS, TimeUnit.MILLISECONDS)) {
                    return;
                }
            } catch (InterruptedException e) {
                LOGGER.error("Interrupted error", e);
            }
            throw new TransactionException(TransactionExceptionCode.FailedLockGlobalTranscation);
        }

        public void unlock() {
            globalSessionLock.unlock();
        }

    }

    @FunctionalInterface
    public interface LockRunnable {

        void run() throws TransactionException;
    }

    @FunctionalInterface
    public interface LockCallable<V> {

        V call() throws TransactionException;
    }
}

三、BranchSession

BranchSession为分支session,管理分支数据,受globalSession统一调度管理,它的lock和unlock方法由lockManger实现。

public class BranchSession implements Lockable, Comparable<BranchSession>, SessionStorable {
    
    private String xid;

    private long transactionId;

    private long branchId;

    private String resourceGroupId;

    private String resourceId;

    private String lockKey;

    private BranchType branchType;

    private BranchStatus status = BranchStatus.Unknown;

    private String clientId;

    private String applicationData;

    @Override
    public boolean lock() throws TransactionException {
        return LockerFactory.getLockManager().acquireLock(this);
    }

    @Override
    public boolean unlock() throws TransactionException {
        return LockerFactory.getLockManager().releaseLock(this);
    }
}

四、DefaultLockManager

DefaultLockManager是LockManager的默认实现,它获取branchSession的lockKey,转换成List<RowLock>,委派Locker进行处理。

public class DefaultLockManager extends AbstractLockManager {

    private static Locker locker = null;

    @Override
    public boolean acquireLock(BranchSession branchSession) throws TransactionException {
        String lockKey = branchSession.getLockKey();
        if (StringUtils.isNullOrEmpty(lockKey)) {
            //no lock
            return true;
        }
        //get locks of branch
        List<RowLock> locks = collectRowLocks(branchSession);
        if (CollectionUtils.isEmpty(locks)) {
            //no lock
            return true;
        }
        return getLocker(branchSession).acquireLock(locks);
    }

    @Override
    public boolean releaseLock(BranchSession branchSession) throws TransactionException {
        List<RowLock> locks = collectRowLocks(branchSession);
        if (CollectionUtils.isEmpty(locks)) {
            //no lock
            return true;
        }
        try {
            return getLocker(branchSession).releaseLock(locks);
        } catch (Exception t) {
            LOGGER.error("unLock error, branchSession:" + branchSession, t);
            return false;
        }
    }

    @Override
    public boolean isLockable(String xid, String resourceId, String lockKey) throws TransactionException {
        List<RowLock> locks = collectRowLocks(lockKey, resourceId, xid);
        try {
            return getLocker().isLockable(locks);
        } catch (Exception t) {
            LOGGER.error("isLockable error, xid:" + xid + ", resourceId:" + resourceId + ", lockKey:" + lockKey, t);
            return false;
        }
    }

    @Override
    public void cleanAllLocks() throws TransactionException {
        getLocker().cleanAllLocks();
    }

    protected Locker getLocker() {
        return getLocker(null);
    }

    protected Locker getLocker(BranchSession branchSession) {
        return LockerFactory.get(branchSession);
    }
}

五、Locker

Locker接口提供根据行数据获取锁,释放锁,是否锁住和清除所有锁的方法。有DataBaseLocker和MemoryLocker两种实现方式,可以根据用户配置db或file进行不同的加载。

public interface Locker {

    boolean acquireLock(List<RowLock> rowLock) ;

    boolean releaseLock(List<RowLock> rowLock);

    boolean isLockable(List<RowLock> rowLock);

    void cleanAllLocks();
}

六、MemoryLocker

MemoryLocker即是使用seata服务器的内存map进行缓存实现锁功能。

public class MemoryLocker extends AbstractLocker {

    private static final int BUCKET_PER_TABLE = 128;

    private static final ConcurrentHashMap<String/* resourceId */,
        ConcurrentHashMap<String/* tableName */,
            ConcurrentHashMap<Integer/* bucketId */,
                Map<String/* pk */, Long/* transactionId */>>>>
        LOCK_MAP
        = new ConcurrentHashMap<String, ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>>>();

    protected BranchSession branchSession = null;

    public MemoryLocker(BranchSession branchSession) {
        this.branchSession = branchSession;
    }

    @Override
    public boolean acquireLock(List<RowLock> rowLocks) {
        if (CollectionUtils.isEmpty(rowLocks)) {
            //no lock
            return true;
        }
        String resourceId = branchSession.getResourceId();
        long transactionId = branchSession.getTransactionId();

        ConcurrentHashMap<Map<String, Long>, Set<String>> bucketHolder = branchSession.getLockHolder();
        ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>> dbLockMap = LOCK_MAP.get(resourceId);
        if (dbLockMap == null) {
            LOCK_MAP.putIfAbsent(resourceId,
                new ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>>());
            dbLockMap = LOCK_MAP.get(resourceId);
        }

        for (RowLock lock : rowLocks) {
            String tableName = lock.getTableName();
            String pk = lock.getPk();
            ConcurrentHashMap<Integer, Map<String, Long>> tableLockMap = dbLockMap.get(tableName);
            if (tableLockMap == null) {
                dbLockMap.putIfAbsent(tableName, new ConcurrentHashMap<Integer, Map<String, Long>>());
                tableLockMap = dbLockMap.get(tableName);
            }
            int bucketId = pk.hashCode() % BUCKET_PER_TABLE;
            Map<String, Long> bucketLockMap = tableLockMap.get(bucketId);
            if (bucketLockMap == null) {
                tableLockMap.putIfAbsent(bucketId, new ConcurrentHashMap<String, Long>());
                bucketLockMap = tableLockMap.get(bucketId);
            }
            synchronized (bucketLockMap) {
                Long lockingTransactionId = bucketLockMap.get(pk);
                if (lockingTransactionId == null) {
                    //No existing lock
                    bucketLockMap.put(pk, transactionId);
                    Set<String> keysInHolder = bucketHolder.get(bucketLockMap);
                    if (keysInHolder == null) {
                        bucketHolder.putIfAbsent(bucketLockMap, new ConcurrentSet<String>());
                        keysInHolder = bucketHolder.get(bucketLockMap);
                    }
                    keysInHolder.add(pk);

                } else if (lockingTransactionId.longValue() == transactionId) {
                    // Locked by me
                    continue;
                } else {
                    LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + lockingTransactionId);
                    try {
                        // Release all acquired locks.
                        branchSession.unlock();
                    } catch (TransactionException e) {
                        throw new FrameworkException(e);
                    }
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public boolean releaseLock(List<RowLock> rowLock) {
        ConcurrentHashMap<Map<String, Long>, Set<String>> lockHolder = branchSession.getLockHolder();
        if (lockHolder == null || lockHolder.size() == 0) {
            return true;
        }
        Iterator<Map.Entry<Map<String, Long>, Set<String>>> it = lockHolder.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<Map<String, Long>, Set<String>> entry = it.next();
            Map<String, Long> bucket = entry.getKey();
            Set<String> keys = entry.getValue();
            synchronized (bucket) {
                for (String key : keys) {
                    Long v = bucket.get(key);
                    if (v == null) {
                        continue;
                    }
                    if (v.longValue() == branchSession.getTransactionId()) {
                        bucket.remove(key);
                    }
                }
            }
        }
        lockHolder.clear();
        return true;
    }

    @Override
    public boolean isLockable(List<RowLock> rowLocks) {
        if (CollectionUtils.isEmpty(rowLocks)) {
            //no lock
            return true;
        }
        Long transactionId = rowLocks.get(0).getTransactionId();
        String resourceId = rowLocks.get(0).getResourceId();
        ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>> dbLockMap = LOCK_MAP.get(resourceId);
        if (dbLockMap == null) {
            return true;
        }
        for (RowLock rowLock : rowLocks) {
            String xid = rowLock.getXid();
            String tableName = rowLock.getTableName();
            String pk = rowLock.getPk();

            ConcurrentHashMap<Integer, Map<String, Long>> tableLockMap = dbLockMap.get(tableName);
            if (tableLockMap == null) {
                continue;
            }
            int bucketId = pk.hashCode() % BUCKET_PER_TABLE;
            Map<String, Long> bucketLockMap = tableLockMap.get(bucketId);
            if (bucketLockMap == null) {
                continue;
            }
            Long lockingTransactionId = bucketLockMap.get(pk);
            if (lockingTransactionId == null || lockingTransactionId.longValue() == transactionId) {
                // Locked by me
                continue;
            } else {
                LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + lockingTransactionId);
                return false;
            }
        }
        return true;
    }

    @Override
    public void cleanAllLocks() {
        LOCK_MAP.clear();
    }
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!