近期,由于某项目验收需要,需要登录接口同时满足至少400个账号同时并发登录,于是开始编写测试代码,以满足项目业务需要。首先,安装jdk,由于本机已安装jdk8:
如果你机器上没有安装jdk,请百度自行安装一下,因为JMeter由java编写,JMeter安装:
Windows 安装 jmeter-CSDN博客
安装redis,可以百度也可以参考之前发布的文章:部署.net6 到 Windows server 2008 r2 IIS_csdn_aspnet的博客-CSDN博客
编写测试webapi登录接口代码:
1.写入redis测试账号:
/// <summary>
/// 添加或移除测试账号
/// </summary>
/// <param name="isDel">0:添加账号 1:移除账号</param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult> AddYhzhRedis(int isDel = 0)
{
string sKey = "YH";
if (isDel == 1)
{
for (int i = 0; i < 500; i++)
{
int nId = i + 1;
string sYhm = "admin_" + nId;
await _cache.RemoveAsync(sKey + ":" + sYhm);
}
return Ok(new
{
code = 200,
success = true,
msg = "移除测试账号成功!"
});
}
Yhzh yhzh = null;
for (int i = 0; i < 500; i++)
{
int nId = i + 1;
yhzh = new Yhzh();
yhzh.id = nId;
yhzh.yhm = "admin_" + nId;
yhzh.mm = MD5Helper.MD5Encrypt32(nId + "123456");
await _cache.SetAsync(sKey + ":" + yhzh.yhm, yhzh, TimeSpan.FromHours(24));
var vId = await SqlSugarHelper.Db.Insertable(yhzh).ExecuteReturnBigIdentityAsync();
}
return Ok(new
{
code = 200,
success = true,
msg = "添加测试账号成功!"
});
}
代码中_cache 为注入的redis缓存接口,可自行编写或使用第三方包,MD5Helper.MD5Encrypt32 你可以在网络上找一个md5加密帮助类即可。
2.登录接口实现:
/// <summary>
/// 登录
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult> Login([FromBody] LoginVM model)
{
string sKey = "YH:";
if (await _cache.GetAsync(sKey + model.yhm))
{
var vCacheModel = await _cache.GetAsync<Yhzh>(sKey + model.yhm);
if (null == vCacheModel)
{
return Ok(new
{
code = 400,
success = false,
msg = "登录失败,用户不存在!"
});
}
string sAfferentPwd = MD5Helper.MD5Encrypt32(vCacheModel.id + model.mm);
if (sAfferentPwd != vCacheModel.mm)
{
return Ok(new
{
code = 400,
success = false,
msg = "登录失败,用户或密码不正确!"
});
}
//TODO:生成新的token 存储缓存信息
return Ok(new
{
code = 200,
success = true,
msg = "登录成功!"
});
}
var vUserModel = await SqlSugarHelper.Db.Queryable<Yhzh, Yhxx, Dwxx>((zh, yh, dw) =>
new JoinQueryInfos(JoinType.Left, zh.yhid == yh.id && yh.sczt == 0, JoinType.Left, zh.dwbh == dw.dwbh && dw.sczt == 0))
.Where((zh, yh, dw) => zh.yhm == model.yhm || yh.lxdh == model.yhm || yh.jh == model.yhm)
.Where((zh, yh, dw) => zh.sczt == 0)
.Select((zh, yh, dw) => new YhzhxxVM
{
id = zh.id,
yhid = zh.yhid,
xm = yh.xm,
yhm = zh.yhm,
mm = zh.mm,
lxdh = yh.lxdh,
//yhtx = zh.yhtx,
yhtx = yh.yhtx,
dwbh = zh.dwbh,
dwmc = dw.dwmc,
qhbm = dw.qhbm,
sfxtzh = zh.sfxtzh,
zhzt = zh.zhzt,
sfkdldp = zh.sfkdldp
})
.FirstAsync();
if (null == vUserModel)
{
return Ok(new
{
code = 400,
success = false,
msg = "登录失败,用户名不存在!"
});
}
string sUserPwd = MD5Helper.MD5Encrypt32(vUserModel.id + model.mm);
if (sUserPwd != vUserModel.mm)
{
return Ok(new
{
code = 400,
success = false,
msg = "登录失败,用户名或密码错误!"
});
}
Dictionary<string, string> dicUserInfo = new Dictionary<string, string>
{
{ "zhid",vUserModel.id.ToString()},
{ "userid",vUserModel.yhid.ToString()},
{ "dwbh",vUserModel.dwbh??""},
{ "dwmc",vUserModel.dwmc??""},
{ "qhbm",vUserModel.qhbm??""},
{ "username",model.yhm??""},
{ "xm",vUserModel.xm??""},
{ "nickname",vUserModel.nc??""},
{ "lxdh",vUserModel.lxdh??""},
{ "yhtx",vUserModel.yhtx??""},
{ "jsmc",""},
{ "jsid",""},
{ "sfxtzh",vUserModel.sfxtzh==null?"0":vUserModel.sfxtzh.ToString()},
{ "logintime",DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")},
{ "tokenExpire",DateTime.Now.AddSeconds(AppSettings.CustomToken.KeyTimeEffectiveTime).ToString("yyyy-MM-dd HH:mm:ss")},
};
var vUserInfo = JsonHelper.ToJson(dicUserInfo);
string sToken = Guid.NewGuid().ToString("N");
await _cache.SetAsync(sToken, vUserInfo, TimeSpan.FromMinutes(10));
return Ok(new
{
code = 200,
success = true,
msg = "登录成功!"
});
}
代码中 SqlSugarHelper使用的第三方 SqlSugar包实现,此类代码如下:
/// <summary>
/// 不能是泛型类
/// </summary>
public class SqlSugarHelper
{
//多库情况下使用说明:
//如果是固定多库可以传 new SqlSugarScope(List<ConnectionConfig>,db=>{}) 文档:多租户
//如果是不固定多库 可以看文档Saas分库
//用单例模式
public static SqlSugarScope Db = new SqlSugarScope(new ConnectionConfig()
{
ConnectionString = "server=127.0.0.1;Database=db;Port=3306;Uid=mysqladmin;Pwd=20231024;Persist Security Info=True;SslMode=None;charset=utf8mb4;",//连接符字串
DbType = DbType.MySql,//数据库类型
IsAutoCloseConnection = true //不设成true要手动close
},
db =>
{
//(A)全局生效配置点,一般AOP和程序启动的配置扔这里面 ,所有上下文生效
//调试SQL事件,可以删掉
db.Aop.OnLogExecuting = (sql, pars) =>
{
Console.WriteLine(sql);//输出sql,查看执行sql 性能无影响
//5.0.8.2 获取无参数化 SQL 对性能有影响,特别大的SQL参数多的,调试使用
//UtilMethods.GetSqlString(DbType.SqlServer,sql,pars)
};
//多个配置就写下面
//db.Ado.IsDisableMasterSlaveSeparation=true;
//注意多租户 有几个设置几个
//db.GetConnection(i).Aop
var vIsConnection = db.Ado.IsValidConnection(); //如果时间长,可以在连接字符串配置 连接超时时间
Console.WriteLine("vIsConnection:"+ vIsConnection);
});
}
打开swagger开始写入账号数据:
redis写入成功:
数据库写入成功:
启动JMeter,进入JMeter的bin目录,右键使用管理员身份运行jmeter.bat文件即可:
启动成功如下图:
修改名称,你可以使用默认名称,本测试修改为登录并发测试,在左侧登录并发测试节点右键添加线程组,如下图:
在左侧登录并发测试节点右键分别添加HTTP信息头管理器,HTTP请求默认值,CSV Data Set Config,如下图:
HTTP信息头管理,没有你可以不添加,点击添加后双击名称列单元格即可输入:
HTTP请求默认值,本次测试只填写红框内参数,IP和端口换成你自己的即可,其余默认:
CSV Data Set Config,如下图:
以上截图中 变量名称 后续会用到,csv文件内容其实就是上面代码中创建的账号信息,使用sql在数据库执行拼接登录名:
SELECT yhm FROM jc_yhzh1;
在查询结果中全选、右键- 复制为- 制表符分隔值(数据),粘贴到txt中,将txt扩展名修改为csv即可。
修改完成后,打开csv文件如下:
在左侧线程组节点右键添加HTTP请求,如下图:
配置信息如下:
请求报文中,${name}为变量,上面选择csv设置的,要一致。
在登录并发测试节点,右键添加查看结果树和聚合报告,添加即可不需要配置:
添加完成后,在线程组右键-启动:
启动后开始执行,绿色的三角变为灰色:
点击查看结果树:
两个截图请求明显登录账号不一样,说明一次600请求是正常的,返回结果如下:
点击聚合报告,异常0.00%,符合预期结果:
至此测试任务完成,将业务过程搬到正式环境代码,改写相关业务接口。