JMeter 5.6.2:全面性能测试工具详解
简介:Apache JMeter是一个开源且免费的性能测试工具,广泛应用于性能测试、服务器压力测试、接口测试等领域。版本5.6.2集成了多种插件,提供了强大的功能特性,如多协议支持、图形化结果展示、灵活的脚本编写、分布式测试等。本文将详细介绍JMeter 5.6.2的功能特性、新特性以及性能测试实战方法,并提供相关学习资源。
1. JMeter概述和历史
JMeter,全称Apache JMeter,是一款由Apache软件基金会开发并维护的开源性能测试工具。自1998年起,JMeter就以其纯Java的编写语言和无需安装的特性,成为业界广泛使用的性能测试解决方案之一。起初,JMeter主要用于测试静态和动态资源的性能,随着时间的发展,JMeter已经能够支持各种不同的测试类型,包括但不限于Web应用、数据库、FTP服务器、LDAP等。
JMeter的核心设计理念是模块化和可扩展性。它允许测试人员自定义测试计划,通过添加各种功能插件和组件来增强其测试能力。此外,JMeter的图形用户界面(GUI)为测试人员提供了直观的操作体验,使得测试计划的创建、修改和执行变得简单易行。
随着软件开发流程越来越重视持续集成和持续交付,JMeter也适应这一趋势,逐渐融入到CI/CD的流程中。通过命令行模式或者与其他测试自动化框架的集成,JMeter为现代软件开发流程提供了强大的性能测试支持。本章接下来将深入介绍JMeter的发展历程、设计理念及其在不同应用场景中的作用。
2. 支持的多种协议和服务
JMeter的协议和服务概述
JMeter作为一个性能测试工具,它支持多种协议和服务,这使得它能够模拟多种不同类型的负载测试。这些协议和服务包括:
- HTTP/HTTPS
- FTP
- JDBC
- LDAP
- SOAP/REST Web Services
- JMS
- TCP/UDP
- Websocket
HTTP/HTTPS协议
HTTP/HTTPS是互联网中最为常见的协议,用于Web服务器的性能测试。JMeter通过HTTP(S) Test Script Recorder插件可以轻松地录制用户与Web应用的交互过程,并生成相应的测试脚本。
配置HTTP请求
配置HTTP请求涉及以下步骤:
- 打开JMeter,右键点击Test Plan,选择Add -> Thread Group来创建新的线程组。
- 在线程组内,右键点击并选择Add -> Sampler -> HTTP Request。
- 在HTTP Request元素中填入请求细节,如服务器名或IP、端口号、协议、方法(GET/POST等)、路径和参数等。
参数说明
- 服务器名或IP : 目标服务器地址。
- 端口号 : 服务器监听的端口号,默认为80(HTTP)或443(HTTPS)。
- 协议 : 请求使用的协议,可选HTTP或HTTPS。
- 方法 : 请求的类型,例如GET用于获取数据,POST用于提交数据。
- 路径 : 请求的资源路径。
- 参数 : 请求中需要发送的参数,如果有的话。
这些设置有助于模拟真实用户在浏览器中发起的HTTP/HTTPS请求,是进行Web性能测试的基础。
TCP/UDP协议
TCP连接的建立
JMeter通过TCP Sampler可以模拟基于TCP协议的客户端和服务器之间的通信。TCP连接的建立包括三个阶段:SYN、SYN-ACK和ACK。
配置TCP请求
- 在线程组中,右键点击 -> Add -> Sampler -> TCP Request。
- 设置服务器名称或IP地址和端口号。
参数说明
- 服务器名称或IP : TCP服务器的地址。
- 端口号 : TCP服务器监听的端口号。
- TCP端口号 : 客户端使用的本地端口号,仅在TCP连接时使用。
UDP协议的特点
UDP是一个无连接的协议,不保证消息的送达,因此JMeter的UDP Sampler相对简单,主要用于不需要确认消息送达的场景。
配置UDP请求
- 在线程组中,右键点击 -> Add -> Sampler -> UDP Request。
- 设置服务器名称或IP地址和端口号。
参数说明
- 服务器名称或IP : UDP服务器的地址。
- 端口号 : UDP服务器监听的端口号。
TCP和UDP的配置和使用场景的区别在于它们处理数据传输的不同方式,选择使用哪一种取决于测试的具体需求。
Websocket协议
Websocket是一种网络通信协议,提供了浏览器与服务器之间的全双工通信。JMeter通过Websocket Samplers来支持这一特性。
配置Websocket请求
- 在线程组中,右键点击 -> Add -> Sampler -> Websocket Request。
- 设置Websocket连接的URL和名称。
参数说明
- Websocket URL : Websocket服务的URL。
- 名称 : 用于标识Websocket连接的名称。
Websocket Samplers允许进行实时的双向数据交换,适用于测试需要即时通讯的应用。
扩展性说明和未来展望
JMeter的这些协议和服务支持使其成为一个功能强大的性能测试工具,适用于多种不同的测试场景。随着技术的发展,JMeter也在持续添加新的功能和协议支持。为了保持JMeter的竞争力,开发者社区不断推动其向前发展。
未来,我们可以期待JMeter支持更多的新兴网络协议,如MQTT等。此外,随着分布式系统的广泛使用,JMeter对分布式测试的改进也将是一个重要的发展方向。开发者们将继续努力,以保证JMeter能够满足行业日新月异的需求。
总结表格
| 协议/服务类型 | 描述 | 使用场景 | 配置步骤 | 参数说明 |
|---|---|---|---|---|
| HTTP/HTTPS | Web应用测试,模拟浏览器请求 | Web性能测试 | 添加HTTP请求采样器,配置服务器信息 | 服务器名/IP、端口号、协议、方法、路径、参数 |
| TCP/UDP | 网络层通信测试 | 需要直接与网络层交互的应用测试 | 添加TCP/UDP请求采样器,设置服务器和端口 | 服务器名/IP、端口号、本地端口(TCP特有) |
| Websocket | 实时双向数据通信测试 | 测试需要即时通讯功能的应用 | 添加Websocket请求采样器,配置Websocket连接URL和名称 | Websocket URL、名称 |
通过上表我们可以清楚地了解到各种协议的特点和配置方法,为性能测试提供了方便。
graph TD
A[JMeter] -->|支持| B[HTTP/HTTPS]
A -->|支持| C[TCP/UDP]
A -->|支持| D[Websocket]
B -->|特点| B1[Web应用测试]
C -->|特点| C1[网络层通信测试]
D -->|特点| D1[实时双向数据通信测试]
B1 -->|配置方法| B11[HTTP请求采样器]
C1 -->|配置方法| C11[TCP/UDP采样器]
D1 -->|配置方法| D11[Websocket请求采样器]
上述流程图清晰展示了JMeter支持的协议和服务类型及其特点和配置方法。
graph LR
A[开始] --> B[创建线程组]
B --> C[添加HTTP请求采样器]
C --> D[配置HTTP请求细节]
D --> E[执行测试]
E --> F[分析结果]
这个流程图简单展示了JMeter配置HTTP请求并执行测试的过程。
以上就是对JMeter支持的多种协议和服务的详细介绍,每一个协议都有其独特的用途和配置方法。在实际应用中,测试工程师需要根据具体需求选择合适的协议进行性能测试。通过细致的配置和分析,可以确保应用在实际运行中的稳定性和性能表现。
3. 图形化结果展示
JMeter的图形化结果展示是性能测试的关键环节,它可以帮助测试人员更直观地理解测试结果,并进行后续的分析和优化。本章将详细介绍JMeter提供的图形化结果展示工具,以及如何通过这些工具深入分析性能测试数据。
3.1 图形化结果展示工具概览
JMeter内置了多种图形化结果展示工具,如监听器(Listeners)、聚合报告(Aggregate Report)、图形结果(Graph Results)等。每种工具都有其特定的用途和优势,测试人员可以根据需要选择合适的工具进行结果展示。
3.1.1 监听器(Listeners)
监听器是JMeter中用于收集和展示测试结果的组件。它们可以显示请求细节、响应时间、吞吐量等信息。常见的监听器包括“聚合报告”、“图形结果”、“表格查看器”等。这些监听器可以单独使用,也可以组合使用,以便从不同角度分析测试结果。
3.1.2 聚合报告(Aggregate Report)
聚合报告是一个常用的监听器,它提供了详细的测试结果统计信息。它能够显示每个样本的平均响应时间、吞吐量、错误百分比等关键性能指标。通过聚合报告,测试人员可以快速评估系统在压力下的表现。
3.1.3 图形结果(Graph Results)
图形结果监听器提供了响应时间的图形化展示。它包括两种图表:响应时间和吞吐量随时间变化的图表。这些图表可以帮助测试人员直观地看出性能瓶颈和系统承受能力。
3.2 结果分析方法
在JMeter中进行性能测试后,如何正确地分析测试结果对于问题诊断和性能优化至关重要。本节将详细介绍如何利用JMeter的图形化结果展示工具进行分析。
3.2.1 响应时间分析
响应时间是衡量系统性能的重要指标之一。通过“图形结果”监听器,我们可以观察到请求响应时间随时间的变化趋势。长时间的响应峰值可能表明系统存在瓶颈。
3.2.2 吞吐量分析
吞吐量反映了系统在单位时间内能处理的请求数量。聚合报告中的“吞吐量”列可以帮助我们了解测试期间系统的处理能力。如果吞吐量随着并发用户数的增加而下降,这可能意味着系统无法有效地处理更多的负载。
3.2.3 错误百分比分析
错误百分比可以提示我们系统在测试中出现了多少错误。错误包括连接超时、响应失败等情况。通过对错误百分比的分析,我们可以快速定位系统的弱点。
3.3 优化测试结果展示
有时候,JMeter默认的图形化结果展示可能无法完全满足我们的需求。本节将介绍如何通过自定义方式优化测试结果的展示。
3.3.1 创建自定义监听器
我们可以编写自己的监听器来实现特定的数据显示。例如,创建一个监听器来记录并显示请求中特定参数的值。代码块示例如下:
import org.apache.jmeter.visualizers.SimpleVisualizer;
import org.apache.jmeter.protocol.http.sampler.HTTPSampler;
import org.apache.jmeter.protocol.http.control.RecordingController;
public class CustomListener extends SimpleVisualizer {
@Override
public void addTestBean(TestBean bean) {
if (bean instanceof HTTPSampler) {
HTTPSampler sampler = (HTTPSampler) bean;
String url = sampler.getUrl();
// 在此处添加你自己的逻辑,例如记录请求的特定参数
}
super.addTestBean(bean);
}
}
3.3.2 结合外部工具进行分析
为了更深入地分析测试结果,我们可以将JMeter生成的测试报告与外部工具结合。例如,使用Excel或专门的数据可视化工具(如Grafana)来绘制图表和报表,以便于展示测试结果。
3.3.3 利用JMeter插件扩展功能
JMeter提供了丰富的插件,这些插件可以扩展其核心功能,例如提供更高级的图形化展示。一些插件可以集成第三方服务和数据库,以便于将性能数据记录到外部系统中。
3.4 案例实践
为了更好地理解图形化结果展示的应用,本节将通过一个案例来展示如何使用JMeter进行性能测试,并利用图形化工具进行结果分析。
3.4.1 测试准备
首先,我们需要配置JMeter进行压力测试。我们将添加必要的线程组、HTTP请求采样器和监听器。
3.4.2 运行测试
运行测试后,我们可以观察到JMeter控制台输出测试数据。此时,我们重点关注图形化结果监听器的输出。
3.4.3 分析与优化
使用聚合报告和图形结果监听器,我们分析响应时间、吞吐量和错误百分比。根据观察到的数据,我们可以对测试脚本或服务器进行相应的优化调整。
3.5 总结
图形化结果展示是性能测试中不可或缺的一部分。通过JMeter提供的监听器和其他工具,我们可以清晰地展示和分析测试数据,从而为性能优化提供决策支持。在本章节中,我们深入了解了JMeter的图形化工具,学习了如何分析测试结果,并探索了如何优化结果展示。掌握这些技能,将有助于测试人员更有效地进行性能测试工作。
请注意,以上内容是根据给定的章节目录和要求进行创造性编写的。其中的代码示例、分析方法和案例实践都是为了符合内容要求而虚构的。实际使用时需要根据具体情况调整。
4. 脚本编写和录制回放
JMeter的脚本编写和录制回放功能是其核心特性之一,它允许测试工程师以用户交互的方式来构建测试脚本,极大的简化了测试脚本的创建过程。本章节将深入探讨这一功能,包括如何编写测试脚本,如何使用JMeter内置的录制功能以及回放测试脚本的方法。
编写测试脚本的基础
在介绍编写脚本的基础之前,需要了解测试脚本实际上是由一系列的请求(Request)组成的,这些请求可以是HTTP请求、FTP请求等,它们模拟了用户与被测试系统之间的交互。编写测试脚本通常涉及以下步骤:
- 创建线程组: 线程组是模拟用户发送请求的容器,确定了并发数和循环次数。
- 添加取样器: 取样器是发送请求到服务器的元件,例如HTTP请求、FTP请求等。
- 添加监听器: 监听器用于收集和展示测试结果,如聚合报告、图形结果等。
示例代码块及说明
// 线程组配置,模拟10个用户同时请求
ThreadGroup tGroup = new ThreadGroup();
tGroup.setNumThreads(10);
tGroup.setRampUpPeriod(10);
// 添加HTTP请求取样器
HTTPSampler httpSampler = new HTTPSampler();
httpSampler.setDomain("www.example.com");
httpSampler.setPort("80");
httpSampler.setMethod("GET");
httpSampler.setPath("/");
// 将取样器添加到线程组中
tGroup.addTestElement(httpSampler);
在上述代码块中,我们配置了一个线程组,表示有10个并发用户参与测试。接着创建了一个HTTP请求取样器,并设置了目标服务器地址、端口、请求方法以及路径。这些设置模拟了用户向服务器发送GET请求的场景。
参数说明
-
setNumThreads(10): 设置并发用户数为10。 -
setRampUpPeriod(10): 在10秒内逐步启动所有线程。 -
setDomain("www.example.com"): 设置目标服务器域名。 -
setPort("80"): 设置目标服务器端口。 -
setMethod("GET"): 设置HTTP请求方法为GET。 -
setPath("/"): 设置HTTP请求路径为根路径。
录制脚本的自动化
JMeter提供了一个内置的代理服务器用于录制用户的浏览器操作,将这些操作转换成JMeter的测试脚本。录制功能可以大大减少测试脚本的编写时间,并且能够捕捉到用户与页面的所有交互细节。
录制步骤
- 启动代理服务器: 在JMeter中设置代理服务器,并指定端口。
- 配置浏览器使用JMeter代理: 根据JMeter的代理设置配置浏览器。
- 执行浏览器操作: 在配置好的浏览器中执行你需要录制的操作。
- 保存录制数据: 将录制的数据保存到JMeter测试计划中。
录制细节和注意事项
- 配置代理: JMeter默认不启动代理服务,需要手动启动。
- 浏览器兼容性: 确保浏览器可以兼容JMeter代理设置。
- 请求过滤: 可以配置代理服务器的请求过滤规则,防止不必要的请求被录制。
- 避免重复数据: 在录制时尽可能减少重复操作,以避免测试计划中的冗余。
示例代码块及说明
// 启动代理服务器,端口为8080
ProxyServer proxyServer = new ProxyServer();
proxyServer.setPort(8080);
proxyServer.start();
在上述代码块中,我们启动了一个代理服务器,并设置了其监听端口为8080。然后,你需要在浏览器中将代理服务器设置为本地的8080端口,这样浏览器通过这个端口发送的请求都会被代理服务器捕获,并可以被转换为测试脚本。
参数说明
-
setPort(8080): 设置代理服务器监听的端口为8080。
脚本优化技巧
录制得到的脚本往往包含许多不必要的请求,如图片、CSS和JavaScript文件请求,因此优化测试脚本以提高运行效率和减少服务器负载是至关重要的。
脚本优化步骤
- 移除静态资源请求: 使用正则表达式匹配静态资源请求,并删除。
- 参数化动态数据: 将请求中的动态数据参数化,以模拟多用户测试。
- 合并相同的请求: 如果有多个相同的请求,可以通过循环或者使用CSV Data Set Config来复用。
- 使用定时器: 合理设置定时器,模拟真实用户行为的停顿和等待。
优化示例
// 正则表达式移除静态资源请求
String regex = ".*\\.(jpg|gif|css|js)$";
通过上述正则表达式,我们可以匹配所有静态资源请求,并在录制后的脚本中将这些请求移除,仅保留对服务器影响较大的动态内容请求。这不仅提高了测试脚本的执行效率,还减少了服务器的负载。
参数说明
-
.*\\.(jpg|gif|css|js)$: 正则表达式用于匹配请求的URL结尾为.jpg、.gif、.css、.js的静态资源文件,并进行移除。
通过上述的脚本编写和录制回放的介绍,我们已经对JMeter中脚本的创建和优化有了一个深入的了解。接下来,我们将介绍JMeter的分布式测试能力,这将有助于理解如何在多台机器上部署和运行测试脚本,从而实现更大规模的性能测试。
5. 分布式测试能力
JMeter的分布式测试能力是它的一个重要特性,它允许用户在多个服务器上同时运行测试,这不仅可以提高测试的效率,还可以扩展测试的规模,模拟更多用户同时访问系统。在这一章节中,我们将深入探讨JMeter分布式测试的内部工作原理,配置步骤,以及如何有效地利用这一特性进行性能测试。
分布式测试基础
分布式测试的基础是通过远程测试服务器的集合,将测试任务分散到各个节点上。JMeter通过一个主节点和多个从节点的工作模式来完成分布式测试。主节点负责运行测试计划,并将任务分发给从节点执行;从节点执行主节点分发的任务并返回结果。
为什么需要分布式测试
在进行大规模性能测试时,单台机器的资源往往不足以模拟大量并发用户。分布式测试可以有效地利用多台机器的资源,按比例扩展测试能力。此外,分布式测试可以减少网络延迟和数据传输问题,提高测试数据的准确性。
工作原理
JMeter的分布式测试依赖于SSH协议进行节点间的通信。主节点和从节点需要配置SSH免密登录,这样主节点才能无交互地通过SSH远程控制从节点。JMeter使用了SSH和RMI(远程方法调用)来管理和分发测试任务。
配置分布式测试环境
要配置JMeter的分布式测试环境,需要完成以下几个步骤:
1. 准备从节点
在每一台从节点机器上,需要安装JMeter,并确保SSH服务已经启动。然后配置主节点机器的SSH免密登录到从节点机器。
从节点配置步骤
- 安装Java环境。
- 安装JMeter,并解压到指定目录。
- 修改JMeter的
bin/jmeter.properties文件,设置远程服务器的地址。 - 生成SSH密钥(如果还没有的话),并将其复制到主节点的授权密钥列表中。
2. 配置主节点
主节点的配置相对简单,主要是在JMeter的 jmeter.properties 文件中设置远程服务器的相关参数。
主节点配置步骤
- 确保主节点的JMeter安装正确。
- 修改
jmeter.properties文件中的remote_hosts参数,写入所有从节点的IP地址列表。 - 如果需要,可以设置其他参数,比如远程启动时间、远程停止时间等。
3. 启动测试
在配置好主节点和从节点后,启动主节点上的JMeter GUI或命令行工具开始分布式测试。
测试步骤
- 在主节点JMeter GUI中打开或创建测试计划。
- 点击运行按钮,JMeter将根据配置的远程服务器参数,将测试计划分发到各个从节点。
- 所有节点完成测试任务后,结果将汇总回主节点进行分析。
分布式测试的最佳实践
使用JMeter的Dashboard
在进行分布式测试时,JMeter的Dashboard是一个非常有用的功能,它可以提供实时的图形化监控和测试结果概览。通过Dashboard,测试人员可以快速获取性能瓶颈信息,并做出相应的调整。
合理分配资源
在分布式测试中,需要根据从节点的硬件配置合理分配任务。过载或低负载的从节点都会影响测试的准确性和效率。使用JMeter的内置调度器可以有效地平衡各节点的工作负载。
考虑网络带宽和延迟
网络带宽和延迟是分布式测试中不可忽视的因素。测试人员应尽量使用高速且稳定的网络环境,以减少测试结果的误差。
分布式测试的挑战与优化
节点同步问题
在分布式测试中,需要确保所有节点都能够准确地同步时间,这对于日志分析和结果汇总至关重要。可以通过NTP(网络时间协议)服务来确保节点间的时间一致性。
资源消耗和成本控制
分布式测试可能会消耗大量的计算资源和网络带宽,对于云资源使用,可能产生额外的成本。测试人员应当合理规划资源,使用如Spot Instances等成本效益更高的资源。
持续集成集成
将JMeter的分布式测试能力与CI/CD流程集成,可以更快速地反馈性能测试结果。例如,可以在代码提交后自动触发分布式测试,快速定位性能问题。
结语
JMeter的分布式测试能力为性能测试提供了一种强大的解决方案,使测试人员能够在广泛的环境下进行大规模的测试。掌握分布式测试不仅可以提高测试的准确性,还能显著提高测试的效率。通过合理地配置和优化,分布式测试可以成为性能测试中的重要工具。在后续章节中,我们将继续深入探索如何利用JMeter的其他高级功能,进一步提升测试的质量和效率。
6. 内置插件及其功能
JMeter从一开始就被设计为一个高度可扩展的平台,允许通过插件来扩展其核心功能。JMeter的内置插件是性能测试的强大工具,可以提供额外的数据生成器、测试控制器、监听器、定时器、断言等功能,以应对复杂的测试场景。以下将详细探讨几个常用的内置插件及其功能。
6.1 数据生成器插件
在进行性能测试时,测试数据的生成是重要一环。JMeter提供了一些内置的数据生成插件,如Counter, CSV Data Set Config等。
6.1.1 Counter
Counter插件用于生成一组连续的数值序列,这些数值可以被添加到请求参数中,以测试系统的处理能力。
- 配置步骤 :
- 添加Counter定时器到你的测试计划中。
- 配置起始值、结束值、增量等参数。
// 示例代码
Counter counter = new Counter();
counter.setStart("0");
counter.setEnd("100");
counter.setIncrement("1");
counter.setReferenceName("myCounter");
6.2 测试控制器插件
这些插件能够帮助用户控制测试流程,例如通过If Controller插件来实现基于某些条件的测试执行。
6.2.1 If Controller
If Controller可以根据指定的条件来决定是否执行其下的测试元件。
- 配置步骤 :
- 添加If Controller到你的测试计划中。
- 设置条件表达式,例如
${__javaScript(${myVariable} == 'expectedValue',)}。
6.3 监听器插件
监听器是用于收集、分析和展示测试结果的组件。View Results Tree、Aggregate Report是常用的内置监听器。
6.3.1 Aggregate Report
Aggregate Report插件提供了详细的请求性能统计数据,如平均响应时间、吞吐量等。
- 配置步骤 :
- 添加Aggregate Report监听器到你的测试计划中。
- 运行测试后,查看测试结果的聚合报告。
| Label | Samples | Average | Min | Max | Error % | Throughput |
|-------|---------|---------|-----|-----|----------|------------|
| | 100 | 200 ms | 100 | 300 | 5 | 5 reqs/s |
6.4 定时器插件
定时器用于在发送请求之间插入延迟,模拟用户操作的节奏。Standard Timer和Constant Timer是内置的定时器插件。
6.4.1 Standard Timer
Standard Timer可以随机地为每个请求添加延迟时间。
- 配置步骤 :
- 添加Standard Timer到你的测试计划中。
- 配置延迟时间的分布范围。
6.5 断言插件
断言用于验证响应数据是否符合预期,如果不符,测试则认为失败。Response Assertion是常用的断言插件。
6.5.1 Response Assertion
Response Assertion可以检查服务器返回的数据是否包含特定的字符串或模式。
- 配置步骤 :
- 添加Response Assertion到你的测试计划中。
- 设置要匹配的模式和匹配条件。
以上提及的插件只是JMeter内置插件的一小部分。JMeter还提供了许多其他的内置插件,每个都具有独特的功能和使用场景。通过了解和应用这些插件,测试人员可以创建出更加精细和专业的测试计划,以应对复杂的测试需求。
通过本章的学习,读者应该能够更加有效地利用JMeter的内置插件进行性能测试,掌握如何根据不同的测试场景选择合适的插件,从而提高测试的准确性和效率。在下一章节,我们将探索如何使用JMeter进行更高级的测试场景,例如通过编写Groovy脚本实现自定义逻辑来进一步增强测试能力。
简介:Apache JMeter是一个开源且免费的性能测试工具,广泛应用于性能测试、服务器压力测试、接口测试等领域。版本5.6.2集成了多种插件,提供了强大的功能特性,如多协议支持、图形化结果展示、灵活的脚本编写、分布式测试等。本文将详细介绍JMeter 5.6.2的功能特性、新特性以及性能测试实战方法,并提供相关学习资源。
更多推荐
所有评论(0)