「原创声明:保留所有权利,禁止转载」
最近在做一个支付成功之后回调接口的压测,场景是用户购买 VIP,详情如下:
测试场景
用户支付成功之后,端上会请求后端来进行 VIP 开通和续费操作。
接口处理逻辑
首先验证接口参数签名是否正确,然后加锁去判断订单信息和状态,处理用户增添 VIP 时间事务,成功之后释放锁。锁是针对用户和订单的分布式锁,使用方案是用的redis
。
接口文档
接口基本信息
- 接口名称购买会员或续费会员
- 请求 Url /api/member/createOrRenewMember
- 将请求参数转为 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);
public static void 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()
}
static class Thr extends ThreadLimitTimesCount {
static Logger logger = getLogger(Thr.class)
public Thr(int times) {
super(null, times, null);
}
@Override
protected void 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”,禁止第三方(腾讯云除外)转载、发表。
技术类文章精选
- Linux 性能监控软件 netdata 中文汉化版
- 性能测试框架第三版
- 如何在 Linux 命令行界面愉快进行性能测试
- 图解 HTTP 脑图
- 将 swagger 文档自动变成测试代码
- Selenium 4.0 Alpha 更新日志
- Selenium 4.0 Alpha 更新实践
- 如何统一接口测试的功能、自动化和性能测试用例
非技术文章精选
TesterHome 为用户提供「保留所有权利,禁止转载」的选项。
除非获得原作者的单独授权,任何第三方不得转载标注了「原创声明:保留所有权利,禁止转载」的内容,否则均视为侵权。
具体请参见TesterHome 知识产权保护协议。
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。