LoadRunner性能测试系统学习教程:Vuser发生器(上)

引言

Vuser发生器(VisualUserGenerator,简称为VuGen)主要通过捕获客户端向服务器发送的HTTP请求,将这些请求录制成脚本,在回放时将捕获的HTTP请求再次发送,以达到模拟客户的行为的目的,所以Vuser主要是用来捕获最终用户业务流程和创建自动化测试脚本,即生成测试脚本。VuGen是录制测试脚本、编辑与完善测试脚本的一个平台,支持C语言语法。
主要包括以下内容:
脚本录制
Recording Options设置
Run-Time Settings设置
脚本完善

脚本录制

启动VisualUserGenerator,创建一个新的脚本,开始录制脚本,在录制脚本过程中,VuGen会自动捕获操作过程中客户端与服务器端进行通信的所有数据。这里涉及的关键点是如何选择录制协议。
脚本开发主要包括四大步骤:计划、录制脚本、脚本增强和单机调试脚本,如图所示。
在这里插入图片描述

如何选择协议
在创建一个新的脚本时,首先会弹出一个对话框,在该对话框中选择录制时需要的协议,这步非常重要,选择的协议将直接影响到录制后的脚本是否理想,如何选择录制协议是录制前必须要解决的问题。
各种协议和相关头文件的对应关系如图所示。
在这里插入图片描述

选择协议的常用方法主要有以下几种:
1.最简单的方法就是向开发工程师确认数据通信所采用的协议,因为开发工程师最清楚应用程序采用的是何种通信协议。
2.没有开发工程师支持时,可以通过概要或详细设计手册获知所使用的协议。
3.使用协议分析工具捕获通信时的数据包并进行分析,然后确定被测对象所使用的协议。在使用协议分析工具分析协议过程中一定要摒除底层协议,不要被底层协议所迷惑。
4.根据以往测试经验来判断被测试对象采用的协议,这种方法具有猜测性,有时候不一定准确。
在LoadRunner新的版本中有一个协议分析的工具(ProtocolAdvisor),通过该工具可以分析当前系统所使用的协议。协议分析工具的使用步骤如下:
1.在【WelcometotheVirtualUserGenerator】界面单击【ProtocolAdvisor】按钮,如图所示。
在这里插入图片描述

2.弹出【ProtocolAdvisor】对话框,如图所示。
在这里插入图片描述

Applicationtype:选择应用程序的类型,被测试的应用程序类型主要包括两类:InternetApplictions和Win32Applications,也即通常说的B/S和C/S框架的两类应用程序。
Programtoanalyze:分析的程序,如果选择的应用程序类型为InternetApplications那么,该选项为MicrosoftInternetExplorer,即IE浏览器,因为LoadRunner在录制时默认启动IE浏览器进行录制;如果选择的应用程序类型为Win32Applications,那么该选项为需要测试的应用程序的路径。
URLAddress:即需要分析的网页地址;
Workingdirectory:工作目录,默认的为LoadRunner所在路径的bin目录。
3.设置待分析程序的路径或URL地址,单击【OK】按钮,即开始分析应用程序,通常分析一个简单的业务即可停止分析。
4.单击浮动框中的【StopAnalyzing】按钮,停止分析应用程序,并产生分析后的结果。
LoadRunner提供了多种协议,具体的协议分类见表。

在这里插入图片描述

1.单协议脚本:创建单协议Vuser脚本,在对话框中选择录制时需要的协议,如图所示。创建新脚本时,会弹出一个对话框,LoadRunner提供三种选择协议的方式:单协议脚本、多协议脚本和最近使用过的协议。
在这里插入图片描述

2.多协议脚本:创建多协议Vuser脚本。在AvailableProtocols中选择一个或多个协议,点击右箭头,将其移入到SelectedProtocols部分中,同样,在SelectedProtocols中选择一个或多个协议,点击左箭头可以移除选中的协议,如图所示。
在这里插入图片描述

3.最近使用过的协议:从最近录制脚本的协议列表中,选择一种协议进行录制,如图所示
在这里插入图片描述

