Loadrunner的使用

Loadrunner的使用

选项公网测试地址:http://cfgjt.cn:8981/devt-web 用户名admin,密码11111111

1.Loadrunner介绍

​ LoadRunner,是一种预测系统行为和性能的负载测试工具。通过模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题,LoadRunner能够对整个企业架构进行测试。企业使用LoadRunner能最大限度地缩短测试时间,优化性能和加速应用系统的发布周期。

​ LoadRunner可适用于各种体系架构的自动负载测试,能预测系统行为并评估系统性能。它支持高并发、多协议。

1.1.功能模块

  • VGenerator:虚拟用户生成器,以虚拟用户的方式模拟真实用户的业务操作行为,可以录制脚本、编写脚本、调式脚本等
  • Controller:负载控制器/调度器,按计划、按目标来控制用户数量,通过脚本组合设计负载场景和方案,它提供一个互动环境,既能建立起持续且循环的负载,又能管理和驱动负载方案。Loadrunner内含集成的实时监测器,在负载测试过程中的任何时候,都能观察到应用系统的运行性能
  • Analysis:结果分析器,提供高级的分析和报告工具,以便迅速查找到性能问题并追溯原由
  • **压力机:**在运行时,可远程安装和监控虚拟用户生成器,适用于大规模压测和大量用户负载,可以安装多个。

1.2.运行视图

image-20230401101502670
  1. 采用虚拟用户生成器录制脚本、编写脚本、调试脚本,完成脚本的准备工作
  2. 通过脚本组合设计压测场景
  3. 调度压力机进行场景分布式压测
  4. 性能结果分析、图标分析

2.Loadrunner安装

2.1.下载

Loadrunner的官方下载地址为:https://www.microfocus.com/en-us/products/LoadRunner-professional/free-trial

image-20230401102533706

2.2.Loadrunner安装

  • 以管理员的方式运行该文件(在安装前需关闭杀毒软件):

  • 选择解压路径:
  • 安装依赖:
  • 选择安装路径:
  • 安装后,桌面多了三个图标,分别是:分析器、调度器和虚拟用户生成器:
  • 不选择证书,并不需要打开自述文件:

2.3.安装组件和语言包

  • 以管理员身份运行组件:
  • 以管理员身份运行语言包:
  • 安装语言包在D:\Software\lr\DVD\Language-Packs\Chinese-Simplified\LoadRunner路径下点击运行,但不建议使用中文,后期录制脚本可能出现编码问题,需要使用lr_convert_string_encoding进行转码。

2.4.安装压力机

安装压力机需在Linux系统上进行,本示例Linux版本问CentOS7,并且用户具有sudo权限。

  • 解压压力机,找到lg_linux_x64文件,并压缩该文件:

  • 在/root目录下使用命令mkdir root创建root目录,并上传以上文件:
image-20230402104723450
  • 解压该文件,并进入目录,赋予文件权限:
unzip lg_linux_x64.zip
cd lg_linux_x64
chmod 755 *
image-20230402105432747
  • 执行安装程序,进行安装:
sh installer.sh

**注意:**如出现以下情况,说明依赖不完整,我们需要修改yum源配置,然后下载依赖:

image-20230402105530315
# 备份本地yum源
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak  

# 下载yum源配置文件到本地
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo

# 清空并更新缓存
yum clean all && yum makecache

# 安装依赖
yum install glibc.i686 libstdc++.i686 ncurses-libs.i686 keyutils-libs.i686 glib2.i686 libnsl.i686 libidn.i686 -y

# 如出现多库版本问题,可执行如下命令,然后再执行上面的安装
yum update --setopt=protected_multilib=false 依赖名(如glib2)-y 

# 再次安装
sh installer.sh

如出现密钥问题,可以参考:https://blog.csdn.net/qq_41817050/article/details/120135877

安装过程中,直接按Enter确认就行了,都选择默认的,只有安装证书时选择(2),不安装。

image-20230402132130329
  • 验证54345端口:
netstat -anp | grep 54345
image-20230402132435594
  • 启动命令:
cd /opt/MF/MF_LoadGenerator/
ls

3.脚本录制

​ 脚本录制就是将可视化操作转换为脚本代码,并利用脚本代码进行重复性操作,这种方式能简化测试人员的操作,大大降低工作量和难度。

​ 脚本录制的方式分为以下几种:

  • 直接录制:只支持Microsoft Edge
  • 本地代理录制:支持各大浏览器
  • 离线流量录制:几乎支持所有浏览器
  • 手动代理录制:几乎支持所有浏览器
  • 第三方代理:采用fidller进行辅助录制

创建解决方案:

  • 创建脚本解决方案的方式为:File—Create a new Script—选择方案类型—命名—create

3.1.直接录制

​ 如上创建一个test01的解决方案,点击上方菜单栏的录制按钮:

录制动作(Record into action)选项释义:

  • **vuser_init:**只执行一次,如登录操作
  • **action:**循环执行
  • **vuser_end:**结束时执行,如退出操作

录制选项(Record)释义:

  • **Web Brower:**web浏览器录制方式
  • **Windows Application:**windows应用程序录制
  • **Remote Application via LoadRunner Proxy:**远程代理录制
  • **Captured Traffic File Analysis:**离线流量分析

最终内容,点击Start Recording直接录制:

image-20230403151933137

打开浏览器后,生成了脚本,进行了收集:

image-20230403153333784

**注意:**有大部分可能进入Edge没法登录,此次脚本录制不成功。

3.2.本地代理录制

​ 由于应用程序的兼容性问题,我们案例以谷歌浏览器作为测试浏览器进行本地代理录制。

  • 新建一个脚本,命名为test2,脚本类型为web-HTTP/HTML:
image-20230403160244577
  • 点击录制脚本按钮,输入内容如下:
image-20230403161021317
  • 点击本页左下角“Recording Options”选项设置,在HTTP Properties—Advanced中勾选以下选项:
  • 点击录制,进入页面,输入用户名admin和密码11111111登录,登录进行后将动作由vuser_init切换到action,然后输入关键字文本搜索,然后再输入文本1搜索:
  • 点击停止按钮,停止录制。看到工作台,vuser_initaction都生成了脚本:

3.3.离线流量录制

流线流量录制适用于大部分浏览器。

  • 打开网站http://cfgjt.cn:8981/devt-web,在登录前打开F12,找到Network,然后登录,并搜索文本:
image-20230403212133348
  • 在网络中生成的数据中鼠标右键,选择“以HAR格式保存所有内容”:
