企业级locust性能框架搭建及使用

本文详细介绍了Locust性能测试框架的安装、配置及实战应用,包括设置退出条件、测试前后的setup-teardown方法,创建测试类,编写性能用例和tag,以及如何分析locust报告。探讨了如何处理并发用户请求不均匀的问题并提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

LR、Jmeter 是测试人员最常使用的性能测试工具。随着测试人员不断的学习,大多数都对python比较熟悉,基于这点考虑,想寻找一款有python支持的性能测试工具,并且代码编辑功能比jmeter强的开源工具或框架。正巧公司在性能压测中,使用的就是locust框架,本篇文章进行一些学习并在项目的性能测试中使用和总结


一、Locust的介绍:

locust是一个比较新的python性能测试框架,近几年才受到关注,该框架的主要功能是可以让用户使用纯python进行性能测试脚本的开发,locust具有高扩展性,它是一个完全事件驱动的框架,拥有一个很大和快速发展的社区,locust完全基于事件,因此可以在一台计算机上支持数千个并发用户,与许多其他基于事件的应用程序相比,它不适用回调。相反,它通过协程(gevent)机制适用轻量级过程

二、安装Locust

pip install locust

三、Locust框架搭建

前置:导入locust模块

from locust import HttpUser, task, constant, between, constant_pacing, events, LoadTestShape, tag

1.设置退出码

@events.quitting.add_listener
def _(environment, **kw):
    if environment.stats.total.fail_ratio > 0.01:
        logging.error("Test failed due to failure ratio > 1%")
        environment.process_exit_code = 1
    elif environment.stats.total.avg_response_time > 2000:
        logging.error("Test failed due to average response time ratio > 2000 ms")
        environment.process_exit_code = 1
    elif environment.stats.total.get_response_time_percentile(0.95) > 5000:
        logging.error("Test failed due to 95th percentile response time > 800 ms")
        environment.process_exit_code = 1
    else:
        environment.process_exit_code = 0

例如满足以上任何条件,则将退出代码设置为非零:
超过0.01的请求失败
平均响应时间大于2000ms
响应时间的第95个百分位数大于5000毫秒

2.设置整个负载测试开始前的setup & teardown 方法

setup,在整个负载测试开始前,会执行此方法:

@events.test_start.add_listener   # todo:在整个负载测试开始前的setup方法
def on_test_start(environment, **kwargs):
    print('test start event called.')
    print('test start event finished.')

teardown,在整个负载测试开始后,会执行此方法:

@events.test_stop.add_listener  # todo:在整个负载测试结束后的 teardown方法
def on_test_stop(environment, **kwargs):
    print('test stop event called.')

3.创建locust性能测试类

class DoStressTestOnAPI(HttpUser):  # todo: 继承HttpUser类,表明新创建的类是支持HTTP协议的web接口测试
    wait_time = constant_pacing(1)  # 系统自动调配等待时间保证每秒中每个user instance最多只能有一个task在执行
    global project_host   
    host = project_host  # todo: 定义项目地址
    headers_branch = {   # todo: 放置请求头
        "Host": "xxxx",
        "Accept": "xxx",
        "Content-Type": "xxx",
        "brandId": "3",
        "Authorization": "xxxx",
        "Cookie": "xxx=xx",
    }

4.设置每个用户的setup和teardown

 def on_start(self):  # todo:每个用户启动都会调用此方法 on_start则是每个用户开始时运行
        print('test started.')
 def on_stop(self):  # todo:每个用户启动都会调用此方法 on_stop则是每个用户停止时运行
        logging.info('test stopped')

5.性能用例的编写+tag方法使用

    @tag('客户信息管理-查询')
    @task(3)  # todo:运行DoStressTestOnAPI时,会从多个task任务中随机选择一个,权重增加了他的选择几率
    def search_retainedCustomer_data(self):

        with self.client.request(
                'post', f'api/customer/retainedCustomer/v1/queryByPage',
                json={'pageSize': 10, 'pageNum': 1,
                      'customerCode': "branchId"},
                catch_response=True,
                headers=DoStressTestOnAPI.headers_branch,
                name="api/customer/retainedCustomer/v1/queryByPage") as response:  # todo: name分组,可解决pathvariable问题
            logging.info('+++++++++++++++++' + str(response.status_code))
            logging.info(response.request.headers)
            if response.status_code == 200 and response.json()['code'] == 200:
                response.success()
                logging.info(response.json())
            else:
                response.failure('test failed and response code is: ' + str(response.json()['code']))
                logging.info('error occured')

tag:表示这个用例的名字,在运行时,可以通过相同的tag名称来执行性能测试场景

