開源測試服務

  • 終於測試完成了,也上線了,雖然有些曲折,一期目標基本達成。

項目地址:https://github.com/JunManYuan...,我以爲出去測試框架部分的內容之外,有兩個地方值得借鑑​。開發過程當中遇到的問題和寫過的BUG都在測開筆記裏面了,有興趣能夠一讀。java

號外:這個倉庫裏面都是一些開源測試框架和測試平臺,你們有GitHub帳號的請不要吝嗇星星。

多線程

多線程處理用例參數和執行用例場景下,線程池的引入。這個首先解決了多用例運行的耗時太多的問題,其次也解決了每次處理任務新建線程對於性能的消耗。git

具體的方案就是新建一個全局的線程池,而後把全部多線程任務包裝成一個線程對象,經過將任務丟到線程池中,而後經過CountDownLatch這個類實現等待執行結束,而後進行下一步操做。具體可參考:- CountDownLatch類在性能測試中應用github

核心代碼以下:數據庫

線程池

package com.okay.family.common.threadpool;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 自定義線程池,用例批量運行用例,非併發測試線程池
 */
public class OkayThreadPool {

    private static ThreadPoolExecutor executor = createPool();

    public static void addSyncWork(Runnable runnable) {
        executor.execute(runnable);
    }

    private static ThreadPoolExecutor createPool() {
        return new ThreadPoolExecutor(16, 100, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));
    }

}

多線程類:

package com.okay.family.common.threadpool;

import com.okay.family.common.basedata.OkayConstant;
import com.okay.family.common.bean.testcase.CaseRunRecord;
import com.okay.family.common.bean.testcase.request.CaseDataBean;
import com.okay.family.common.enums.CaseAvailableStatus;
import com.okay.family.common.enums.RunResult;
import com.okay.family.utils.RunCaseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;

public class CaseRunThread implements Runnable {

    private static Logger logger = LoggerFactory.getLogger(CaseRunThread.class);

    int envId;

    CaseDataBean bean;

    CaseRunRecord record;

    CountDownLatch countDownLatch;

    public CaseRunRecord getRecord() {
        return record;
    }

    private CaseRunThread() {

    }

    public CaseRunThread(CaseDataBean bean, CountDownLatch countDownLatch, int runId, int envId) {
        this.bean = bean;
        this.envId = envId;
        this.countDownLatch = countDownLatch;
        this.record = new CaseRunRecord();
        record.setRunId(runId);
        record.setUid(bean.getUid());
        record.setParams(bean.getParams());
        record.setCaseId(bean.getId());
        record.setMark(OkayConstant.RUN_MARK.getAndIncrement());
        bean.getHeaders().put(OkayConstant.MARK_HEADER, record.getMark());
        record.setHeaders(bean.getHeaders());
    }

    @Override
    public void run() {
        try {
            if (bean.getAvailable() == RunResult.USER_ERROR.getCode()) {
                record.fail(RunResult.USER_ERROR, bean);
            } else if (bean.getEnvId() != envId || bean.getAvailable() != CaseAvailableStatus.OK.getCode()) {
                record.fail(RunResult.UNRUN, bean);
            } else {
                RunCaseUtil.run(bean, record);
            }
        } catch (Exception e) {
            logger.warn("用例運行出錯,ID:" + bean.getId(), e);
            record.fail(RunResult.UNRUN, bean);
        } finally {
            countDownLatch.countDown();
        }
    }


}

對於用戶鎖的實現

其中包括線程同步鎖和分佈式鎖。之因此採用兩個,主要是由於競爭中拿不到鎖的時候,不會像業務開發那樣直接丟出來拿鎖失敗的業務,而是須要等待其餘線程安全對用戶的驗證以後,再取出最新的用戶憑證。這裏面涉及到的東西比較複雜,中間由於邏輯問題我也寫了好幾個BUG。編程

這裏涉及的一些多線程編程的內容,還有在多用例執行的過程當中我用到ConcurrentHashMap做爲緩存,第一是爲了減小對數據庫的讀寫。第二是爲了防止用例中大量引用錯誤的用戶致使執行時間變長。緩存