image-20230403212257136
  • 打开LoadRunner,创建test3的脚本,并将光标放到action中,指把脚本放在里面:
image-20230403212352837

  • 点击录制按钮,将动作改为action,将Record改为离线脚本录制,离线文件路径为刚刚保存的HAR文件,取消127.0.0.1前面的勾,并在Recording Options中取消之前选中的代理录制设置,具体设置如下:
image-20230403213045376
  • 点击开始录制,生成录制脚本:

image-20230403213201738

3.4.远程代理录制

  • 添加一个脚本,点击录制按钮,录制方式采用远程代理录制:
  • 录制开始,控制台有启动录制的IP和端口:
image-20230410145439063
  • 打开浏览器,进入http://cfgjt.cn:8981/devt-web的登录页,然后设置代理IP为刚刚设置的代理IP(192.168.1.102)和端口号(浏览器设置-系统-代理):
image-20230410145846678
  • 登录进网页,搜索文本,然后停止录制,查看生成的脚本:
image-20230410150310107
  • 关闭之前的浏览器代理。

3.5.脚本优化

  • 脚本结构认识:
    • Actions:脚本区
    • Extra Files:头文件区
    • Runtime Settings:运行时设置
    • Parameters:参数设计
    • Recording Report:录制报告
    • Replay Summary:回放总结

image-20230410150713334

  • 脚本区的执行顺序由先到后是vuser_init--Action--vuser_end。我们可以分别在这三个中添加执行输出的测试脚本,点击回放查看,脚本如下:
lr_log_message("测试1","init")
lr_log_message("测试2","Action")
lr_log_message("测试3","end")

  • 选择Actions,添加一个新的Action1:

image-20230410152423432

  • 录制一个脚本,录制设置为本地代理,录制信息如下:
image-20230410152058497 image-20230410152122038
  • 登录进页面后,动作改为Action,搜索文本,再更换一个Action1,点击组件视图,搜索小红,然后停止录制;
  • Recording Report中查看生成的报告:
  • Host中,可能有很多与我们业务不相关的主机,我们可以去掉勾选,然后点击Regenerate重新生成,这样就和我们当前录制脚本相关了:
  • 在右边图示的Content Type中过滤掉一些无用资源,只保留Json数据,然后重新生成:

3.6.录制选项优化

录制选项的打开有两种方式:

  • 在录制中有两种方式可以选择:
    • HTML-based script(默认):把所有非资源请求录制到一个组;
    • URL-based script:把所有资源录制到一个组,包含JS,CSS等文件。

当录制选项重新设置,不需要再录制脚本,我们只需要把之前的脚本重新申请即可,点击RecordRegenerate ScriptOK

  • 脚本语言设置:

4.脚本编写

4.1.脚本参数化

​ 在实际的测试业务中,参数可能有很多,我们可以使用一个统一的变量替换这些参数,这些参数可以是文件或表格的形式,相当于在jmeter中cvs的参数格式。

​ 这些参数可以增强代码的可读性,降低维护难度,减少错误的发生。比如在测试过程中主机、端口、查询条件等都是很少修改的量,可以使用参数代替。

​ 在LR中有很多内置的参数,合理的采用会提升测试的效率。

4.1.1.LoadGN

​ 全称为 LoadGeneratorName,该参数是以压力机名称为基础,可按不同格式生成:

//定义一个 LoadGeneratorName 参数
char* NewParam=lr_eval_string("{NewParam}");
lr_log_message("参数 1,压力机参数名:%s",NewParam);
  • 创建一个测试脚本,在Action.c中放入以上脚本,我们定义了一个参数名叫NewParam

image-20230417143823666

  • Parameters中新增参数NewParam,具体步骤如下:
  • 点击运行,打印参数值:
4.1.2.VuserID

​ 该参数是以虚拟用户的序号为基础,可按不同格式生成。它相当于是当前用户所占用的一个线程:

  • 创建一个VuserID,选择不通格式,步骤如下:
  • 点击运行,看到打印出来的序号:
4.1.3.表格参数化

​ 表格参数是以表格行数据为基础生成的参数,和jmeter相同,可以支持记事本和cvs等文件格式导入。

  • 定义一个表格数据参数:
lr_log_message("参数 2,表格参数:%s",lr_eval_string("{table_param}"));
image-20230417150534352

​ 需要注意的是,我们在设置表格式数据时,取值数据和更新规则可以有如下设置方式:

  • 取下一行数据方法有三种:

      1. 顺序:按照参数化的顺序一个一个的来取
      2. 随机:参数化中的数据,每次随机的从中选取数据
      3. 唯一:为每个虚拟用户分配一个唯一的一条数据
  • 更新值的规则:

      1. 每一次迭代时更新
      2. 运行场景中只更新一次

    设置方式如下:

  • 添加脚本,更改迭代次数,点击运行:

lr_log_message("参数 2,表格参数:%s",lr_eval_string("{table_param}"));

看到结果,迭代了5次,且按顺序运行:

image-20230417151810114

可以按不同取下一行和更新值的方式来测试。

4.1.4.文件参数化

​ 文件参数化和表格参数化的用法一样,只是将参数类型改为文件即可,此处不再赘述。

4.1.5.迭代编号

​ 迭代编号参数是以运行逻辑的替代次数为基础,可按不同格式生成。

  • 新建一个参数,参数类型选择为迭代编号:
lr_log_message("参数 3,迭代编号参数:%s",lr_eval_string("{iteration}"));
  • 点击运行,迭代了5次,因为在Runtime Settings中设置了迭代5次:
4.1.6.日期参数

​ 日期参数可以按照固定的格式生成参数。

  • 创建一个参数名为date,选择参数格式为Date/Time,并选择想要生成的日期格式:
  • 添加如下脚本,点击运行:
lr_log_message("参数 1,迭代编号参数:%s",lr_eval_string("{date}"));
4.1.7.随机数
  • 创建参数random,选择随机数的参数类型Random Number,并设置生成随机数的范围和格式:
image-20230417160404104
  • 点击运行,看到生成的随机数:

image-20230417160446011

4.1.8.xml格式参数

​ xml格式参数和表格参数使用相同,只不过生成的格式为xml格式。

  • 创建参数xml,选择参数类型为xml,并设置值:
  • 添加脚本,运行查看结果:
lr_log_message("参数 1,迭代编号参数:%s",lr_eval_string("{xml}"));

image-20230417161549574

4.1.9.用户自定义变量

​ 如Jmeter中,用户可以自定义变量。

  • 创建变量custom,并选择参数类型为Custom,并设置值:
  • 输入脚本,运行查看:
