Operations 8 min read

Load Testing Proportional Login Methods with Java Concurrency and AtomicInteger

The article presents a detailed solution for load‑testing two login mechanisms—username/password and phone‑code—by creating a thread‑safe user pool, using AtomicInteger for proportion control, marking each request, and providing complete Java scripts for the test framework, login APIs, and password encryption.

FunTester
FunTester
FunTester
Load Testing Proportional Login Methods with Java Concurrency and AtomicInteger

The author received a testing task requiring load testing of two login methods (account/password and phone+verification code) with a specific proportion while ensuring each login uses a distinct user and that phone numbers are bound to verification codes.

Requirements: unique users per login, phone‑code binding, ability to locate long‑running requests.

Challenges: generating a different user each time, obtaining a fresh verification code and its associated trace number for each phone login, and marking each test run (phone login generates two requests).

Solution: create a sufficiently large user pool and use a thread‑safe AtomicInteger to avoid duplicate users; encapsulate two login methods with an extra parameter to distinguish the login type; parameterize the proportion using another AtomicInteger ; and mark each login by assigning a unique user ID.

The following is the complete test script (Java):

package com.okayqa

import com.fun.base.constaint.ThreadBase
import com.fun.base.constaint.ThreadLimitTimeCount
import com.fun.base.interfaces.MarkThread
import com.fun.frame.excute.Concurrent
import com.fun.utils.ArgsUtil
import com.okayqa.appmiddle.base.OkayBase
import com.okayqa.common.OkayUsers

import java.util.concurrent.atomic.AtomicInteger

class OKAYTT extends OkayBase {
    static AtomicInteger num = new AtomicInteger(0)
    static AtomicInteger r = new AtomicInteger(0)
    static def split;

    public static void main(String[] args) {
        def argsUtil = new ArgsUtil(args)
        def thread = argsUtil.getIntOrdefault(0, 2)
        def time = argsUtil.getIntOrdefault(1, 5)
        split = argsUtil.getStringOrdefault(3, "1:2").split(":")

        def threads = []
        thread.times {
            thread << new Plus(time)
        }

        new Concurrent(threads, "多种登录模式压测").start()
        allOver()
    }

    public static boolean getType() {
        def var1 = split[0] as int
        def var2 = split[1] as int
        def var = var1 + var2
        def increment = r.getAndIncrement() % var
        if (increment < var1) true
        false
    }

    static class Plus extends ThreadLimitTimeCount {
        int i;
        public Plus(int time) {
            super(null, time, null)
            this.i = OKAYTT.num.getAndIncrement();
            this.mark = new Mark(OkayUsers.getStuUser(i))
        }
        @Override
        protected void doing() throws Exception {
            new OkayBase(i, getType())
        }
    }

    static class Mark implements MarkThread {
        String mark_name;
        @Override
        String mark(ThreadBase threadBase) {
            return mark_name;
        }
        public Mark(String name) {
            this.mark_name = name;
        }
        public Mark() {}
        @Override
        MarkThread clone() {
            return null
        }
    }
}

In this case, new OkayBase(i, getType()) performs the actual login, where the second argument selects the login method; i is the index of the user taken from the pool.

The two login wrappers are:

public JSONObject loginPwd(String tel, int role) {
    String url = LoginApi.LOGIN;
    JSONObject params = getParams();
    JSONObject types = new JSONObject();
    types.put("type", 1);
    types.put("phone", account);
    types.put("pwd", pwd);
    params.put("type", types);
    params.put("app_id", "1");
    params.put("role", role);
    return getPostResponse(url, params);
}

/**
 * @param tel
 * @param role 0家长 1学生 默认0家长
 */
public JSONObject loginTel(String tel, int role) {
    String url = LoginApi.LOGIN;
    JSONObject params = getParams();
    JSONObject types = new JSONObject();
    types.put("type", 2);
    types.put("phone", tel);
    types.put("traceno", sendCode(tel));
    types.put("code", Common.TEL_CODE);
    params.put("role", role);
    params.put("type", types);
    return getPostResponse(url, params);
}

public String sendCode(String tel) {
    String url = LoginApi.SEND_CODE;
    JSONObject params = getParams();
    params.put("phone", tel);
    JSONObject response = getPostResponse(url, params);
    output(response);
    if (isRight(response)) {
        return response.getJSONObject("data").getString("traceno");
    }
    return EMPTY;
}

The password encryption algorithm used is DES with a fixed salt, implemented as:

protected static String getPwd(String pwd) {
    try {
        String salt = "17802ec2980353bdc3f082b0668bd1e4";
        String key = StringUtils.substring(salt, 0, salt.length() / 8 * 8);
        SecureRandom sr = new SecureRandom();
        DESKeySpec dks = new DESKeySpec(key.getBytes());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
        SecretKey securekey = keyFactory.generateSecret(dks);
        Cipher cipher = Cipher.getInstance(DES);
        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
        byte[] bt = cipher.doFinal(pwd.getBytes());
        return new BASE64Encoder().encode(bt);
    } catch (Exception e) {
        return EMPTY;
    }
}

To reduce local machine variance, the author reset all account passwords to a common value and pre‑computed the encrypted password, eliminating the encryption step during testing.

Disclaimer: The article was originally published on the "FunTester" public account and may not be reproduced without permission (except by Tencent Cloud).

Additional technical article references are listed at the end of the original post.

Javaperformanceconcurrencyload testingloginAtomicInteger
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.