问答 jmeter BeanShell PreProcessor 性能问题

哔哩哔哩温 · 2021年11月25日 · 最后由 小人物 回复于 2021年11月30日 · 3993 次阅读

在 jmeter 中使用 beanshell 实现签名算法,如下图,在使用过程中发现,直接在 BeanShell PreProcessor 实现加密算法在执行过程中耗时较久,达到 200ms+,影响性能测试

之后将签名算法打包成 jar 包,发现性能提升许多

有哪位大佬可以告诉我这中间的区别吗?以及原因吗?

具体代码

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.text.DecimalFormat;

public class App {
    public static byte[] TripleDesEncrypt(byte[] content, byte[] key) throws Exception {
        byte[] icv = new byte[8];
        System.arraycopy(key, 0, icv, 0, 8);
        return TripleDesEncrypt(content, key, icv);
    }

    protected static byte[] TripleDesEncrypt(byte[] content, byte[] key, byte[] icv) throws Exception {
        final SecretKey secretKey = new SecretKeySpec(key, "DESede");
        final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        final IvParameterSpec iv = new IvParameterSpec(icv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
        return cipher.doFinal(content);
    }

    public static byte[] TripleDesDecrypt(byte[] content, byte[] key) throws Exception {
        byte[] icv = new byte[8];
        System.arraycopy(key, 0, icv, 0, 8);
        return TripleDesDecrypt(content, key, icv);
    }

    protected static byte[] TripleDesDecrypt(byte[] content, byte[] key, byte[] icv) throws Exception {
        final SecretKey secretKey = new SecretKeySpec(key, "DESede");
        final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        final IvParameterSpec iv = new IvParameterSpec(icv);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
        return cipher.doFinal(content);
    }
    public static String sign(String content, String privateKeyPem,long time) {
        try {
            long startTime = System.currentTimeMillis();         
        String privateKeyPem_String = privateKeyPem.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "");
        byte[] encodedKey = org.bouncycastle.util.encoders.Base64.decode(privateKeyPem_String.getBytes("UTF-8"));
        long spendtime2 = System.currentTimeMillis() - startTime;
        log.info("=====sign Base64 :"+ spendtime2); 

        long startTime3 = System.currentTimeMillis();
        PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
        Signature signature = Signature.getInstance("SHA256WithRSA");

        long spendtime4 = System.currentTimeMillis() - startTime3;
        log.info("=====sign signature :"+ spendtime4); 
        long startTime1 = System.currentTimeMillis();
        signature.initSign(privateKey);
        long spendtime3 = System.currentTimeMillis() - startTime1;
        log.info("=====sign initSign :"+ spendtime3); 
        signature.update(content.getBytes("utf-8"));
        byte[] signed = signature.sign();
        long spendtime5 = System.currentTimeMillis() - startTime1;
        log.info("=====sign update :"+ spendtime5); 
        return new String(org.bouncycastle.util.encoders.Base64.encode(signed));
        } catch (Exception var6) {
            String errorMessage = "签名遭遇异常,content=" + content + " privateKeySize=" + privateKeyPem.length() + " reason=" + var6.getMessage();
            // log.error("====error: "+errorMessage);
            throw new RuntimeException(errorMessage, var6);
        }
    }

}
// ==============begin================
String timestamp = String.valueOf(System.currentTimeMillis());
vars.put("timestamp",timestamp);
long startTime = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
Random rand = new Random();
Integer randomNum = rand.nextInt(5000000) + 1000000;
String randomNumStr = String.valueOf(randomNum);
String order_id = sdf.format(new Date()) + randomNumStr;
vars.put("order_id",order_id);

double a=Math.random()*100 + 1.00;
DecimalFormat df = new DecimalFormat( "0.00" );
String pay_str=df.format(a);

Random rand = new Random();
Integer randomNum = rand.nextInt(5000000) + 1000000;
String randomNumStr = String.valueOf(randomNum);
String id_card = "430" + randomNumStr + "10104219"; 

String private_key = vars.get("key");
String dealer_id = "00243810";

String data = "{\"order_id\":\""+order_id+"\",\"dealer_id\":\"\",\"broker_id\":\"\",\"real_name\":\"\",\"card_no\":\"\",\"phone_no\":\"\",\"id_card\":\"\", \"pay\":\""+pay_str+"\"}";
byte[] des3key = "0857b75Fmev7mbTFuhUNa6Rt".getBytes("utf-8");
byte[] enc = App.TripleDesEncrypt(data.getBytes("utf-8"), des3key);

byte[] enc64 = Base64.encodeBase64(enc);
String data64 = new String(enc64);
vars.put("data",data64);

// 进行sign
String sign_data = "data=" + data64 + "&mess=test&timestamp="+ timestamp + "&key=0aYxoR0s474JZns9kS09oXnA2e7Rz0fj";
String sign = App.sign(sign_data, private_key, startTime);
vars.put("sign",sign);

共收到 9 条回复 时间 点赞
仅楼主可见

个人理解:
1、beanshell 应该属于解释型语言,实时编译 + 执行,执行效率相对并不那么高。而且它目的更多是功能的扩展,性能本身并不是它所关注的,也不是它擅长的。
2、jar 虽然只是变成了字节码,但已经是有一定的预编译和编译器优化,所以性能会高不少。

除非你本身就需要去测试这种算法的性能,否则不建议用 jmeter 来做这个,反而会影响性能结果.
jmeter 只是某一个制造发起压力的客户端而已,算法的实现过程 jmeter 并不关心,也不需要通过就 meter 来实现.

王津伟 回复

没打包之前打印的是 sign 内部的耗时,打包之后打印的是 sign 整个方法的耗时,没有在 sign 的 jar 包里面加日志

jacksboy 回复

是的,主要最开始直接在 beanshell 中实现了加密功能,最后没想到,因为 beanshell 中加密时长,随着并发的加大,逐渐增加,导致增大并发数,接口的 tps 也无法增加,影响了业务性能测试,后来改成 jar 包方式,发现性能提升许多,而且 beanshell 执行时间稳定,只是好奇中间有什么区别。

beanshell 本身存在性能问题,官方有说明不推荐使用的

Tester_谜城 回复

官方哪里有说吗?可以帮忙找一下,介绍这块的地方吗?


官方推荐使用 JSR223 +Groovy 来提高性能
参考官方地址:https://jmeter.apache.org/usermanual/component_reference.html#BeanShell_Sampler

性能测试的时候本就不应该还去做这些操作

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册