lr_log_message("参数 1,迭代编号参数:%s",lr_eval_string("{custom}"));

4.2.规则关联

​ 一般在发起请求时有可能用到前面步骤返回的结果,LR关联是把服务器返回的数据以参数来表示,同时作为后续请求的一个变量,规则关联分为手动关联和自动关联。

4.2.1.手动关联

​ 手动关联的步骤为:

  1. 找到前置请求中返回的业务数据
  2. 将上述数据设置为参数
  3. 将后续条件采用参数化替代
  • 创建一个新的脚本,录制这个脚本,录制信息如下:
image-20230417165000856
  • 登录页面,登录完成后停止录入脚本:
image-20230417170507744
  • 优化脚本,脚本只需要登录和查询菜单的,其他删除:
image-20230417170747869
  • 在回放设置中,打开日志,并将数据从服务器返回:
  • 点击回放,分析日志(如出现乱码,可按下步骤操作):
    • 在录制选项选择 UTF-8
    • 在回放选项中选择 UTF-8
    • 在工作项中将转换脚本编码去掉

控制台输出信息:

​ 那么为什么会返回登录过期呢?是因为我们没有做规则关联,导致传递信息的时候导致认证token没有传递过去。我们打开页面,在登录前打开F12然后,登录,在网络中观察login.domenu.do

​ 所以我们把login.do的动作的token放到menu.do中,做一种关联。操作的步骤是在脚本中添加如下脚本:

//从服务端返回中取认证数据--放login.do前
web_reg_save_param("devtToken",
"LB=\"devt_token\":\"",
"RB=\"",
LAST);

lr_log_message("取得结果为:%s",lr_eval_string("{devtToken}"));

//将认证数据放置头部信息中--放menu.do前
web_add_auto_header("Authorization","Bearer {devtToken}");

​ 如这些函数不记得,我们可以用关键字来搜索,搜索方式为:在脚本中鼠标右键---Insert---New Step---搜索关键字(此处搜索save),然后在函数web_reg_save_param中添加信息:

​ 生成脚本后,需要进行优化,否则转义时可能报错,如双引号需要进行转义,优化后的脚本如下:

web_reg_save_param("devtToken",
    "LB=\"devt_token\":\"",
    "RB=\"",
    "NotFound=ERROR",
    "Search=Body",
    LAST);

​ 注意关联的脚本会放在动作之前。生成脚本后需要将该参数放到menu.do的头部信息,我们在menu.do前还是Insert---New Step,添加一个web_add_auto_header的函数,添加的函数为menu.do中的认证:

image-20230421120232213
web_add_auto_header("Authorization",
		"Bearer {devtToken}");
image-20230421120300663

此时,重新回放,目录信息生成。

4.2.2.自动关联

​ 由 loadrunner 内置规则在录制时会自动生成关联,比如 JSESSION,COOKIE 自关动联。另外所有的自动关联均可以用手动关联替代,因此手动关联是我们的学习重点。

4.3.事务的定义

​ 事务(Transaction)用于模拟用户的一个相对完整的、有意义的业务操作过程,例如登录、查询、转账,这些都可以作为事务,而一般不会把每次 HTTP 请求作为一个事务。

​ 在LoadRunner中,关于事务函数有:

lr_start_transaction("devt-query");				// 事务开始
lr_end_transaction("devt-query",LR_AUTO);		// 事务结束
lr_end_transaction("devt-query",LR_PASS);		// 事务成功结束
lr_end_transaction("devt-query",LR_FAIL);		// 事务失败结束

​ 示例:

//不检查结果,自动结束
lr_end_transaction("devt-query",LR_AUTO);
//判断是否成功
if(atoi(lr_eval_string("{param1}"))>0)
{
	lr_end_transaction("devt-query",LR_PASS);
}
else
{
	lr_end_transaction("devt-query",LR_FAIL);
}
  • 录制一个简单的脚本,登录页面,并做搜索操作:
image-20230421141935857
  • 优化脚本,保留login.do,menu.do.list.do_3
image-20230421142425115
  • 在登录之前和查询之后(即最前面和最后面)添加开始事务和结束事务,添加方式为:鼠标右键---Insert----Start Transaction/End Transaction注意:事务的名字要相同,否则会报错
lr_start_transaction("_search");
lr_end_transaction("_search", LR_AUTO);
  • list.do_3前面添加一个参数规则web_reg_save_param,并将双引号进行转义,参数的名字为页面list.do中的返回码repCode
image-20230421144345004

添加规则完成后:

image-20230421144435324
  • 再修改结束事务的判断,如果大于0为成功,其他为失败:
if(atoi(lr_eval_string("{repCode}"))>0)
{
	lr_end_transaction("_search",LR_PASS);
}
else
{
	lr_end_transaction("_search",LR_FAIL);
}
image-20230421144830649
  • 回放脚本,成功:

image-20230421144858081

4.4.结果检查

​ 利用结果检查函数,我们可以判断业务是否正确,类似于Jmeter的断言。检查函数有两个可以选择:

  • 第一种:
web_reg_find("Text=Welcome",
	LAST );
  • 第二种:
web_reg_find("Fail=NotFound",
    "Search=Body",
    "SaveCount=Token_Count",
    "TextPfx=\"devt_token\":\"",
    "TextSfx=\"",
    LAST);

​ 我们以4.3录制的脚本为例,将事务的相关函数删除,我们检查是否登录成功。以第一种方式,添加一个web_reg_find()函数:

image-20230421151540831 image-20230421151604964

​ 回放脚本,匹配成功:

image-20230421151640318

5.常用函数

​ 针对LR中常用的函数,我们要比较熟悉,一些不清楚的地方,记得查询官网帮助,以下是官网在线文档,非常具有参考价值。

  • LR函数参考:https://admhelp.microfocus.com/vugen/en/2021-2021_R1/help/function_reference/Content/FuncRef/FuncRef.htm
  • C语言函数参考:https://admhelp.microfocus.com/vugen/en/2021-2021_R1/help/function_reference/Content/FuncRef/c_language/etc/lrFuncRef_C_Lng_Funcs_cat.htm
  • LR 常用函数参考©:https://admhelp.microfocus.com/vugen/en/2021-2021_R1/help/function_reference/Content/FuncRef/c_vuser/etc/lrFuncRef_CV_Utility_Functions.htm
  • LR 常用函数参考(Java):https://admhelp.microfocus.com/vugen/en/2021-2021_R1/help/function_reference/Content/FuncRef/java_vuser/etc/lrFuncRef_JV_Utility_Functions.htm
  • LR 常用函数参考(JS):https://admhelp.microfocus.com/vugen/en/2021-2021_R1/help/function_reference/Content/FuncRef/scripting/etc/lrFuncRef_VB_Utility_Funcs.htm