task:权重的设置。locust的权重表示着被用户选择并请求的概率,随着权重越高,则被用户请求的几率就会越高。locust的权重设置比较灵活,可以设置每个方法接口访问的比例

catch_response:
布尔参数
<1>如果设置为True,则表示,如果响应代码是正常(2XX),则标记通过。
<2>如果设置为False,则表示,如果响应代码是正常(2XX),则标记通过。

name:
name分组,如果API传参是在url后传递,也就是pathvariable。可以加上name做一个分组统计,最后报告会计算共请求了多少次

6.locust脚本运行

locust --headless -f filename.py -u5 -r 5 -t 60 --tags 客户信息管理-查询 -s 99 --html=.\stress_report\report.html

–headless :为无头模式运行;
-f: 打开指定路径的locust文件;
–tags: 为设置的tag标签,可运行相同tag标签的性能case;
.\stress_report\report.html: 存放locust性能报告路径
-u :设置虚拟用户数,也是并发用户数
-r :设置每秒启动虚拟用户数。用户递增的速率
-t :设置设置运行时间
在这里插入图片描述
在这里插入图片描述

7.locust报告指标分析

在这里插入图片描述
Method: 请求方式
name: 请求路径的api
requests: 请求数
fails: 请求失败数
Average: 平均数,响应时间,单位毫秒
Min: 最小响应时间,单位毫秒
Max: 最大响应时间,单位毫秒
Average Size(bytes): 单个请求大小,单位为字节
RPS: 每秒事务数(TPS)
Fallures/s: 每秒失败次数

在这里插入图片描述

Total Requests per Second
每秒总的请求数,也需要看趋势
如果上下波动比较大,就要考虑,是不是这个用户在请求某个接口卡主了,这个数量就会变低,如果请求的响应时长都保持在一致(3~5s是用户能够接受的响应时长范围),那这个曲线应该是平的

Response Times(ms)
接口响应时间,主要看趋势,是否平稳
3~5s是用户能够接受的响应时长范围;如果过长,需要进行SQL效率调优

Number of Users
每秒用户数;需要观察是否有用户阻塞的现象存在

7、整体locust文件

from locust import HttpUser, task, constant, between, constant_pacing, events, LoadTestShape, tag
import logging



@events.quitting.add_listener
def _(environment, **kw):
    if environment.stats.total.fail_ratio > 0.01:
        logging.error("Test failed due to failure ratio > 1%")
        environment.process_exit_code = 1
    elif environment.stats.total.avg_response_time > 2000:
        logging.error("Test failed due to average response time ratio > 2000 ms")
        environment.process_exit_code = 1
    elif environment.stats.total.get_response_time_percentile(0.95) > 5000:
        logging.error("Test failed due to 95th percentile response time > 800 ms")
        environment.process_exit_code = 1
    else:
        environment.process_exit_code = 0

@events.test_start.add_listener   # todo:在整个负载测试开始前的setup方法
def on_test_start(environment, **kwargs):
    print('test start event called.')
    print('test start event finished.')


@events.test_stop.add_listener  # todo:在整个负载测试结束后的 teardown方法
def on_test_stop(environment, **kwargs):
    print('test stop event called.')

class DoStressTestOnAPI(HttpUser):  # todo: 继承HttpUser类,表明新创建的类是支持HTTP协议的web接口测试
    wait_time = constant_pacing(1)  # 系统自动调配等待时间保证每秒中每个user instance最多只能有一个task在执行
    global project_host
    host = project_host  # todo: 定义项目地址
    headers_branch = {   # todo: 放置请求头
        "Host": "xxxx",
        "Accept": "xxx",
        "Content-Type": "xxx",
        "brandId": "3",
        "Authorization": "xxxx",
        "Cookie": "xxx=xx",
    }

    def on_start(self):  # todo:每个用户启动都会调用此方法 on_start则是每个用户开始时运行
        print('test started.')

    def on_stop(self):  # todo:每个用户启动都会调用此方法 on_stop则是每个用户停止时运行
        logging.info('test stopped')

    @tag('客户信息管理-查询')
    @task(3)  # todo:运行DoStressTestOnAPI时,会从多个task任务中随机选择一个,权重增加了他的选择几率
    def search_retainedCustomer_data(self):

        with self.client.request(
                'post', f'api/customer/retainedCustomer/v1/queryByPage',
                json={'pageSize': 10, 'pageNum': 1,
                      'customerCode': "branchId"},
                catch_response=True,
                headers=DoStressTestOnAPI.headers_branch,
                name="api/customer/retainedCustomer/v1/queryByPage") as response:  # todo: name分组,可解决pathvariable问题
            logging.info('+++++++++++++++++' + str(response.status_code))
            logging.info(response.request.headers)
            if response.status_code == 200 and response.json()['code'] == 200:
                response.success()
                logging.info(response.json())
            else:
                response.failure('test failed and response code is: ' + str(response.json()['code']))
                logging.info('error occured')