开始录制脚本

协议选择好后可以开始录制脚本。这里以Web(HTTP/HTML)协议为例进行录制。
VuGen录制浏览器主要是通过代理的方式来实现的。开始录制时,VuGen打开浏览器(默认使用Mircosoft自带的IE浏览器,使用其它浏览器录制容易出现HTTP请求被丢失的现象,所以尽量使用IE浏览器进行录制),并以VuGen作为代理来访问目标服务器。这样,VuGen就可以捕获客户端与服务器之间通过的数据包,如图所示。
在这里插入图片描述

在使用VuGen进行录制用户操作时,VuGen会对捕获的数据进行分析,并将其还原成对应协议的由API组成的脚本。同时,VuGen会将这些函数生成的脚本插入到VuGen编辑器中,以创建原始的Vuser脚本。
录制时系统弹出一个录制窗口,如图所示。
在这里插入图片描述

在URLAddress中输入要录制的站点地址。RecordintoAction选项表示将录制的代码放到哪个部分。LoadRunner生成的代码由三部分组成:vuser_init、Action和vuser¬_end。
注意:一般情况下都是将生成的代码放在Action部分,因为vuser_init和vuser¬_end两部分的代码只会执行一次,这样会出现这种问题,客户的并发虚拟用户只执行一次,执行完成一次后再也不执行,这样就没有HTTP请求给服务器,也即服务器没有压力。如下例子,图是每秒点击率的值。
在这里插入图片描述

从图中可以看出,在场景执行到25秒后,客户端的点击率为0,即25秒后客户端没有提交任何请求,这就是典型的由于将脚本放在vuser_init引起的,因为脚本放在vuser_init中,导致每个虚拟用户只会执行一次这部分的脚本,当用户加载完成后,再也不行,所以看到后期的点击率都为0。
Recordtheapplicationstartup选项表示应用程序一旦启动,VuGen就立即开始录制;如果不选中,应用程序启动后,VuGen会弹出如图所示的对话框,并且暂时不会进行录制,当用户操作应用程序到需要录制的地方时,点击Record按钮,VuGen才开始录制。默认情况下Recordtheapplicationstartup是选中的状态。点击Record按钮开启录制。
在这里插入图片描述

在录制前还需要注意在RecordinOptions设置对话框中,设计脚本录制的方式,关于脚本的录制方式将在3.2.1小节中详细介绍。
开始录制后,会出现如图所示的工具栏
在这里插入图片描述

该工具条从左到右依次代表开始录制、暂停录制、停止录制、新建Action、在脚本与录制界面之间切换、添加开始事务标识、添加结束事务标识、设置集合点和添加注释。
录制过程中,LoadRunner会自动记录用户的操作。录制完成后,点击“停止录制”按钮结束录制,这时VuGen会自动生成一个脚本,如图所示。
在这里插入图片描述

这是一个比较简单的脚本,但可以看出LoadRunner生成的脚本都是由函数组成。

Recording Options设置

在进行录制时,首先要对录制的一些参数进行设置,只有将这些参数设置好,才能录制并生成需要的脚本。
首先是RecordingOptions设置,需要注意的设置项有:Recording选项卡、Advanced选项卡和Correlation选项卡。
在Tools菜单中选择RecordingOptions或直接按快捷键Ctrl+F7进入参数设置对话框,如图所示。
在这里插入图片描述

Recording选项卡
在RecordingOptions对话框中,选择Recording选项卡。RecordingLevel包含两种录制模式:HTML-basedscript和URL-basedscript,如图所示
在这里插入图片描述

默认情况下选中HTML-basedscript录制方式。当然,两种录制模式也存在差别

单击【HTMLAdvanced…】按钮,弹出【AdvancedHTML】对话框,如图所示
在这里插入图片描述