5.1.参数类函数

  • lr_eval_string():将参数转化为字符串。
lr_save_int(0, "searchCount");   	// 将数据存为int类型
lr_save_string("我是字符串", "str1");  // 将数据存为string
lr_output_message("Is zero, %s", lr_eval_string("{str1}"));
if (atoi(lr_eval_string("{searchCount}")) == 0) {
    lr_output_message("Is zero, %s", lr_eval_string("{searchCount}"));
}
lr_save_int(47, "searchCount");
if (atoi(lr_eval_string("{searchCount}")) != 0) {
    lr_output_message("Not zero, %s", lr_eval_string("{searchCount}"));
}

运行结果:

image-20230421155327234
  • lr_eval_json():将字符串或者文件转换为 Json 对象。
char* json_input = "{"
    "\"firstName\": \"John\","
    "\"lastName\": \"Smith\""
"}";
lr_save_string(json_input, "JSON_Input_Param");
// 创建 json 对象
lr_eval_json("Buffer={JSON_Input_Param}",
	"JsonObject=json_obj_1", LAST);
//修改 json 对象值
lr_json_set_values("JsonObject=json_obj_1",
    "Value=test111111",
    "QueryString=$.firstName",
    "SelectAll=Yes",
    LAST);
//取得 json 对象值
lr_json_get_values("JsonObject=json_obj_1",
    "ValueParam=test1_result",
    "QueryString=$.firstName",
    LAST);
//json 传字符串
lr_json_stringify("JsonObject=json_obj_1","Format=compact","OutputParam=Result",LAST );
//打印结果
lr_log_message("%s--%s",lr_eval_string("{test1_result}"),lr_eval_string("{Result}"));

运行结果:

image-20230421155620083
  • lr_json_set_values():修改 Json 对象中的值。
  • lr_json_set_values():获取 Json 对象中的值。
  • lr_json_stringify():Json 对象转字符串。
  • lr_save_string():将变量以字符串类型存入参数。
char str1[100]="我是测试字符串";
lr_save_string(str1,"param1");
lr_log_message("%s",lr_eval_string("{param1}"));
  • lr_save_int():将变量以数字类型存入参数
int n=1314;
lr_save_int(n,"param1");
lr_log_message("%s",lr_eval_string("{param1}"));
  • lr_save_datetime():时间格式化,精确到秒,存入参数
lr_save_datetime("%Y-%m-%d %H:%M:%S", TIME_NOW, "currDateTime");
lr_output_message(lr_eval_string("{currDateTime}"));
  • lr_save_timestamp():将时间戳存入到参数,可精确到秒,毫秒,微秒
//以秒为单位
lr_save_timestamp("param", "DIGITS=10", LAST );
lr_output_message(lr_eval_string("{param}"));
//以毫秒为单位
lr_save_timestamp("param", LAST );
lr_output_message(lr_eval_string("{param}"));
//以微秒为单位
lr_save_timestamp("param", "DIGITS=16", LAST );
lr_output_message(lr_eval_string("{param}"));
  • web_save_timestamp_param():将时间戳存入到参数,固定为毫秒
web_save_timestamp_param("tStamp", LAST );//默认以毫秒为单位,将时间戳存入参数
lr_output_message(lr_eval_string("Timestamp: {tStamp}"));
  • lr_param_sprintf():格式化字符串,类似 sprintf
int index = 56;
char * suffix = "txt";
//底层采用 C 的 sprintf 函数
lr_param_sprintf ("LOG_NAME_PARAM", "log_%d.%s", index, suffix,100);
lr_output_message("The new file name is %s",lr_eval_string("{LOG_NAME_PARAM}"));
  • lr_convert_string_encoding():编码转换,可防止中文乱码
lr_convert_string_encoding("我是中文",LR_ENC_SYSTEM_LOCALE,LR_ENC_UTF8,"ReplyMessage");

5.2.检查关联类

该类函数一般跟在 web actions 函数前面。

  • web_reg_find():在行动函数前面搜索指字符串,默认找不到则失败。
//搜索指定字符串
web_reg_find("Search=Body",
    "Text=devt_token",
    LAST);
//搜索指定字符串(开始+结束)
web_reg_find("Search=Body",
    "TextPfx=\"devt_token\":\"",
    "TextSfx=\"",
    LAST);
//通配搜索
web_reg_find("Text/ALNUMLC=\"devt_toke^\"",
	LAST);
web_custom_request("login.do_3",
    "URL={host}/devt-service/api/login.do",
    "Method=POST",
    "Resource=0",
    "RecContentType=application/json",
    "Referer={host}/devt-web/",
    94
    "Snapshot=t290.inf",
    "Mode=HTML",
    "EncType=application/json",
    "Body={\"acctName\":\"admin\",\"acctPwd\":\"11111111\",\"type\":\"0\"}",
    LAST);
  • web_reg_save_param():将动态数据注册到参数。
//从服务端返回中取得数据,支持双右边界
web_reg_save_param("devtToken",
    "LB=\"devt_token\":\"",
    "RB=\"",
    LAST);
web_custom_request("login.do_3",
    "URL={host}/devt-service/api/login.do",
    "Method=POST",
    "Resource=0",
    "RecContentType=application/json",
    "Referer={host}/devt-web/",
    "Snapshot=t290.inf",
    "Mode=HTML",
    "EncType=application/json",
    "Body={\"acctName\":\"admin\",\"acctPwd\":\"11111111\",\"type\":\"0\"}",
    LAST);
  • web_reg_save_param_regexp():将动态数据注册到参数,比 web_reg_save_param 功能强大。
//从服务端返回中取得数据
web_reg_save_param_regexp(
    "ParamName=devtToken",
    "RegExp=(\"devt_token\":\".*?\")",
    "Group=0",
    SEARCH_FILTERS,
    "Scope=BODY",
    LAST);
web_custom_request("login.do_3",
    "URL={host}/devt-service/api/login.do",
    "Method=POST",
    "Resource=0",
    "RecContentType=application/json",
    "Referer={host}/devt-web/",
    "Snapshot=t290.inf",
    "Mode=HTML",
    "EncType=application/json",
    "Body={\"acctName\":\"admin\",\"acctPwd\":\"11111111\",\"type\":\"0\"}",LAST);
lr_log_message("取得 token 为:%s",lr_eval_string("{devtToken}"));
  • web_reg_save_param_json():以 json 方式解析动态数据并注册到参数(要求数据为 json)。