四、 Q & A

有这样一个问题:
用户数我们设置了80,80个用户可能有的用户1秒请求很多次,有的用户1秒请求比较少,所以会出现请求速率对不上的情况(并发会去抢协程)

解决办法(转载:https://www.jianshu.com/p/b01e413a25ba):

我们在每次请求前记录一个start_time,在请求结束后记录一个时间,计算两者时间差
如果时间差小于1s,需要等待不足1s的剩余时间,如果时间差大于1s,继续下一次请求

start_time = time.time()
try:
    with self.client.get(url, name="so_group_search_press",
                         catch_response=True) as response:
        # 断言
        if response.status_code == 200 and json.loads(response.text)["code"] == 0 and \
                json.loads(response.text)['message'] == 'ok':
            response.success()
        else:
            response.failure("请求失败,status_code不为200或返回code不为0或返回message不为ok: %s" % str(json.loads(response.text))[0:500])
except Exception as e:
    response.failure("请求失败,status_code不为200或返回code不为0或返回message不为ok: %s" % str(e)[0:500])

time_delay = time.time() - start_time

if time_delay > 1:
    pass
else:
    wait_time = 1 - time_delay
    time.sleep(wait_time)
<think>好的,用户现在问的是“python企业级爬虫用那个方式比较稳定”。首先,我需要回顾之前的对话历史,用户之前询问过关于Selenium在企业级爬虫项目中的应用案例,我详细介绍了几个案例和技术实现的关键点。现在用户可能希望了解在Python环境下,企业级爬虫的稳定解决方案,特别是除了Selenium之外的其他方法,或者如何结合使用不同的工具。 用户的问题中提到“稳定”,这可能意味着他们关心系统的可靠性、抗干扰能力、长期运行的维护性等方面。企业级应用通常需要处理大规模数据、高并发、反爬机制、法律合规等问题,所以需要综合考虑这些因素。 首先,我需要确定用户对“稳定”的具体需求是什么。是否指应对反爬措施的能力?还是系统长时间运行的容错性?或者数据抓取的效率和准确性?可能需要进一步细化,但根据问题本身,可能需要覆盖多个方面。 接下来,我需要整理Python中常用的爬虫框架和工具,比如Scrapy、Requests + BeautifulSoup、Selenium、Playwright等,分析它们的优缺点,以及在企业级环境中的适用性。同时,结合之前提到的Selenium案例,可能用户需要比较Selenium与其他方法在稳定性上的差异。 另外,用户可能想知道如何构建一个稳定的爬虫系统,而不仅仅是选择工具。这包括架构设计、分布式处理、代理管理、数据存储、监控和容错机制等。需要将这些方面结合起来,给出综合建议。 可能还需要提到法律合规和伦理问题,因为企业级应用必须遵守相关法规,否则即使技术再稳定,也可能面临法律风险。 现在需要结构化地回答,可能分为几个部分:工具选择、架构设计、反反爬策略、监控维护、法律合规等。每个部分给出推荐的工具或方法,并解释为什么它们有助于稳定性。 需要确保回答中提到的工具和策略都是当前主流且经过验证的,比如Scrapy的异步处理能力,结合Selenium处理动态页面,使用Scrapy-Redis做分布式,代理池的搭建,以及Celery进行任务调度等。 同时,用户可能对代码示例或具体配置感兴趣,但问题比较偏向方法论,所以可能需要简要提及技术栈,而不过多深入代码细节,除非用户后续追问。 最后,总结时强调没有单一的最佳工具,而是根据具体需求选择合适的组合,并注重系统设计和维护,才能确保稳定性。</think>--- ### Python企业级爬虫稳定方案设计与技术选型指南 --- #### 一、企业级稳定性核心要求 - **高可用性**:7×24小时持续运行,故障自动恢复 - **抗干扰能力**:突破反爬限制(IP封锁、验证码、行为检测) - **可扩展性**:支持动态扩容(如分布式架构) - **数据一致性**:保障数据完整性与去重 - **合规安全**:遵守数据抓取协议与隐私保护 --- #### 二、稳定技术方案推荐(组合使用) ##### 1. **基础框架选择** - **Scrapy框架(首选)** - **优势**: - 异步IO架构(Twisted引擎)天然支持高并发 - 内置中间件系统(Middleware Pipeline) - 成熟扩展生态(如`scrapy-redis`分布式支持) - **适用场景**:静态页面快速抓取、结构化数据处理 - **代码示例**: ```python class ProductSpider(scrapy.Spider): name = 'amazon' custom_settings = { 'CONCURRENT_REQUESTS': 32, # 并发控制 'DOWNLOAD_DELAY': 0.5 # 请求间隔 } def parse(self, response): item = {} item['price'] = response.css('span.a-price-whole::text').get() yield item ``` - **Selenium/Playwright(动态渲染补充)** - **优势**: - 完整模拟浏览器行为(点击、滚动、表单提交) - 处理JavaScript渲染页面(如React/Vue单页应用) - **稳定策略**: - 使用`undetected-chromedriver`绕过自动化检测 - 结合`WebDriverWait`智能等待元素加载 ##### 2. **分布式架构** - **Scrapy-Redis方案**: - 基于Redis的任务队列实现分布式调度 - 支持断点续爬与负载均衡 - **Celery+Docker方案**: - 容器化部署爬虫节点 - 动态伸缩Worker数量应对流量波动 ##### 3. **反爬对抗体系** | 反爬类型 | 解决方案 | |----------------|--------------------------------------------------------------------------| | **IP封禁** | 代理池轮换(推荐付费服务如Oxylabs+自建IP池) | | **验证码** | 第三方识别平台(如2Captcha)或深度学习模型(CNN+LSTM) | | **行为指纹** | 修改浏览器指纹(通过`pyppeteer-stealth`伪装) | | **请求频率** | 动态调整延迟(如$ delay = \frac{1}{QPS} + random.uniform(-0.2,0.2) $) | --- #### 三、稳定性增强关键配置 ##### 1. **请求管理优化** ```python # Scrapy中间件示例:自动切换代理 class ProxyMiddleware: def process_request(self, request, spider): request.meta['proxy'] = get_proxy() # 从代理池获取 # Selenium防检测配置 options = webdriver.ChromeOptions() options.add_argument("--disable-blink-features=AutomationControlled") options.add_experimental_option("excludeSwitches", ["enable-automation"]) ``` ##### 2. **容错机制设计** - **重试策略**:指数退避重试(如`RETRY_TIMES=3`,$ delay = 2^{retry} $秒) - **异常捕获**: ```python try: element = driver.find_element(By.CSS_SELECTOR, ".price") except NoSuchElementException: logger.error("元素定位失败,页面结构可能已变更") self.retry_request() ``` ##### 3. **监控告警系统** - **Prometheus+Grafana**:实时监控指标(请求成功率、数据抓取量、QPS) - **企业微信/钉钉机器人**:异常通知(如连续失败率>5%触发告警) --- #### 四、典型技术栈组合 | 需求场景 | 推荐技术方案 | |------------------|----------------------------------------------------------------------------| | **高并发抓取** | Scrapy + Scrapy-Redis + Kubernetes(自动扩缩容) | | **动态页面处理** | Playwright + Pyppeteer(轻量级Headless方案) | | **数据存储** | PostgreSQL(关系型) + MongoDB(非结构化) + MinIO(文件存储) | | **任务调度** | Airflow(复杂DAG编排)或 Apache DolphinScheduler(可视化调度) | --- #### 五、最佳实践建议 1. **环境隔离**: - 使用`virtualenv`或`conda`创建独立Python环境 - Docker容器化部署(包含完整依赖) 2. **法律合规检查**: - 定期扫描`robots.txt`变更(如自动解析`User-agent: * Disallow: /private/`) - GDPR合规数据清洗(如手机号脱敏:$ \text{138****5678} $) 3. **性能压测**: - Locust压力测试(模拟$ 1000+ $并发请求) - 优化瓶颈(如启用`HTTP/2`协议、调整TCP连接池大小) --- #### 六、总结 最稳定的Python企业级爬虫方案通常采用**Scrapy核心框架+Playwright/Selenium动态渲染补充+分布式架构**的组合。关键要点: - **动态页面**:优先尝试API逆向工程,次选浏览器自动化 - **反爬对抗**:代理池与请求指纹管理需占整体开发时间的40%以上 - **监控体系**:建立从网络层到业务层的全链路监控 - **成本控制**:平衡自建代理池与第三方服务的成本(建议$ \text{预算占比} \leq 15\% $) 实际项目中,建议通过`A/B测试`验证不同策略的有效性(例如对比`Selenium`与`Playwright`的资源消耗),最终形成适合业务特性的稳定方案。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值