在该对话框中关于HTML-basedscript脚本方式又有两种:“Ascriptdescribinguseractions”和“Ascriptcontainingexplicit”。
Ascriptdescribinguseractions:模拟用户行为录制,即GUI录制,把用户每一步的操作显示出来,最后生成的脚本非常直观并且会将上下文的一些敏感信息记录下来。它创建URL(web_url)、link(web_link)、image(web_image)和提交表单(web_submit_form)。
下面以Ascriptdescribinguseractions方式录制一个登录的功能,录制后的代码如下:
web_url(“WebTours”,
“URL=http://127.0.0.1:1080/WebTours/”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=”,
“Snapshot=t1.inf”,
“Mode=HTML”,
LAST);
lr_think_time(4);
web_submit_form(“login.pl”,
“Snapshot=t2.inf”,
ITEMDATA,
“Name=username”,“Value=test1”,ENDITEM,
“Name=password”,“Value=1”,ENDITEM,
“Name=login.x”,“Value=65”,ENDITEM,
“Name=login.y”,“Value=8”,ENDITEM,
LAST);
return0;
从生成的代码中可以看到,在录制时只做了两个操作,生成的代码也只有两个函数,也即这种录制模式只录制用户的操作,其它的内容不会被录制。使用的提交信息函数为web_submit_form()。
AscriptcontaningexplictURLsonly:录制所有links(链接)、images(图片)和URL(web_url),但不创建web_link、web_image和提交表单(web_submit_form)。这种录制方式生成的脚本不直观。
下面以AscriptcontaningexplictURLSonly方式录制一个登录的功能,录制后的代码如下:
web_url(“WebTours”,
“URL=http://127.0.0.1:1080/WebTours/”,
“TargetFrame=”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=”,
“Snapshot=t1.inf”,
“Mode=HTML”,
LAST);
web_submit_data(“login.pl”,
“Action=http://127.0.0.1:1080/WebTours/login.pl”,
“Method=POST”,
“TargetFrame=body”,
“RecContentType=text/html”,
“Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home”,
“Snapshot=t2.inf”,
“Mode=HTML”,
ITEMDATA,
“Name=userSession”,“Value=108748.859052248ffQDHzQptDHfDDfzcpzVzzcf”,ENDITEM,
“Name=username”,“Value=test1”,ENDITEM,
“Name=password”,“Value=1”,ENDITEM,
“Name=JSFormSubmit”,“Value=off”,ENDITEM,
“Name=login.x”,“Value=53”,ENDITEM,
“Name=login.y”,“Value=10”,ENDITEM,
LAST);
return0;
从生成的代码中可以看到,同样的提交登录的信息但使用的函数为web_submit_data(),不再以表单的方式提交,而web_submit_form()函数则是以表单的信息进行提交的,该函数运行时,首先在页面上去查找表单,再提交数据,而web_submit_data()则不需要,直接向服务器发送要提交的数据。
但在录制过程中很可能会录制到一些非HTML的元素(如Java小程序、XML、ActiveX元素、JavaScript),这些非HTML元素主要用于包含或去获取自己的一些资源,例如,JavaScript的JS文件用于调用加载多个图片。对于这类非HTML的元素,录制时有三种方式:
Recordwithinthecurrentscriptstep:在录制时对于非HTML资源并不会生成一个新的功能。它列出所有相关资源的参数,如web_url、web_link和web_submit_data。这些web功能的参数使用EXTRARES标示。
如以下代码:
web_url(“index.asp”,
“URL=http://www.daisy.com/index.asp”,
“TargetFrame=”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=”,
“Snapshot=t2.inf”,
“Mode=HTML”,
EXTRARES,
“Url=http://www.daisy.com/ScrollApplet.class”,“Referer=”,ENDITEM,
“Url=http://www.daisy.com/board.txt”,“Referer=”,ENDITEM,
“Url=http://www.daisy.com/nav_login1.gif”,ENDITEM,

LAST);
Recordinseparatestepsanduseconcurrentgroups:在一个组中记录这些单独的步骤,为每个非HTML资源创建一个新的功能(但不包括一些页面的功能,如web_url、web_link等)。所有的web_url资源都将放置并行组中(并行组由web_concurrent_start和web_concurrent_end进行标示)。
如以下代码:
web_url(“index.asp”,
“URL=http://www.daisy.com/index.asp”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=”,
“Snapshot=t2.inf”,
“Mode=HTML”,
LAST);
web_concurrent_start(NULL);
web_url(“ScrollApplet.class”,
“URL=http://www.daisy.com/ScrollApplet.class”,
“Resource=1”,
“RecContentType=application/octet-stream”,
“Referer=”,
LAST);
web_url(“board.txt”,
“URL=http://www.daisy.com/board.txt”,
“Resource=1”,
“RecContentType=text/plain”,
“Referer=”,
LAST);
web_concurrent_end(NULL);
Donotrecord:不记录,对于非HTML元素不记录。
注意:使用HTML-Based模式录制时,VuGen插入目标帧到web_url函数中时,VuGen会在run-time运行的浏览器中和结果报告中显示页面的正确性。
如以下代码:
web_url(“buttonhelp.gif”,
"URL=http://www.hplab.com/home?com/rstr?BV_EngineID…,
“TargetFrame=main”,
“Resource=0”,
“RecContentType=text/html”,
"Referer=http://www.hplab.com/home?..
“Snapshot=t5.inf”,
“Mode=HTML”,
LAST);
URL-basedscript方式:将每条客户端发出的请求录制成一条语句,对LoadRunner来说,在该模式下,一条语句只能建立一个到服务器的连接,并将通信过程中的很多隐藏的信息都录制出来(如session、cookie)。LoadRunner提供了web_concurrent_start()和web_concurrent_end()函数模拟URL-basedscript的工作方式。
下面以URL-basedscript方式录制一个登录的功能,录制后的代码如下:
web_url(“WebTours”,
“URL=http://127.0.0.1:1080/WebTours/”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=”,
“Snapshot=t1.inf”,
“Mode=HTTP”,
LAST);
web_concurrent_start(NULL);
web_url(“header.html”,
“URL=http://127.0.0.1:1080/WebTours/header.html”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=http://127.0.0.1:1080/WebTours/”,
“Snapshot=t2.inf”,
“Mode=HTTP”,
LAST);
web_url(“welcome.pl”,
“URL=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=true”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=http://127.0.0.1:1080/WebTours/”,
“Snapshot=t4.inf”,
“Mode=HTTP”,
LAST);
web_concurrent_end(NULL);
web_concurrent_start(NULL);
web_url(“hp_logo.png”,
“URL=http://127.0.0.1:1080/WebTours/images/hp_logo.png”,
“Resource=1”,
“RecContentType=image/png”,
“Referer=http://127.0.0.1:1080/WebTours/header.html”,
“Snapshot=t3.inf”,
LAST);
web_url(“webtours.png”,
“URL=http://127.0.0.1:1080/WebTours/images/webtours.png”,
“Resource=1”,
“RecContentType=image/png”,
“Referer=http://127.0.0.1:1080/WebTours/header.html”,
“Snapshot=t5.inf”,
LAST);
web_concurrent_end(NULL);
web_concurrent_start(NULL);
web_url(“home.html”,
“URL=http://127.0.0.1:1080/WebTours/home.html”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=true”,
“Snapshot=t6.inf”,
“Mode=HTTP”,
LAST);
web_url(“nav.pl”,
“URL=http://127.0.0.1:1080/WebTours/nav.pl?in=home”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=http://127.0.0.1:1080/WebTours/welcome.pl?signOff=true”,
“Snapshot=t7.inf”,
“Mode=HTTP”,
LAST);
web_concurrent_end(NULL);
web_url(“mer_login.gif”,
“URL=http://127.0.0.1:1080/WebTours/images/mer_login.gif”,
“Resource=1”,
“RecContentType=image/gif”,
“Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home”,
“Snapshot=t8.inf”,
LAST);
web_submit_data(“login.pl”,
“Action=http://127.0.0.1:1080/WebTours/login.pl”,
“Method=POST”,
“RecContentType=text/html”,
“Referer=http://127.0.0.1:1080/WebTours/nav.pl?in=home”,
“Snapshot=t9.inf”,
“Mode=HTTP”,
ITEMDATA,
“Name=userSession”,“Value=108749.254191981ffQDHiQpHzcfDDfzcpHDifHf”,ENDITEM,
“Name=username”,“Value=test1”,ENDITEM,
“Name=password”,“Value=1”,ENDITEM,
“Name=JSFormSubmit”,“Value=off”,ENDITEM,
“Name=login.x”,“Value=63”,ENDITEM,
“Name=login.y”,“Value=9”,ENDITEM,
LAST);
web_concurrent_start(NULL);
web_url(“nav.pl_2”,
“URL=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=http://127.0.0.1:1080/WebTours/login.pl”,
“Snapshot=t10.inf”,
“Mode=HTTP”,
LAST);
web_url(“login.pl_2”,
“URL=http://127.0.0.1:1080/WebTours/login.pl?intro=true”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=http://127.0.0.1:1080/WebTours/login.pl”,
“Snapshot=t12.inf”,
“Mode=HTTP”,
LAST);
web_concurrent_end(NULL);
web_concurrent_start(NULL);
web_url(“flights.gif”,
“URL=http://127.0.0.1:1080/WebTours/images/flights.gif”,
“Resource=1”,
“RecContentType=image/gif”,
“Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home”,
“Snapshot=t11.inf”,
LAST);
web_url(“itinerary.gif”,
“URL=http://127.0.0.1:1080/WebTours/images/itinerary.gif”,
“Resource=1”,
“RecContentType=image/gif”,
“Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home”,
“Snapshot=t13.inf”,
LAST);
web_url(“signoff.gif”,
“URL=http://127.0.0.1:1080/WebTours/images/signoff.gif”,
“Resource=1”,
“RecContentType=image/gif”,
“Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home”,
“Snapshot=t14.inf”,
LAST);
web_url(“in_home.gif”,
“URL=http://127.0.0.1:1080/WebTours/images/in_home.gif”,
“Resource=1”,
“RecContentType=image/gif”,
“Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home”,
“Snapshot=t15.inf”,
LAST);
web_concurrent_end(NULL);
return0;
选择URL-basedscript选择,单击【URLAdvanced…】,弹出【AdvancedURL】对话框。关于URL的高级设置有两种方式:“CreateconcurrentgroupsforresourcestheirsourceHTMLpage”和“Useweb_custom_requestonly”。
CreateconcurrentgroupsforresourcestheirsourceHTMLpage:将捕获所有HTML页面的资源,并将其保存在并发组中(并发组使用web_concurrent_start和web_concurrent_endstatements两个函数标识),如果不选中该选项时,HTML页面资源将会分成独立的、单独的web_url步骤,但并不放入并行组中。
如以下代码:
web_concurrent_start(NULL);