//从服务端返回中取得数据
web_reg_save_param_json(
    "ParamName=devtToken",
    "QueryString=$..devt_token",
    SEARCH_FILTERS,
    "Scope=BODY",
    LAST);
  • web_reg_save_param_xpath():以 xpath 方式解析动态数据并注册到参数(要求数据为 xml)。
web_reg_save_param_xpath( 
    "ParamName=CorrelationParameter", 	"QueryString=/LR_EXTENSION[1]/object[1]/object[1]/array[1]",
    "DFEs=JsonXml",
    "ReturnXML=Yes", 
    SEARCH_FILTERS, 
    "Scope=Body",
    LAST);
  • web_reg_save_param_attrib():以 html 文档方式解析动态数据并注册到参数。
//<INPUT TYPE="HIDDEN" NAME="field1" VALUE="Usj3xEEneBBTcIi6IXNHBBaz7D7ytfDmmbgOKjVuc59C">
web_reg_save_param_attrib(
    "ParamName=param1",
    "TagName=input",
    "Extract=value",
    "Name=field1",
    "Type=*",
    SEARCH_FILTERS,
    "IgnoreRedirections=No", 
    LAST);

5.3.日志类函数

  • lr_output_message():向日志文件、输出窗口和其他测试报告摘要发送消息。
lr_output_message("取得 token 为:%s",lr_eval_string("{devtToken}"));
  • lr_log_message():向日志文件发送消息。
lr_output_message("取得 token 为:%s",lr_eval_string("{devtToken}"));
  • lr_debug_message():向日志文件发送调试消息(需要设置日志级别)。
lr_debug_message(LR_MSG_CLASS_RESULT_DATA | LR_MSG_CLASS_PARAMETERS,lr_eval_string("1111{devtToken}"));
  • lr_error_message():向日志文件发送错误消息(红色显示)。
lr_error_message("取得 token 为:%s",lr_eval_string("{devtToken}"));

6.LR语言编程

​ LR虽然也支持JS、Java等语言进行编程,但由于C语言在LR的执行效率更高,所以一般建议使用C语言进行编程。

6.1.变量的定义

LoadRunner 中的变量即其实就是 C 语言变量,不过有以下约束

  1. 变量可以定义在.h 文件中

  2. 变量可以定义在 Action 函数外部

  3. 变量可以定义在 Action 开始部分

  4. Action 体内不可以定义变量

示例:

int a1=0;
int a2=0;
char* p1;
char str2[]="string111111222";
Action()
{
    lr_log_message("%s",&str2[5]);
    p1=lr_eval_string("{param1}");
    lr_log_message("%s",p1);
    //int a3=0;//此处编译会报错
    return 0;
}

