The great thing that we talked about before is finally implemented. We shared a simplified version of the dynamic model of Turbocharging pause [FunTester test framework]. Today I’m going to share with you a simplified version of the implementation of dynamic pressure increase or decrease, which basically means adjusting (increasing or decreasing) the pressure (thread count) at any time during the pressure measurement.

Train of thought

First, discard the original model structure, treat each multithreaded task as a manageable object, need to have an interrupt method, then have a global health management class, including some basic ability to add, remove, and terminate individual multithreaded tasks.

Trigger different management class methods by an external factor: add a use case, for example, and then clone a task randomly from the task pool (later selecting a task) and put it back into the task pool.

The local version of the FunTester test framework treats keyboard input as an external factor, while the distributed servitization FunTester test framework treats interface requests as an external factor.

The operation process is as follows:

  • Start with a small number of tasks with base pressure
  • Start additional threads to process parameters passed by external factors
  • Control the increase, decrease or termination of multithreaded task pool by external factors

transform

Multithreaded task class

First base class for multithreaded task, I wrote a com again. Funtester. Base. Constaint. ThreadBase subclasses of com. Funtester. Base. Constaint. FunThread, dedicated to create the dynamic model of task. Here transformation is divided into two kinds: (1) is increased to terminate properties. Com funtester. Base. Constaint. FunThread# BREAK_KEY method and the corresponding com. Funtester. Base. Constaint. FunThread# interrupt; 2. Is to simplify the com. Funtester. Base. Constaint. FunThread# run method, transformed into a will always run, the method of avoiding termination phenomenon occurring in a clone.

package com.funtester.base.constaint;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Vector;

public abstract class FunThread<F> extends ThreadBase {

    private static final long serialVersionUID = 7878297575504772944L;

    private static final Logger logger = LogManager.getLogger();

    /** * Manage all threads */
    private static Vector<FunThread> threads = new Vector<>();

    /** * Single-thread interrupt switch, used to dynamically adjust concurrency pressure, default value false */
    private boolean BREAK_KEY = false;

    public FunThread(F f, String name) {
        this.isTimesMode = true;
        this.threadName = name;
        this.limit = Integer.MAX_VALUE;
        this.f = f;
    }

    protected FunThread(a) {
        super(a); }@Override
    public void run(a) {
        before();
        while(! BREAK_KEY) {try {
                doing();
            } catch (Exception e) {
                logger.warn("Mission failed!", e); }}}/** * Preparation before running the method under test */
    public void before(a) {}/** * The dynamic model will not end properly */
    protected void after(a) {}private static synchronized boolean checkName(String name) {
        for (FunThread thread : threads) {
            String threadName = thread.threadName;
            if (StringUtils.isAnyBlank(threadName, name) || threadName.equalsIgnoreCase(name)) {
                return false; }}return true;
    }

    /** * Copy object method, which is used to count the number of requests and successes when a single object is called by multiple threads. For complex cases of <T>, it is necessary to rewrite the clone method ** for T type@return* /
    @Override
    public abstract FunThread clone(a);

    /** * thread terminates, used to dynamically adjust concurrency pressure */
    public void interrupt(a) {
        BREAK_KEY = true; }}Copy the code

Management functions

Management functions. I wrote the com funtester. Base. Constaint. FunThread class, through a Java. Util. Vector collection to store all of the running task, as a task pools, increased the add, delete, query, termination, cloning method.

/** * Used to prematurely terminate tests in certain circumstances */
public static synchronized void stop(a) {
    threads.forEach(f -> f.interrupt());
    threads.clear();
}

public static synchronized boolean addThread(FunThread base) {
    if(! checkName(base.threadName))return false;
    return threads.add(base);
}

/** * Delete a task, or stop **@param base
 */
public static synchronized void remoreThread(FunThread base) {
    base.interrupt();
    threads.remove(base);
}

public static synchronized FunThread find(String name) {
    for (int i = 0; i < threads.size(); i++) {
        FunThread funThread = threads.get(i);
        if (StringUtils.isNoneBlank(funThread.threadName, name) && funThread.threadName.equalsIgnoreCase(name)) {
            returnfunThread; }}return null;
}

public static synchronized void remoreThread(String name) {
    FunThread funThread = find(name);
    if (funThread == null) remoreThread(funThread);
}

public static synchronized FunThread getRandom(a) {
    return random(threads);
}

public static synchronized int aliveSize(a) {
    return threads.size();
}


Copy the code

Dynamically executed class

Execution classes are very simple because they don’t need statistics, just task pool management.

package com.funtester.frame.execute;

import com.funtester.base.constaint.FunThread;
import com.funtester.base.interfaces.IFunController;
import com.funtester.config.HttpClientConstant;
import com.funtester.frame.SourceCode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;

/** * Startup class for dynamic manometer model */
public class FunConcurrent extends SourceCode {

    private static Logger logger = LogManager.getLogger(FunConcurrent.class);

    /** * Task set */
    public List<FunThread> threads = new ArrayList<>();

    /** * thread pool */
    public static ExecutorService executorService;

    public static IFunController controller;

    / * * *@paramThreads Thread group */
    public FunConcurrent(List<FunThread> threads) {
        this.threads = threads;
        executorService = ThreadPoolUtil.createCachePool(HttpClientConstant.THREADPOOL_MAX);
    }