web_url(“ClickHereForAdditionalRestrictions”,
“URL=http://www.hplab.com/restrictions.html”,
“Resource=0”,
“RecContentType=text/html”,
"Referer=http://www.hplab.com/home?..
“Snapshot=t4.inf”,
“Mode=HTTP”,
LAST);
web_url(“buttonhelp.gif”,
"URL=http://www.hplab.com/home?com/rstr?BV_EngineID…,
“Resource=0”,
“RecContentType=text/html”,
"Referer=http://www.hplab.com/home?..
“Snapshot=t5.inf”,
“Mode=HTTP”,
LAST);

web_concurrent_end(NULL);
Useweb_custom_requestonly:如果录制的是非浏览器的应用程序,可以设置VuGen自定义HTTP请求,在LoadRunner中使用web_custom_reques函数来实现,不管内容如何。
如以下代码:
web_custom_request(“www.hplab.com”,
“URL=http://www.hplab.com/”,
“Method=GET”,
“Resource=0”,
“RecContentType=text/html”,
“Referer=”,
“Snapshot=t1.inf”,
“Mode=HTTP”,
LAST);
选择HTML-basedscript还是URL-basedscript,应该根据实际需要进行,下面是一些常见的参考原则:
1.基于浏览器的应用程序推荐使用HTML-basedscript。
2.不是基于浏览器的应用程序推荐使用URL-basedscript。
3.如果基于浏览器的应用程序中包含了JavaScript,并且该脚本向服务器发送了请求,比如DataGrid的分页按钮等,推荐使用URL-basedscript。
4.基于浏览器的应用程序中使用了HTTPS安全协议,建议使用URL-basedscript。如果使用HTML-basedscript模式录制后不能成功回放,可以考虑改用URL-basedscript模式来录制。因为这种情况多是由上面所列举的原因所引起的。
Advanced选项卡
Advanced选项是设置脚本回放的高级选项,如图所示。