6.2.参数的操作

  • 字符类参数的操作(未出现的参数可以在Parameters中定义,类型可为Custom
char str1[]="str111";
//参数的取值
lr_log_message("%s",lr_eval_string("{param1}"));
lr_log_message("%s",lr_eval_string("{param2}"));
//参数的赋值
lr_save_string(str1,"{param1}");
lr_save_string("new param2","{param2}");
//参数的取值
lr_log_message("%s",lr_eval_string("{param1}"));
lr_log_message("%s",lr_eval_string("{param2}"));
  • int类型参数的操作
//参数的取值
lr_log_message("%s",lr_eval_string("{param1}"));
lr_log_message("%s",lr_eval_string("{param2}"));
//参数的赋值
lr_save_int(11,"param1");
lr_save_int(22,"param2");
//参数的取值
lr_log_message("%s",lr_eval_string("{param1}"));
lr_log_message("%s",lr_eval_string("{param2}"));
  • 参数的互转
char str1[]="str111";
char format1[200]="";
int n1=0;
//参数转字符串变量
strcpy(str1, lr_eval_string("{param1}"));
lr_output_message("%s", str1);
//参数转数值变量(参数统一为字符,故先转 int)
n1=atoi(lr_eval_string("{param2}"));
lr_output_message("%d", n1);
//格式化参数
sprintf(format1,"%s---%d",str1,n1);
lr_log_message("%s",format1);
//参数转参数
lr_save_string(lr_eval_string("{param1}"),"param2");

6.3.逻辑控制

​ C 语言中的顺序,分支,循环同样适用于 lr,C 语言中的函数同样可以在 lr 中使用,lr 中的函数可以定义在 Action 外部,也可以定义在.h 文件中

​ 示例:

void say(int flag){
    if(flag==0){
        lr_log_message("我是偶数");
    }else{
        lr_log_message("我是奇数");
    }
}
Action() {
	int i=0;
    for(i=0;i<10;i++){
    	if(i%2==0){
    		say(0);
    	}else{
    		say(1);
    	}
	}
return 0;
}

6.4.JS脚本

​ JS脚本在LR中使用可以通过两种方式:1.在C语言脚本中通过web_js_run()函数来运行;2.在脚本录制时,选用js为主体的脚本,效果和C语言一样。

​ 在执行JS脚本前,需要让LR允许执行JS脚本。设置方式为:Runtime Settings---Internet Protocol---Perferences

  • web_js_run()函数运行:

    • 创建一个脚本,取名为js脚本
    • 准本js1.js文件,放置在工作目录下:
    function test1(){
    	return new Date().getTime();
    }
    function test2(a,b){
    	return a+b;
    }
    
    • 准备 js2.js文件放置到工作目录下:
    function test3() {
        var user = '{ "id": 1, "name": "longge1" }';
        var tuser = JSON.parse(user);
        return tuser.name;
    }
    

    • LR主脚本:
    web_js_run("Code=test3()",
        "ResultParam=jsVal",
        SOURCES,"File=js1.js",
        ENDITEM,"File=js2.js",
        ENDITEM,
        LAST);
    lr_log_message("%s",lr_eval_string("{jsVal}"));
    

    正常运行:

    image-20230422123947582
  • 直接使用JS录制脚本:

    • 录制一个简单脚本,在录制设置中将脚本语言更改为js:
    - 录制完成,生成js脚本: image-20230422130913666

6.5.Java over HTTP脚本

​ LR也可以使用Java作为脚本语言进行录制。但需要注意的是:Java over HTTP在Linux系统中不支持,所以必须采用压力机。

  • 创建一个脚本,脚本类型选择Java over HTTP
image-20230422131735899

6.6.唯一序列

​ 在一些场景压测时,需要唯一序列号,我们可以采用如下思路实现,我们可以让每一个 Vuser 的序列统一采用 VuserID+时间戳(时间戳不重复)。

image-20230422135743428
char seq[20];
/**
* 取唯一序列
*/
char* GetSeq(){
    //先取第一个 Vuser 唯一前缀,保证不同线程之间不冲突
    char* start=lr_eval_string("{start}");
    web_save_timestamp_param("tamp",LAST);
    //保证线程内部不冲突
    sleep(1);
    sprintf(seq,"%s%s",lr_eval_string("{start}"),lr_eval_string("{tamp}"));
    return seq;
}
Action()
{
    int i=0;
    for(i=0;i<100;i=i+1)
    {
        lr_log_message("%s",GetSeq());
    }
    return 0;
}

6.7.实例考试-登录脚本

实战考试,录制一套完整的脚本,内容包括登录动作:

1、登陆逻辑放置在 actions 里面

2、易变的常量实现参数化(主机、用户名、密码、端口……)

3、登录实现规则关联

4、登陆采用 5 个以上账号

  • 创建脚本,录制脚本,登录管理平台,优化脚本,只剩login.do的脚本:
image-20230422151038579
  • 将URL中http://cfgjt.cn:8981参数化,选中后右键—Replace with Parameter---Create new Param,参数类型为custom
image-20230422151327532 image-20230422151411194
  • 将脚本中的账号和密码参数化:选中admin/11111111---右键Replace with Parameter---Create new Param,参数类型为File
image-20230422151855936
  • 打开左边Parameters,设置5个账户,密码都是11111111:
image-20230422152040763 image-20230422152116091
  • 在登录脚本上方插入函数:
  • 并开启/关闭事务,判断账户是否过期,全篇脚本如下:
Action()
{
	//注释1
	lr_start_transaction("login");
	
	web_reg_save_param_ex(
		"ParamName=devtToken",
		"LB=\"devt_token\":\"",
		"RB=\"",
		SEARCH_FILTERS,
		"Scope=BODY",
		LAST);

	web_custom_request("login.do", 
		"URL={hostPort}/devt-service/api/login.do", 
		"Method=POST", 
		"Resource=0", 
		"RecContentType=application/json", 
		"Referer={hostPort}/devt-web/", 
		"Snapshot=t2.inf", 
		"Mode=HTML", 
		"EncType=application/json", 
		"Body={\"acctName\":\"{acctName}\",\"acctPwd\":\"{acctPwd}\",\"type\":\"0\"}", 
		LAST);
	if(strcmp(lr_eval_string("{devtToken}"),"{devtToken}")==0){
		lr_end_transaction("login",LR_FAIL);
	}else{
		lr_end_transaction("login",LR_PASS);		
	}
	return 0;
}
  • 回放脚本,成功:
image-20230422153224244

6.8.实例考试-单查询脚本

实战考试,录制一套完整的脚本,内容包括至少一个单查询:

1、登陆放置在 init 里面,退出放置在 end,核心逻辑放置在 actions 里面

2、易变的常量实现参数化(主机、用户名、密码、端口……)

3、登录实现规则关联

4、选取一个查询条件设计为 20 种以上组合

5、查询设置为一个事务,并采用业务判断成功与否

  • vuser_init脚本参考:
vuser_init()
{
	//注释
	lr_start_transaction("login");
	
	web_reg_save_param_ex(
		"ParamName=devtToken",
		"LB=\"devt_token\":\"",
		"RB=\"",
		SEARCH_FILTERS,
		"Scope=BODY",
		LAST);

	web_custom_request("login.do", 
		"URL={hostPort}/devt-service/api/login.do", 
		"Method=POST", 
		"Resource=0", 
		"RecContentType=application/json", 
		"Referer={hostPort}/devt-web/", 
		"Snapshot=t2.inf", 
		"Mode=HTML", 
		"EncType=application/json", 
		"Body={\"acctName\":\"{acctName}\",\"acctPwd\":\"{acctPwd}\",\"type\":\"0\"}", 
		LAST);
	if(strcmp(lr_eval_string("{devtToken}"),"{devtToken}")==0){
		lr_end_transaction("login",LR_FAIL);
	}else{
		lr_end_transaction("login",LR_PASS);		
	}
	return 0;
}
  • action脚本参考:
Action()
{
	
	lr_start_transaction("query1");
	web_add_auto_header("Authorization",
	                    lr_eval_string("Bearer {devtToken}"));

	web_reg_find("Text=rspCode",
		LAST);

	web_custom_request("list.do_3", 
		"URL={hostPort}/devt-service/devtComp/list.do", 
		"Method=POST", 
		"Resource=0", 
		"RecContentType=application/json", 
		"Referer={hostPort}/devt-web/", 
		"Snapshot=t8.inf", 
		"Mode=HTML", 
		"EncType=application/json", 
		"Body={\"currentPage\":1,\"pageSize\":10,\"name\":\"{qryText}\",\"treeId\":null}", 
		LAST);
	lr_end_transaction("query1",LR_AUTO);
	return 0;
}

6.9.实例考试-入库脚本

实战考试,录制一套完整的脚本,内容包括一次提交动作

1、登陆放置在 init 里面,退出放置在 end,核心逻辑放置在 actions 里面

2、易变的常量实现参数化(主机、用户名、密码、端口……)

3、登录实现规则关联

4、入库内容采用自定义函数模拟生成

5、入库设置为一个事务,并采用业务判断成功与否

  • vuser_init脚本参考:
vuser_init()
{
	//ע��
	lr_start_transaction("login");
	
	web_reg_save_param_ex(
		"ParamName=devtToken",
		"LB=\"devt_token\":\"",
		"RB=\"",
		SEARCH_FILTERS,
		"Scope=BODY",
		LAST);

	web_custom_request("login.do", 
		"URL={hostPort}/devt-service/api/login.do", 
		"Method=POST", 
		"Resource=0", 
		"RecContentType=application/json", 
		"Referer={hostPort}/devt-web/", 
		"Snapshot=t2.inf", 
		"Mode=HTML", 
		"EncType=application/json", 
		"Body={\"acctName\":\"{acctName}\",\"acctPwd\":\"{acctPwd}\",\"type\":\"0\"}", 
		LAST);
	if(strcmp(lr_eval_string("{devtToken}"),"{devtToken}")==0){
		lr_end_transaction("login",LR_FAIL);
	}else{
		lr_end_transaction("login",LR_PASS);		
	}
	return 0;
}
  • action脚本参考:
GetTitle(){
	web_save_timestamp_param("timestamp", 
		LAST);
	lr_save_string(lr_eval_string("标题{timestamp}"),"title");
}
Action()
{
	lr_start_transaction("save");
	web_add_auto_header("Authorization",
	                    lr_eval_string("Bearer {devtToken}"));
	GetTitle();
	web_reg_find("Text=rspCode",
		LAST);
	web_custom_request("save.do", 
		"URL={hostPort}/devt-service/devtMsg/save.do", 
		"Method=POST", 
		"Resource=0", 
		"RecContentType=application/json", 
		"Referer={hostPort}/devt-web/", 
		"Snapshot=t29.inf", 
		"Mode=HTML", 
		"EncType=application/json", 
		"Body={\"name\":\"{title}\",\"type\":\"1\",\"msg\":\"内容\"}", 
		LAST);
	lr_end_transaction("save",LR_AUTO);
	return 0;
}

7.场景压测

​ LoadRunner 中场景(Scenario)是一种用来模拟大量用户操作的技术手段,通过配置和执行场景向服务器产生负载,验证系统各项性能指标是否达到。

用户要求;场景通过一系列的脚本组合,并通过 1 到多个压力机来产生负载

  1. 了解一下 Controller 界面,大体熟悉一下各项菜单

  2. 场景分两种,一种是计划场景,一种是目标场景

  3. 场景先进行设计,然后再运行,运行时可查看各项性能指标,运行完成后可分析性能结果

场景压测分为计划场景和目标场景。

7.1.计划场景设计和压测

​ 计划场景有成手动场景,是指按照测试人员的计划进行设计测试。

  • 在已有的脚本中点击导航栏Integrations---Create Controller Scenario
  • 当创建好场景后,虚拟用户生成器会自动打开调度器Controller,此时我们就需要取按照测试人员的计划取设计计划表:

  • 设计好场景后就可以点击下方的菜单run运行:

7.2.目标场景设计和压测

  • 虚拟用户生成器中按7.1的步骤创建目标场景压测:
image-20230422212305135

  • 设计好后可以点击Run进行运行。

7.3.组合场景压测

​ 组合场景压测的方式和单个脚本大抵相同,只不过是将多个脚本进行组合后进行压测。

  • 在打开Controller时,添加脚本,选择多个脚本,此处选择考试实例中的test1,test2,test3三个脚本:
  • 其他设置和单个脚本压测的方式一样:用户数、启动时间等。
image-20230424102449190

7.4.分布式压测

​ 在生产过程中,基本上选择的都是远程压测,即使用压力机压测Linux上的服务,这种方式即为分布式压测。

  • 点击添加主机:
  • 名字为IP地址,系统为Linux,压力机路径为安装路径:
  • 其他压测步骤和之前一样。

8.结果分析

9.1.分析需求

  1. TPS 越高,响应时间越小,性能越好,反之性能越差

  2. 同一场景中,可以采用前后对比测试,来确定前后性能的好坏(一般伴随着程序有优化处理)

  3. 同一场景中,可以采用横对比,来确定不同事务中性能好坏

性能根源的分析和调优比较复杂,牵涉到操作系统以及各方面的知识

操作系统性能分析*&调优(CPU**,磁盘,IO,网络,中断,进程**&文件数)*

JVM 性能分析*&调优(JVM* *模型,*JAVA 内存分析,JAVA 线程诊断)

HTTP&TCP 性能分析*&调优(连接数,长连接,短连接)*

9.2.输出报告

当测试场景测试完毕后,就可以分析结果了。这个结果通常交给第三方查看,步骤如下:

  • 测试场景测试结束,点击菜单栏Result---Analyze Result,此时就会跳转到分析器:
image-20230424103927127

ngrui/note-pic/raw/master/img/202304222123276.png" alt=“image-20230422212305135” style=“zoom:50%;” />

[外链图片转存中…(img-GGpMpNl9-1712134734793)]

  • 设计好后可以点击Run进行运行。

7.3.组合场景压测

​ 组合场景压测的方式和单个脚本大抵相同,只不过是将多个脚本进行组合后进行压测。

  • 在打开Controller时,添加脚本,选择多个脚本,此处选择考试实例中的test1,test2,test3三个脚本:
  • 其他设置和单个脚本压测的方式一样:用户数、启动时间等。
image-20230424102449190

7.4.分布式压测

​ 在生产过程中,基本上选择的都是远程压测,即使用压力机压测Linux上的服务,这种方式即为分布式压测。

  • 点击添加主机:
  • 名字为IP地址,系统为Linux,压力机路径为安装路径:
  • 其他压测步骤和之前一样。

8.结果分析

9.1.分析需求

  1. TPS 越高,响应时间越小,性能越好,反之性能越差

  2. 同一场景中,可以采用前后对比测试,来确定前后性能的好坏(一般伴随着程序有优化处理)

  3. 同一场景中,可以采用横对比,来确定不同事务中性能好坏

性能根源的分析和调优比较复杂,牵涉到操作系统以及各方面的知识

操作系统性能分析*&调优(CPU**,磁盘,IO,网络,中断,进程**&文件数)*

JVM 性能分析*&调优(JVM* *模型,*JAVA 内存分析,JAVA 线程诊断)

HTTP&TCP 性能分析*&调优(连接数,长连接,短连接)*

9.2.输出报告

当测试场景测试完毕后,就可以分析结果了。这个结果通常交给第三方查看,步骤如下:

  • 测试场景测试结束,点击菜单栏Result---Analyze Result,此时就会跳转到分析器:
image-20230424103927127

[外链图片转存中…(img-98JFdfgE-1712134734795)]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/515531.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu部署BOA服务器

BOA服务器概述 BOA是一款非常小巧的Web服务器&#xff0c;源代码开放、性能优秀、支持CGI通用网关接口技术&#xff0c;特别适合用在嵌入式系统中。 BOA服务器主要功能是在互联嵌入式设备之间进行信息交互&#xff0c;达到通用网络对嵌入式设备进行监控&#xff0c;并将反馈信…

【c/c++】深入探秘:C++内存管理的机制

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们详细讲解c中的动态内存管理 目录 1.C/C内存分布2.C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3.c内存管理方式3.1new/delete对内…

LeetCode-199. 二叉树的右视图【树 深度优先搜索 广度优先搜索 二叉树】

LeetCode-199. 二叉树的右视图【树 深度优先搜索 广度优先搜索 二叉树】 题目描述&#xff1a;解题思路一&#xff1a;广度优先搜索解题思路二&#xff1a;深度优先搜索解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它…

股权激励和期权激励对比辨析

文章目录 概念定义 收益方式 风险评估 应用和分析 股权激励和期权激励&#xff0c;两者的区别是什么&#xff0c;本文就来梳理对比一下。 概念定义 股权激励&#xff0c;是指上市公司以本公司股票为标的&#xff0c;对其董事、高级管理人员及其他员工进行的长期性激励。取得…

微服务(基础篇-008-es、kibana安装)

目录 05-初识ES-安装es_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LQ4y127n4?p81&vd_source60a35a11f813c6dff0b76089e5e138cc 1.部署单点es 1.1.创建网络 1.2.加载镜像 1.3.运行 2.部署kibana 2.1.部署 2.2.DevTools 3.安装IK分词器 3.1.在线安装ik…

程序员们应注意的行业特有的法律问题

大家好&#xff0c;我是不会魔法的兔子&#xff0c;是一枚执业律师&#xff0c;持续分享技术类行业项目风险及预防的问题。 一直以来兔子都在以大家做项目时候会遇到的风险问题做分享&#xff0c;最近有个念头一直挥之不去&#xff0c;就是要不要给我们广大的程序员们也分享一…

一文彻底搞懂ZooKeeper选举机制

文章目录 1. ZooKeeper 集群2. ZooKeeper 启动3. ZooKeeper 选举机制4. Follower&#xff08;跟随者&#xff09;和Candidate&#xff08;候选者&#xff09;节点区别5. Leader节点挂掉期间写操作是否会丢失 1. ZooKeeper 集群 ZooKeeper 是一个分布式的开源协调服务&#xff…

Node.js------模块化

◆ 能够说出模块化的好处◆ 能够知道 CommonJS 规定了哪些内容◆ 能够说出 Node.js 中模块的三大分类各自是什么◆ 能够使用 npm 管理包◆ 能够了解什么是规范的包结构◆ 能够了解模块的加载机制 一.模块化的基本概念 1.模块化 模块化是指解决一个复杂问题时&#xff0c…

基于SpringBoot+Thymeleaf的学生会管理系统

在这里插入图片描述 在这里插入图片描述

MYSQL——索引概念索引结构

索引 索引是帮助数据库高效获取数据的排好序的数据结构。 有无索引时&#xff0c;查询的区别 主要区别在于查询速度和系统资源的消耗。 查询速度&#xff1a; 在没有索引的情况下&#xff0c;数据库需要对表中的所有记录进行扫描&#xff0c;以找到符合查询条件的记录&#…

现在优秀企业都用SaaS知识库工具,原因就在这里

在这个信息化、知识化时代&#xff0c;企业的竞争力往往取决于能否有效管理和利用内部的知识资源。而如何实现这一任务呢&#xff1f;答案就在SaaS知识库工具。现在&#xff0c;很多优秀的企业已经使用了SaaS知识库工具进行知识管理&#xff0c;那么&#xff0c;他们为什么要这…

【洛谷 P8695】[蓝桥杯 2019 国 AC] 轨道炮 题解(映射+模拟+暴力枚举+桶排序)

[蓝桥杯 2019 国 AC] 轨道炮 题目描述 小明在玩一款战争游戏。地图上一共有 N N N 个敌方单位&#xff0c;可以看作 2D 平面上的点。其中第 i i i 个单位在 0 0 0 时刻的位置是 ( X i , Y i ) (X_i, Y_i) (Xi​,Yi​)&#xff0c;方向是 D i D_i Di​ (上下左右之一, 用…

kubadm部署kubernetes

什么是kubernetes Kubernetes是一款应用于集群的&#xff0c;容器自动部署、扩展和管理的开源平台&#xff0c;提供了一种以容器为中心的基础架构。利用kubernetes&#xff0c;你可以快速高效地响应客户如下请求&#xff1a; 应用程序的动态、精准部署应用程序的动态扩展无缝推…

【机器学习】K-近邻算法(KNN)介绍、应用及文本分类实现

一、引言 1.1 K-近邻算法&#xff08;KNN&#xff09;的基本概念 K-近邻算法&#xff08;K-Nearest Neighbors&#xff0c;简称KNN&#xff09;是一种基于实例的学习算法&#xff0c;它利用训练数据集中与待分类样本最相似的K个样本的类别来判断待分类样本所属的类别。KNN算法…

2024福建三支一扶报名流程,超全超详细!

2024年福建三支一扶报名已经开始&#xff0c;请注意时间&#xff01; ⚠2024年福建省省级“三支一扶”计划招募岗位1070个 报名时间&#xff1a;4月1日8:00至4月17日17:00 审查考核&#xff1a;4月18日至5月10日 确定派遣人员&#xff1a;5月11日至5月31日 报名入口&#xff1…

数据质量决定大模型能力,景联文科技提供高质量大模型数据

随着大模型的深入发展&#xff0c;各类资源要素的配置状态已悄然变化。其中&#xff0c;数据的价值已被提升到一个新高度。 大模型往往拥有庞大的参数和复杂的网络结构&#xff0c;需要大量的数据来学习和优化。数据的质量和数量直接决定了模型的训练效果。若数据不足或质量不佳…

【JavaScript 漫游】【051】Set 和 Map 数据结构

文章简介 本篇文章为【JavaScript 漫游】专栏的第 051 篇文章&#xff0c;记录了 ES6 规范新增的 Set 和 Map 数据结构的相关知识点。 SetWeakSetMapWeakMap Set 基本用法 类似于数组&#xff0c;但是成员的值都是唯一的&#xff0c;没有重复的值。 Set 本身是一个构造函…

IT外包行业未来发展趋势

随着企业对高可用性系统和分布式系统需求的增加&#xff0c;IT人才外包行业迎来了前所未有的发展机遇。未来几年&#xff0c; IT外包行业将呈现出一系列发展趋势 首先&#xff0c;IT外包人才队伍将不断壮大。随着企业对人效的需求日益增长&#xff0c;以及为规避用工风险和降低…

StarRocks实战——携程火车票指标平台建设

目录 前言 一、早期OLAP架构与痛点 二、指标平台重构整体设计 2.1 指标查询过程 2.1.1 明细类子查询 2.1.2 汇总类子查询 2.1.3 “缓存” 2.2 数据同步 三、Starrocks使用经验分享 3.1 建表经验 3.2 数据查询 3.3 函数问题 四、查询性能大幅提升 五、 后续优化方…

LeetCode575——分糖果

题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 这道题比较简单&#xff0c;但我还是花费了将近四个小时的时间去解答&#xff0c;AC的那一刻&#xff0c;终于全身舒畅&#xff0c;这道题的思路就是先求出糖果的种数&#xff0c;然后我们从题中可以得出&#x…