专栏首页FunTester线程安全类在性能测试中应用

线程安全类在性能测试中应用

最近在做一个支付成功之后回调接口的压测,场景是用户购买VIP,详情如下:

测试场景

用户支付成功之后,端上会请求后端来进行VIP开通和续费操作。

接口处理逻辑

首先验证接口参数签名是否正确,然后加锁去判断订单信息和状态,处理用户增添VIP时间事务,成功之后释放锁。锁是针对用户和订单的分布式锁,使用方案是用的redis

接口文档

接口基本信息

  1. 接口名称购买会员或续费会员
  2. 请求Url /api/member/createOrRenewMember
  3. 将请求参数转为JSON字符串走验签处理,请求方式见 加签示例

请求参数

{
    "sign": "CGvmkLeGtDv/Om8RbY52pMKNMi/xdLYAgRhIxC3uPqJsEdOvHj+S6zFobN0fxA/vksmCKJHhN6hFQrFIa3C6oK6EH7/BnJEsUMmIRsXMra32lzE/Tq9nqjYIdy996qc6eJWsxshqquj9Tb78pN152ndCNgvujMYsUHT4v7QxUW4=",
    "orderNo": "E2341234",
    "systemId": 94848494
}

请求参数说明

字段说明

字段名称

字段类型

备注

订单号

orderNo

string

订单编号

用户账号

systemId

number

必传

签名

sign

String

签名字符串,请用我方提供工具类生成

返回参数

{
    "code": 0,
    "message": "success",
    "data": {
        "memberId": 123123,
        "systemId": 86123123
    }
}

code 等于0的时候成功

测试方案

类似方案参考如何对消息队列做性能测试

难点

  • 因为锁的关系,一个用户只能同时有一个订单在处理,压测参数必需是每次必不相同。
  • 用户必需是存在的用户,对压测用户量提出了要求。

解决方案

  • 将用户id和订单号进行参数化,使用AtomicInteger这个线程安全的类和一个提前加载好的参数数组来保证每一次参数都是唯一且相互不同。(不适用随机的方法,因为有概率重复和消耗更多性能)
  • 储备更多用户,由于获取用户是按照数组索引增大顺序获取,并不需要每一个请求都绑定一个用户。经过尝试2000个用户循环去取就能满足需求。

关于Java线程安全的问题参考:操作的原子性与线程安全快看,i++真的不安全原子操作组合与线程安全

测试脚本

保留一下调试的方法和功能,性能测试框架第三版里面有引用类的代码。

package com.okayqa.others.payyst

import com.fun.base.constaint.ThreadLimitTimesCount
import com.fun.frame.excute.Concurrent
import com.fun.frame.httpclient.FanLibrary
import com.fun.utils.ArgsUtil
import com.fun.utils.RString
import com.okayqa.common.RSAUtilLJT
import com.okayqa.common.Users
import com.alibaba.fastjson.JSONObject
import org.apache.http.client.methods.HttpPost
import org.slf4j.Logger

import java.util.concurrent.atomic.AtomicInteger

class T extends FanLibrary {static Logger logger = getLogger(T.class)


    static AtomicInteger i = new AtomicInteger(111000);

    publicstaticvoid main(String[] args) {
        def argsUtil = new ArgsUtil(args)
        def thread = argsUtil.getIntOrdefault(0, 1)
        def times = argsUtil.getIntOrdefault(1, 100)
        def reqs = []

        thread.times {
//            def mark = new HeaderMark("requestid")
            reqs << new Thr(times)
        }

        new Concurrent(reqs, "会员支付和续费接口").start()
        testOver()
    }

    staticclass Thr extends ThreadLimitTimesCount {static Logger logger = getLogger(Thr.class)

        public Thr(int times) {
            super(null, times, null);
        }

        @Overrideprotectedvoid doing() throws Exception {
            String url = com.okayqa.studentapd.base.OkayBase.HOST+"/api/member/createOrRenewMember"
            Map<String, String> p = new HashMap<>();
            p.put("days", "1");
            p.put("memberId", "208");
            p.put("orderNo", "F" + RString.getString(4) + i.getAndAdd(1));
            p.put("orderPaySystemId", "85123213");
            p.put("orderPayTime", "2020-02-09 10:00:00");
            p.put("payMoney", "30");
            p.put("recordSources", "3");
            p.put("renewal", "false");
            def user = Users.getStuUser(i.getAndAdd(1) % 2000)
//            output(user)
            p.put("systemId", user);
            String sign = RSAUtilLJT.sign(p, RSAUtilLJT.getPrivateKey(RSAUtilLJT.RSA_PRIVATE_KEY));
            p.put("sign", sign);
            HttpPost post = getHttpPost(url, JSONObject.fromObject(p).toString());
            def s = "F" + getNanoMark()
            post.addHeader(getHeader("requestid", s));

            def simlple = FanLibrary.excuteSimlple(post)
            if (!simlple.contains("success")) {
                logger.warn(s + OR + user + simlple.toString())
                fail()
            }
        }

    }
}

这里有一个坑,AtomicInteger类虽然是一个线程安全的类,但是并不是所有的方法都是安全的,比如get(),所以我两次都使用了getAndAdd()方法,虽然增加了用户量循环一次的速度,但准确性还是最重要的,经过试验验证2000个用户足够用。


  • 郑重声明:文章首发于公众号“FunTester”,禁止第三方(腾讯云除外)转载、发表。

本文分享自微信公众号 - FunTester(NuclearTester),作者:八音弦

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 利用python wxpy和requests写一个自动应答微信机器人实例

    在做测试的过程中,同事们经常需要获取一个账户的token和个人信息,我自己利用spring boot写了一个接口,但是对于APP测试同学来说不是很方便,因为需要...

    八音弦
  • 给moco API添加random功能

    使用moco API快速搭建测试挡板服务的时候,有一些接口需求是随机返回固定的几个响应体,但是moco API提供并未提供此功能,幸好有先前增加limit功能的...

    八音弦
  • 记一次失败的爬虫

    收到一天振奋人心的假新闻,导致我去找了公开信息网站定点药店的信息,虽然结果比较失败,过程还是挺欢乐的,记录下来又可以水一篇文章了。以下是原文:

    八音弦
  • 安卓基础干货(五):安卓线程与数据上传下载的学习

    緣來
  • Android 滑动效果入门篇(二)—— Gallery

    Gallery 是Android官方提供的一个View容器类,继承于AbsSpinner类,用于实现页面滑动效果。

    阳光岛主
  • springboot整合mongodb

    想写这篇文章有好久了,可是一直没有时间坐下来去好好去写这一篇自己喜欢的文章了,也算是自己的一次总结吧。

    用户3625239
  • Discuz数据库security_failedlog错误及修复

    最近跑了之前的论坛转了转,发现一直在报数据库中表security_failedlog错误,百度了好久,最终找到解决方案。

    汐楓
  • keras之数据预处理

    from keras.preprocessing.sequence import pad_sequences

    学到老
  • 手把手撸PHP扩展 0x06: 协程创建(二)

    首先,我们需要对传给接口的参数进行解析。解析参数需要使用PHP提供给我们的宏来完成,分别是开头的和结尾的宏:

    桶哥
  • Spring Boot2.x 动态数据源配置

    基于 Spring Boot 2.x、Spring Data JPA、druid、mysql 的动态数据源配置Demo,适合用于数据库的读写分离等应用场景。通过...

    壹言

扫码关注云+社区

领取腾讯云代金券