在这里插入图片描述

Savesnapshotresourceslocally:表示运行结果中保存一个快照。
AddcommentstoscriptforHTTPerrorswhilerecording表示出现错误时会自动添加注释。
点击Headers…按钮,会弹出HTTPHeaders配置对话框,如图所示。在该对话框中可以选择需要录制的Headers,以便服务器能够正确处理编辑信息。需要注意的是Accept-Language选项,像Websphere这类服务器会根据HTTP请求中的Header来确定编码。
在这里插入图片描述

Correlation选项卡
Correlation选项卡用来对脚本中的关联属性进行设置,如图3-19所示。LoadRunner包括两种规则:一是内建规则;二是:自定义的规则;LoadRunner会默认自带一些内建规格。在录制时选中需要的关联规则,录制脚本过程中LoadRunner会自动匹配需要关联的规格,并生成关联函数。如果当前的这些关联规则无法满足录制的需求,那么可以点击【NewApplication】按钮来新建一个关联,再点击
在这里插入图片描述

【NewRule】按钮为该关联新建一个规则。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Loadrunner工具概述 4 工具简介 4 下载 4 安装 5 在windows下安装loadrunner 5 在liunx下安装Load Generator组件 6 LoadRunner主要组件 7 工作原理 7 LR自带实例 8 负载流程 9 Loadrunner引导界面 9 VuGen--组件概述 10 VuGen--脚本创建 10 脚步创建流程 10 VuGen起始页 11 协议支持选择 11 协议种类 12 协议选择方法 14 协议选择注意要点 16 开始录制(Start Recording)及选项(Recording Option) 16 Internet 应用程序录制选项 20 Win32应用程序录制选项 21 常规选项—脚本(General--Script) 21 常规选项—协议(General—Protocols) 22 常规选项—录制(General—Recording) 22 网络—端口映射(NetWork—Port Mapping) 28 HTTP属性—高级(HTTP Properties--Advanced) 29 HTTP属性—关联(HTTP Properties--Correlation) 31 VuGen--脚本录制 32 录制步骤 32 脚本查看 33 脚本Action介绍 34 VuGen--脚本回放 35 Web(HTTP/HTML)常规选项 35 参数化(Parameterization) 35 回放(Replay) 35 环境(Environment) 36 显示(Display) 37 关联(Correlation) 37 Web(HTTP/HTML)运行设置 37 运行逻辑(Run Logic) 38 步(Pacing) 39 日志(Log) 39 思考时间(Think Time) 40 其他(Miscellances) 41 VuGen--增强功能 42 事务 42 参数化 43 内容检查 49 关联 50 集合点 53 Controller组件概述 55 设计场景思想 55 新建场景方式 56 场景类型 56 场景运行 59 执行步骤 60 场景运行期间增加负载 61 系统监控 62 监控负载下的客户端程序 62 实时观察Vuser运行情况 62 Load Generator组件概述 64 Generator连接 64 IP虚拟 64 添加IP地址 65 使用虚拟IP 65 IP虚拟使用误区 66 Analysis组件概述 66 服务水平协议 68 定义SLA 69 常用性能指标介绍 71 事务(Transation) 71 Web资源分析(Web Resources) 72 网页元素细分(Web Page Breakdown) 74 其他指标 76 性能测试资源占用率分析方法 76 附录:LoadRunner常用函数 77
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值