核心代碼以下:安全

/**
     * 獲取用戶登陸憑據,map緩存
     *
     * @param id
     * @param map
     * @return
     */
    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public String getCertificate(int id, ConcurrentHashMap<Integer, String> map) {
        if (map.containsKey(id)) return map.get(id);
        Object o = UserLock.get(id);
        synchronized (o) {
            if (map.containsKey(id)) return map.get(id);
            logger.warn("非緩存讀取用戶數據{}", id);
            TestUserCheckBean user = testUserMapper.findUser(id);
            if (user == null) UserStatusException.fail("用戶不存在,ID:" + id);
            String create_time = user.getCreate_time();
            long create = Time.getTimestamp(create_time);
            long now = Time.getTimeStamp();
            if (now - create < OkayConstant.CERTIFICATE_TIMEOUT && user.getStatus() == UserState.OK.getCode()) {
                map.put(id, user.getCertificate());
                return user.getCertificate();
            }
            boolean b = UserUtil.checkUserLoginStatus(user);
            logger.info("環境:{},用戶:{},身份:{},登陸狀態驗證:{}", user.getEnvId(), user.getId(), user.getRoleId(), b);
            if (!b) {
                updateUserStatus(user);
                if (user.getStatus() != UserState.OK.getCode()) {
                    map.put(id, OkayConstant.EMPTY);
                    UserStatusException.fail("用戶不可用,ID:" + id);
                }
            } else {
                testUserMapper.updateUserStatus(user);
            }
            map.put(id, user.getCertificate());
            return user.getCertificate();
        }
    }
    
        /**
     * 更新用戶登陸狀態,全局鎖+分佈式鎖
     *
     * @param bean
     * @return
     */
    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public int updateUserStatus(TestUserCheckBean bean) {
        int userLock = NodeLock.getUserLock(bean.getId());
        int lock = commonService.lock(userLock);
        if (lock == 0) {
            logger.info("分佈式鎖競爭失敗,ID:{}", bean.getId());
            int i = 0;
            while (true) {
                SourceCode.sleep(OkayConstant.WAIT_INTERVAL);
                TestUserCheckBean user = testUserMapper.findUser(bean.getId());
                String create_time = user.getCreate_time();
                long create = Time.getTimestamp(create_time);
                long now = Time.getTimeStamp();
                if (now - create < OkayConstant.CERTIFICATE_TIMEOUT && user.getStatus() == UserState.OK.getCode()) {
                    bean.copyFrom(user);
                    return testUserMapper.updateUserStatus(bean);
                }
                if (i++ > OkayConstant.WAIT_MAX_TIME) {
                    UserStatusException.fail("獲取分佈式鎖超時,沒法更新用戶憑據:id:" + bean.getId());
                }
            }
        } else {
            logger.info("分佈式鎖競爭成功,ID:{}", bean.getId());
            try {
                TestUserCheckBean user = testUserMapper.findUser(bean.getId());

                String create_time = user.getCreate_time();
                long create = Time.getTimestamp(create_time);
                long now = Time.getTimeStamp();
                if (bean.same(user) && StringUtils.isNotBlank(user.getCertificate())) {
                    if (now - create < OkayConstant.CERTIFICATE_TIMEOUT && user.getStatus() == UserState.OK.getCode()) {
                        bean.copyFrom(user);
                        return testUserMapper.updateUserStatus(bean);
                    }
                    if (UserUtil.checkUserLoginStatus(user)) bean.copyFrom(user);
                }
                UserUtil.updateUserStatus(bean);
                return testUserMapper.updateUserStatus(bean);
            } catch (Exception e) {
                logger.error("用戶驗證失敗!ID:{}", bean.getId(), e);
                bean.setStatus(UserState.CANNOT.getCode());
                return testUserMapper.updateUserStatus(bean);
            } finally {
                commonService.unlock(userLock);
            }
        }
    }

  • 公衆號FunTester首發,更多原創文章:FunTester440+原創文章,歡迎關注、交流,禁止第三方擅自轉載。

熱文精選

相關文章
相關標籤/搜索