Jmeter(三) - 从入门到精通 - 测试计划(Test Plan)的元件(详解教程)
北京-宏哥 人气:11.简介
上一篇中宏哥已经教你如何通过JMeter来创建一个测试计划(Test Plan),那么这一篇我们就将JMeter启动起来,创建一个测试计划(Test plan),然后宏哥给大家介绍一下测试计划(Test Plan)有哪些元件组成的。
2.测试计划(Test Plan)要素
本节主要描述测试计划的不同部分要素。JMeter中一个脚本就是一个测试计划(Test Plan),也是一个管理单元。JMeter 的请求模拟与并发数(设置线程数,一个线程代表一个虚拟用户)设置都在脚本文件中一起设置。JMeter 不像 LoadRunner 把脚本与虚拟用户设置分开。
2.1测试计划要素如下:
(1)要素一:脚本中测试计划只能有一个
1、Jmeter 测试计划类似 LoadRunner Controller 中的测试场景,同一时刻场景故然只能有一个,。
2、JMeter 脚本在 GUI 中显示时是树型结构,测试计划是根节点,根节点当然只能有一个。
(2)要素二:测试计划中至少要有一个线程组
1、JMeter 负裁是通过线程组驱动的,所以计划中至少要出现一个线程组。
2、JMeter 测试计划支持多个线程组。
3、我们可以在计划下面建立多个线程组,类似 LoadRunner 中的 Group 方式的场景,我们可以把JMeter 计划理解成LoadRmmer 中的 Group 方式场景,把不相关联的业务分布在不同的线程组中( LoadRunner 中的不同 Group)。
(3)要素三:至少要有一个取样器
1、测试的目的就是要模拟用户请求,没有取样脚本就毫无意义。
(4)要素四:至少要有一个监听器
1、测试结果用来衡量系统性能,我们需要从结果中分析系统性能。
3.测试计划(Test Plan)元件
打开Jmeter页面:包括测试计划+工作台。
注意:敲黑板,敲脑壳啦!!!最新版的jmeter去掉了工作台。不要大惊小怪的导出截图问,我的JMeter为什么没有工作台,我同事的有工作台,如果你是在想要就下载一个低版本的JMeter安装好启动以后,就可以看到你的JMeter也有工作台了。
3.1测试计划(Test Plan)
Test Plan (测试计划):用来描述一个性能测试,包含与本次性能测试所有相关的功能。也就说本的性能测试的所有内容是于基于一个计划的。
右键单击“测试计划”弹出菜单:
它用来描述一个测试方案,包含与本次性能测试所有相关的功能。也就说本次测试的所有内容是于基于一个计划的。
注意:敲黑板,敲脑壳啦!!!
测试计划对象具有一个名为“ 函数测试模式 ” 的复选框。如果选择,它将使JMeter记录每个样本从服务器返回的数据。如果您在测试侦听器中选择了文件,则此数据将被写入文件。如果要进行少量运行以确保正确配置JMeter并确保服务器返回预期结果,这将很有用。结果是文件将快速增长,JMeter的性能将受到影响。如果要进行压力测试,则应禁用此选项(默认情况下处于禁用状态)。
如果您没有将数据记录到文件中,则此选项没有区别。
您还可以使用监听器上的“ 配置”按钮来确定要保存的字段。
3.2线程组Threads (Users)
线程组元素是任何测试计划的起点。所有控制器和采样器必须在线程组下。其他元素(例如,侦听器)可以直接放置在测试计划下,在这种情况下,它们将应用于所有线程组。顾名思义,线程组元素控制JMeter将用于执行测试的线程数。线程组的控件使您可以:
- 设置线程数
- 设置加速时间
- 设置执行测试的次数
每个线程将完整地执行测试计划,并且完全独立于其他测试线程。多个线程用于模拟与服务器应用程序的并发连接。
加速期告诉JMeter将“加速”到所选线程的总数需要多长时间。如果使用了10个线程,并且启动周期为100秒,那么JMeter将花费100秒来启动和运行所有10个线程。每个线程将在上一个线程开始后10(100/10)秒开始。如果有30个线程,启动周期为120秒,则每个连续线程将延迟4秒。
加速需要足够长的时间来避免在测试开始时工作量过大,并且还必须足够短以使最后一个线程在第一个线程完成之前开始运行(除非有人希望这种情况发生)。
从“上升=线程数”开始,然后根据需要向上或向下调整。
默认情况下,线程组配置为在其元素之间循环一次。
线程组还提供了调度程序。单击“线程组”面板底部的复选框以启用/禁用其他字段,您可以在其中输入测试的持续时间,启动延迟,运行的开始和结束时间。您可以配置持续时间(秒)和启动延迟(秒)来控制每个线程组的持续时间以及启动后的秒数。当测试开始时,JMeter将在启动线程组的线程之前等待启动延迟(秒),然后运行配置的持续时间(秒)。请注意,这两个选项会覆盖“ 开始时间”和“ 结束时间”。
另外,您也可以使用其他两个字段Start time和End time(尽管不建议这样做,因为它不太灵活)。测试开始时,如有必要,JMeter将等待直到达到启动时间。在每个周期的末尾,JMeter会检查是否已达到结束时间,如果已结束,则运行将停止,否则,将允许测试继续进行直到达到迭代限制。
线程组的添加路径:【测试计划】-【THreads(Users)线程组】
3.2.1添加线程组
选中要添加线程组的测试计划(Test Plan),右键点击“Add”,选中“Threads(Users)”,我们目前可以看到三个线程组。
虽然有三个添加线程组的选项,名字不一样, 创建之后,其界面是完全一样的。之前的版本只有一个线程组的名字。现在多一个setUp theread Group 与terDown Thread Group
1) setup thread group
一种特殊类型的ThreadGroup的,可用于执行预测试操作。这些线程的行为完全像一个正常的线程组元件。不同的是,这些类型的线程执行测试前进行定期线程组的执行。
setUp Thread Group类似于lr的init.可用于执行预测试操作。
2) teardown thread group.
一种特殊类型的ThreadGroup的,可用于执行测试后动作。这些线程的行为完全像一个正常的线程组元件。不同的是,这些类型的线程执行测试结束后执行定期的线程组。
tearDown Thread Group类似于lr的end.可用于执行测试后动作。
3) thread group(线程组).
这个就是我们通常添加运行的线程。通俗的讲一个线程组,可以看做一个虚拟用户组,线程组中的每个线程都可以理解为一个虚拟用户。线程组中包含的线程数量在测试执行过程中是不会发生改变的。
3.2.2线程组界面介绍
这个就是我们通常添加运行的线程。通俗的讲一个线程组,,可以看做一个虚拟用户组,线程组中的每个线程都可以理解为一个虚拟用户。
Ramp-Up Period:单位是秒,默认时间是1秒。它指定了启动所有线程所花费的时间。如果你需要Jmeter立即启动所有线程,将此设定为0即可
循环次数:表示每个线程执行多少次请求。
名称:就如字面意思,起个有意义的名字就行
注释:
线程数:这里选择1
Ramp-Up Period:单位是秒,默认时间是1秒。它指定了启动所有线程所花费的时间,比如,当前的设定表示“在5秒内启动5个线程,每个线程的间隔时间为1秒”。如果你需要Jmeter立即启动所有线程,将此设定为0即可
循环次数:表示每个线程执行多少次请求。
3.3拓展
这个是关于阶梯加压线程组,后期关于这部分会详细介绍,这里先提一下,有兴趣的自己可以研究一下,很简单的需要给JMeter下载安装一个插件就可以了。
注意:Stepping Thread Group 可用于模拟阶梯加压!
3.4控制器(Controllers)
JMeter有两种类型的控制器:采样器和逻辑控制器。用这些元件来驱动测试的进行。
采样器告诉JMeter将请求发送到服务器。例如,如果您希望JMeter发送HTTP请求,则添加一个HTTP Request Sampler。您还可以通过将一个或多个配置元素添加到采样器来自定义请求。有关更多信息,请参见 采样器。
逻辑控制器使您可以自定义JMeter用于决定何时发送请求的逻辑。例如,您可以添加一个Interleave Logic Controller在两个HTTP Request Samplers之间交替。有关更多信息,请参见逻辑控制器。
3.5采样器(Samplers)
采样器也可以翻译成取样器;用来模拟用户的操作,向服务器(被测系统)发出Http请求、WebService(SOAP/XML-RPC Request)请求或者Java请求等。我们可以把Http请求元件看成是一个没有界面的浏览器,它可以发送Http请求,接收服务器的响应数据。采样器(Sampler)是测试中向服务器发送请求,记录响应信息,记录响应时间的最小单元,JMeter 原生支持多种不同的sampler 。如 HTTP Request Sampler 、 FTP Request Sampler 、TCP Request Sampler 、JDBC Request Sampler 等。高版本的jmeter支持更丰富的Sampler。
采样器的添加路径:【测试计划】-【线程组】-【采样器】。
采样器告诉JMeter将请求发送到服务器并等待响应。它们按照它们在树中出现的顺序进行处理。控制器可用于修改采样器的重复次数。
JMeter采样器包括:
FTP请求
HTTP请求(也可用于SOAP或REST Web服务)
JDBC请求
Java对象请求
JMS请求
JUnit测试请求
LDAP要求
邮件要求
操作系统进程请求
TCP请求
每个采样器都有几个可以设置的属性。您可以通过向测试计划中添加一个或多个配置元素来进一步自定义采样器。
如果要将相同类型的多个请求(例如HTTP请求)发送到同一服务器,请考虑使用默认配置元素。每个控制器都有一个或多个Defaults元素(请参见下文)。
切记在测试计划中添加一个侦听器,以查看和/或将请求结果存储到磁盘。
如果您有兴趣让JMeter对请求的响应执行基本验证,请将Assertion添加到采样器。例如,在对Web应用程序进行压力测试时,服务器可能返回成功的“ HTTP响应”代码,但是页面上可能有错误或缺少部分。您可以添加断言来检查某些HTML标记,常见错误字符串等。JMeter允许您使用正则表达式创建这些断言。
3.6逻辑控制器(Logic Controllers)
逻辑控制器使您可以自定义JMeter用于决定何时发送请求的逻辑。逻辑控制器可以更改来自其子元素的请求的顺序。他们可以自己修改请求,使JMeter重复请求,等等。
逻辑控制器器的添加路径:【测试计划】-【线程组】-【逻辑控制器】。
要了解逻辑控制器对测试计划的影响,请看一下以下测试树:
- Test Plan
- Thread Group
- Once Only Controller
- Login Request (an HTTP Request)
- Load Search Page (HTTP Sampler)
- Interleave Controller
- Search "A" (HTTP Sampler)
- Search "B" (HTTP Sampler)
- HTTP default request (Configuration Element)
- HTTP default request (Configuration Element)
- Cookie Manager (Configuration Element)
此测试的第一件事是,登录请求将仅在第一次执行。随后的迭代将跳过它。这是由于“ Once Only Controller”的作用。
登录后,下一个Sampler将加载搜索页面(我们可以想象一个测试场景:用户登录到Web应用程序,然后转到搜索页面进行搜索)。这只是一个简单的请求,不会通过任何逻辑控制器进行过滤。
加载搜索页面后,我们要进行搜索。实际上,我们要进行两种不同的搜索。但是,我们希望在每次搜索之间重新加载搜索页面本身。我们可以通过具有4个简单的HTTP请求元素(加载搜索,搜索“ A”,加载搜索,搜索“ B”)来实现。相反,我们使用“Interleave Controller”,该控制器每次通过测试都会传递一个子请求。它保持子元素的顺序(即,它不会随机传递,而是“记住”其位置)。交叉处理2个子请求可能会过多,但很容易会有8个或20个子请求。
注意HTTP请求默认值属于Interleave Controller。想象一下,“搜索A”和“搜索B”共享相同的PATH信息(HTTP请求规范包括域,端口,方法,协议,路径和参数以及其他可选项)。这很有道理-都是搜索请求,都命中了相同的后端搜索引擎(例如servlet或cgi-script)。与其在PATH字段中为两个HTTP Samplers配置相同的信息,不如将这些信息抽象到单个Configuration Element中。当Interleave Controller“传递”来自“搜索A”或“搜索B”的请求时,它将使用HTTP default request配置元件中的值填充空白。因此,对于这些请求,我们将PATH字段留空,并将该信息放入配置元素。在这种情况下,这充其量是次要的好处,但可以证明其功能。
树中的下一个元素是另一个HTTP default request,这次已添加到线程组本身。线程组具有内置的逻辑控制器,因此,它完全如上所述使用此配置元件。它填补了所有通过的请求的空白。因此在Web测试中,将所有HTTP Sampler元件中的DOMAIN字段保留为空白,然后将该信息放入HTTP默认请求元素(添加到线程组中)非常有用。这样,您只需更改测试计划中的一个字段即可在另一台服务器上测试应用程序。否则,您将必须编辑每个Sampler。
最后一个元件是HTTP Cookie Manager。Cookie Manager应添加到所有Web测试中-否则JMeter将忽略cookie。通过在线程组级别添加它,我们确保所有HTTP请求将共享相同的cookie。
逻辑控制器可以组合使用以获得各种结果。请参阅内置逻辑控制器列表。
3.7测试片段(Test Fragments)
测试片段元素是一种特殊类型的控制器,它与线程组元素位于同一级别的测试计划树上。它与线程组的区别在于,除非被模块控制器或Include_Controller引用,否则它不会执行。
此元件仅用于测试计划中的代码重用。它是一个辅助的组件,在此节点下几乎可以放置任何JMeter测试元件,但它一般不会被运行,那么它的作用到底是什么了?
(1)在脚本开发的过程中,可以用来备份元件。
(2)可以被模块控制台调用,我们可以用它模块化请求(是不是有点似曾相识的感觉了,没错就是程序开发中的,将业务封装成一个方法供复用)供模块化控制器调用
3.8监听器(Listeners)
监听器提供对JMeter运行时JMeter收集的有关测试用例的信息的访问。图形结果听者曲线在曲线图上的响应时间。“查看结果树”侦听器显示采样器请求和响应的详细信息,并可以显示响应的基本HTML和XML表示形式。其他侦听器提供摘要或聚合信息。
此外,监听器可以将数据定向到文件以供以后使用。JMeter中的每个监听器都提供一个字段来指示要将数据存储到的文件。还有一个“配置”按钮,可用于选择要保存的字段以及使用CSV还是XML格式。
请注意,所有监听器都保存相同的数据。唯一的区别在于数据在屏幕上的显示方式。
可以在测试中的任何位置(包括直接在测试计划下)添加监听器。他们将仅从其级别或以下级别的元素收集数据。
JMeter附带了多个监听器。JMeter的测试结果需要添加监听器来收集。
监听器的添加路径:【测试计划】-【监听器】
3.8.1监听器的任务
(1)添加监听结果,并且可以保存测试结果到文件中,这些测试结果可以供再次分析使用。
(2)展示结果,JMeter可以以表格以及图形的形式展示测试结果,方便测试人员分析测试结果。我们在开发测试脚本的时候,不可避免需要调试,监听器也提供了辅助(例如:我们查看结果树,我们在其中可以看到请求与响应的数据)。
3.9定时器(Timer)
默认情况下,JMeter线程按顺序执行采样器而不会暂停。我们建议您通过将可用计时器之一添加到线程组来指定延迟。如果不添加延迟,JMeter可能会在很短的时间内发出太多请求,从而使服务器不堪重负。这就是我们通常说的负载,为了足够真实的模拟用户负载,我们有时候会需要模拟这些请求在同一时刻发送,就好像把大家集合在同一起跑线上,然后扣动发令枪的扳机,同时向终点(被测试系统)冲去。
计时器将导致JMeter 在其范围内的每个采样器之前延迟一定的时间。
如果您选择在一个线程组中添加多个计时器,JMeter将使用计时器的总和,并在执行该计时器所适用的采样器之前暂停该时间。可以将计时器作为采样器或控制器的子级添加,以限制将它们应用到的采样器。
要在测试计划中的单个位置提供暂停,可以使用Flow Control Action Sampler。
定时器的添加路径:【测试计划】-【线程组】-【定时器】。
3.10断言
说到断言对于我们一个测试来说应该很熟悉了吧。断言用来验证结果是否正确,说白了就是用一个预设的结果(期望值、表达式、时间长短等条件)与实际结果匹配,匹配到成功,反之失败。断言使您可以断言有关从被测试服务器收到的响应的事实。使用断言,您基本上可以“测试”您的应用程序正在返回期望的结果。
例如,您可以断言对查询的响应将包含一些特定的文本。您指定的文本可以是Perl样式的正则表达式,并且可以指示响应包含文本,或者应与整个响应匹配。
您可以将断言添加到任何采样器。例如,您可以将断言添加到HTTP请求中以检查文本“ </ HTML> ”。然后,JMeter将检查该文本是否出现在HTTP响应中。如果JMeter找不到文本,则它将标记为失败的请求。
请注意,断言适用于其范围内的所有采样器。要将声明限制为单个采样器,请将该声明添加为采样器的子代。
要查看断言结果,请将“断言侦听器”添加到线程组。失败的断言还将显示在树视图和表侦听器中,并将计入错误百分比,例如在“汇总”和“摘要”报告中。
3.11配置元件
性能测试中为了模拟大量的用户操作系统,我们往往需要做参数化,JMeter的参数化可以通过配置元件来完成。例如:CSV Data Set Config,它可以帮助我们从文件中读取测试数据。另外JMeter也提供了众多函数(通过函数助手可以查看到,后续宏哥会讲到,这里只是简单的提一下)来帮助我们动态的生成数据。当然了配置元件的作用不仅于此,它还可以记录服务器的返回数据,例如:Http Cache Manager,自动记录服务器返回的Cache信息,简而言之就是它为取样器提供预备数据,然后由取样器发出请求。配置元素与采样器紧密配合。尽管它不发送请求(HTTP(S)测试脚本记录器除外),但是它可以添加或修改请求。
配置元素只能从放置该元素的树枝内部访问。例如,如果您将HTTP Cookie Manager放置在简单逻辑控制器中,则您放置在Simple Logic Controller中的HTTP请求控制器将只能访问Cookie Manager(请参见图1)。Cookie管理器可用于HTTP请求“网页1”和“网页2”,但不能访问“网页3”。
而且,树枝内部的配置元素比“父”分支中的相同元素具有更高的优先级。例如,我们定义了两个HTTP请求默认值元素:“ Web默认值1”和“ Web默认值2”。由于我们在循环控制器内放置了“ Web Defaults 1”,因此只有“ Web Page 2”可以访问它。其他HTTP请求将使用“ Web默认值2”,因为我们将其放置在线程组(所有其他分支的“父级”)中。
图1-显示配置元素可访问性的测试计划
在用户定义的变量配置元素是不同的。无论在何处放置,都将在测试开始时对其进行处理。为简单起见,建议将元素仅放置在线程组的开始处。
配置元件的添加路径:【测试计划】-【配置元件】。
3.12前置处理器
预处理器在发出“采样器请求”之前执行一些操作。如果将预处理器附加到Sampler元素,则它将在该Sampler元素运行之前执行。预处理器最常用于在样品请求运行前修改其设置,或更新未从响应文本中提取的变量。有关执行预处理器的更多详细信息,请参见作用域规则。这块宏哥举一个使用这个元件的测试场景:在测试脚本的开发过程中,我们在请求发送之前可能会做一些环境或者参数的准备工作,那么我们可以在前置处理器中来完成这些工作。例如:我们对数据库进行操作前需要建立一个数据库连接,那么前置处理器就可以完成这个功能。
前置处理器的添加路径:【测试计划】-【前置处理器】。
3.13后置处理器
后置处理器一般放在取样器之后,用来处理服务器的返回结果,例如:一个Web应用程序,我们登录之后会返回一个SessionID,这个SessionID在登录之后的业务操作过程中会作为验证条件,验证用户是否合法登录了之后才进行的业务操作。发出采样器请求后,后处理器将执行某些操作。如果将后处理器附加到Sampler元素,则它将在该Sampler元素运行之后立即执行。后处理器最常用于处理响应数据,经常从中提取值。有关执行后处理器的更多详细信息,请参见作用域规则。
到此,我们已经简单了解了jmeter的基本组成原件,我们后序的测试工作也就是使用这些元件来完成测试任务。
3.14执行顺序
- 配置元素
- 预处理器
- 计时器
- 取样器
- 后处理器(除非SampleResult为null)
- 断言(除非SampleResult为null)
- 监听器(除非SampleResult为null)
例如,在以下测试计划中:
- 控制器
- 后处理器1
- 采样器1
- 采样器2
- 计时器1
- 断言1
- 预处理器1
- 计时器2
- 后处理器2
执行顺序为:
预处理器1 计时器1 计时器2 采样器1 后处理器1 后处理器2 断言1 预处理器1 计时器1 计时器2 采样器2 后处理器1 后处理器2 断言1
3.15范围鉴定规则
JMeter测试树包含分层和有序的元素。测试树中的某些元素严格地是分层的(侦听器,配置元素,后处理器,预处理器,断言,计时器),而有些则主要是有序的(控制器,采样器)。创建测试计划时,您将创建样本请求的有序列表(通过Samplers),该列表表示要执行的一组步骤。这些请求通常在也已排序的控制器中组织。给定以下测试树:
示例测试树
请求的顺序将为一,二,三,四。
某些控制器会影响其子元素的顺序,您可以在组件参考中阅读有关这些特定控制器的信息。
其他元素是分层的。例如,断言在测试树中是分层的。如果其父项是一个请求,则将其应用于该请求。如果其父级是Controller,则它将影响该Controller的所有后代请求。在以下测试树中:
层次结构示例
断言1仅适用于请求1,而断言2仅适用于请求2和3。
另一个示例,这次使用Timers:
复杂的例子
在此示例中,对请求进行命名以反映其执行顺序。计时器#1将应用于请求2、3和4(请注意顺序与分层元素无关)。断言1仅适用于请求三。计时器2将影响所有请求。
希望这些示例可以清楚说明如何应用配置(分层)元素。如果您想象每个请求都在树枝上传递给它的父级,然后传递给它的父级的父级,等等,并且每次收集该父级的所有配置元素,那么您将了解它是如何工作的。
3.16属性和变量
JMeter 属性在jmeter.properties中定义(有关更多详细信息,请参见入门-配置JMeter)。
属性对于jmeter是全局的,并且主要用于定义JMeter使用的某些默认值。例如,属性remote_hosts定义JMeter将尝试远程运行的服务器。可以在测试计划中引用属性-请参阅功能-读取属性 -但不能用于特定于线程的值。
JMeter 变量是每个线程局部的。每个线程的值可以相同,也可以不同。
如果某个变量由线程更新,则仅更改该变量的线程副本。例如,正则表达式提取器后处理器将根据其线程读取的样本设置其变量,这些变量稍后可在同一线程中使用。有关如何引用变量和函数的详细信息,请参见函数和变量
请注意,在启动时,将使 “ 测试计划” 和“ 用户定义的变量”配置元素定义的值可用于整个测试计划。如果同一变量由多个UDV元素定义,则最后一个变量生效。线程启动后,会将初始变量集复制到每个线程。其他元素(例如 用户参数预处理器或正则表达式提取器后处理器)可用于重新定义相同的变量(或创建新变量)。这些重新定义仅适用于当前线程。
所述的setProperty函数可以用来定义JMeter的属性。这些对于测试计划是全局的,因此可以用于在线程之间传递信息-如果需要的话。
3.17使用变量对测试参数化
变量不必更改-可以定义一次,并且如果单独保留,则不会更改值。因此,您可以将它们用作测试计划中经常出现的表达式的简写形式。或对于在运行期间保持恒定但在运行之间可能有所不同的项目。例如,主机名或线程组中的线程数。
在决定如何构建测试计划时,请记下哪些项目对于运行是恒定的,但在运行之间可能会改变。为此确定一些变量名称-也许使用命名约定,例如以C_或K_前缀,或仅使用大写字母将它们与测试期间需要更改的变量区分开。还应考虑哪些项需要在线程本地进行,例如使用正则表达式后处理程序提取的计数器或值。您可能希望对它们使用不同的命名约定。
例如,您可以在测试计划中定义以下内容:
主机www.example.com 底线10 圈数20
您可以在测试计划中将它们称为$ {HOST} $ {THREADS}等。如果以后要更改主机,只需更改HOST变量的值即可。这对于少量的测试工作正常,但是在测试许多不同的组合时变得乏味。一种解决方案是使用属性来定义变量的值,例如:
主机$ {__ P(host,www.example.com)} 螺纹$ {__ P(threads,10)} 循环$ {__ P(loops,20)}
然后,您可以在命令行上更改某些或所有值,如下所示:
jmeter…-Jhost = www3.example.org -Jloops = 13
4.小结
好了,今天有关测试计划(Test Plan)的元件就分享到这里,后边后对这些元件进行详细的介绍和说明,以及会涉及到部分元件的实际应用。灰常感谢您阅读到这里,如果您觉得不错,就帮忙点个推荐呗。
您的肯定就是我进步的动力。如果你感觉还不错,就请鼓励一下吧!记得随手点波 推荐 不要忘记哦!!!
别忘了点 推荐 留下您来过的痕迹
加载全部内容