    private FunConcurrent(a) {}If there is no threadname, the name of the threadname is desc+ the number of threads used as the threadname, removing the date */
    public void start(a) {
        if (controller == null) controller = new FunTester();
        new Thread(controller,"Receiver").start();
        threads.forEach(f -> addTask(f));
    }

    public static void addTask(FunThread thread) {
        boolean b = FunThread.addThread(thread);
        logger.info("Task {} add {}", thread.threadName, b ? "Success" : "Failure");
        if (b) executorService.execute(thread);
    }

    public static void addTask(a) {
        FunThread thread = FunThread.getRandom();
        addTask(thread.clone());
    }

    public static void removeTask(FunThread thread) {
        logger.info("Task {} terminated", thread.threadName);
        FunThread.remoreThread(thread);
    }

    public static void removeTask(String name) {
        logger.info("Task {} terminated", name);
        FunThread.remoreThread(name);
    }

    public static void removeTask(a) { FunThread thread = FunThread.getRandom(); removeTask(thread); }}Copy the code

Multithreaded classes that handle external factors

Here I’m doing it fairly simply, just adding and subtracting and terminating, adding bulk adding and subtracting later, and dynamically importing pressure tasks from Groovy scripts, which of course relies on more refined task pool management.

    private static class FunTester implements IFunController {

        boolean key = true;

        @Override
        public void run(a) {
            while (key) {
                String input = getInput();
                switch (input) {
                    case "+":
                        add();
                        break;
                    case "-":
                        reduce();
                        break;
                    case "*":
                        over();
                        key = false;
                        break;
                    default:
                        break; }}}@Override
        public void add(a) {
            addTask();
        }

        @Override
        public void reduce(a) {
            removeTask();
        }

        @Override
        public void over(a) {
            logger.info("Dynamic end mission!"); FunThread.stop(); }}Copy the code

Now that the basic functionality has been implemented, let’s test it out.

test

The test script

I used two tasks as a base task, then performed a pressure test, and controlled use cases to add or subtract via keyboard output. The video demo is at the back, or go to Station B and follow me on the video number, which is called FunTester.

package com.funtest.funthead;

import com.funtester.base.constaint.FunThread;
import com.funtester.frame.SourceCode;
import com.funtester.frame.execute.FunConcurrent;

import java.util.Arrays;

public class Ft extends SourceCode {

    public static void main(String[] args) {
        FunTester e2 = new FunTester("task A");
        FunTester e22 = new FunTester("task B");
        new FunConcurrent(Arrays.asList(e2, e22)).start();
    }

    private static class FunTester extends FunThread {

        public FunTester(String name) {
            super(null, name);
        }

        @Override
        protected void doing(a) throws Exception {
            sleep(3.0 + getRandomDouble());
            output(threadName + TAB + "Mission in progress!");
        }

        @Override
        public FunThread clone(a) {
            return new FunTester(this.threadName + "Clone"); }}}Copy the code

Console output

The following shows only the necessary information that a use case must terminate manually or be triggered by an external trigger. The clone fails because the task name is duplicate. The task name is planned to be used as a marker for task management. Can avoid duplication in com. Funtest. Funthead. Ft. FunTester# clone name assigned randomly and uniqueness.

INFO - > main current user: oker, working directory: / Users/oker/IdeaProjects funtester/system coding format: utf-88Mac OS X version:10.16INFO-> Main Task A Added successfully INFO-> Main task B added successfully INFO-> FT-2Task B The task is running! INFO-> FT-1Task A Task is running! + INFO-> Receiver Input: + INFO-> Receiver task Task A Clone added successfully INFO-> FT-2Task B The task is running! INFO-> FT-1Task A Task is running! INFO-> FT-3Task A Clone task is running! + INFO-> Receiver input: + INFO-> Receiver task Task A Failed to add clone INFO-> FT-2Task B The task is running! INFO-> FT-1Task A Task is running! INFO-> FT-3Task A Clone task is running! + INFO-> Receiver Input: + INFO-> Receiver task Task B Clone added INFO-> FT-3Task A Clone task is running! INFO-> FT-2Task B The task is running! INFO-> FT-4Task B The clone task is running! INFO-> FT-1Task A Task is running! _ INFO-> Receiver input: _ INFO-> FT-3Task A Clone task is running! - INFO-> Receiver input: - INFO-> Receiver task Task B Clone terminated INFO-> FT-4Task B The clone task is running! INFO-> FT-2Task B The task is running! INFO-> FT-1Task A Task is running! Process finished with exit code130 (interrupted by signal 2: SIGINT)

Copy the code

Welcome to FunTester, Have Fun ~ Tester!

  • 140 interview questions (UI, Linux, MySQL, API, security)
  • Graphic HTTP brain map
  • Share a Fiddler study bag
  • Thread pools handle batch interface request practices
  • 100K QPS, K6, Gatling and FunTester showdown!
  • Performance bottleneck tuning
  • How do I choose an API test tool
  • Beginner’s API testing tips
  • API Automation Test Guide
  • API Testing Basics
  • There is no data to drive automated tests
  • Automated and manual testing to keep the balance!
  • 43 common software test categories