【Python】从同步到异步多核:测试桩性能优化,加速应用的开发和验证
目录
测试工作中常用到的测试桩mock能力
应用场景
简单测试桩
http.server扩展:一行命令实现一个静态文件服务器
性能优化:使用异步响应
异步响应
能优化:利用多核
gunicorn
安装 gunicorn
使用 gunicorn 启动服务
性能优化:使用缓存(functools.lru_cache)。
单元测试中的mock
Python unittest.mock
总结
资料获取方法
测试工作中常用到的测试桩mock能力
在我们的测试工作过程中,可能会遇到前端服务开发完成,依赖服务还在开发中;或者我们需要压测某个服务,而这个服务的依赖组件(如测试环境MQ
) 无法支撑并发访问的场景。这个时候我们可能就需要一个服务,来替代测试环境的这些依赖组件或服务,而这就是本文的主角--测试桩。
测试桩可以理解为一个代理,它可以用于模拟应用程序中的外部依赖项,如数据库、网络服务或其他API,它可以帮助我们在开发和测试过程中隔离应用程序的不同部分,从而使测试更加可靠和可重复。
应用场景
测试桩使用的一般有以下几种场景:
场景 | 使用测试桩的原因与目的 |
---|---|
单元测试 | 隔离被测代码与其他组件或外部依赖的交互,便于在不考虑其他部分的情况下对被测代码进行测试。 |
集成测试 | 当某些组件未实现或不可用时,使用测试桩模拟这些组件,以便继续进行集成测试。 |
性能测试 | 快速生成高负载和大量并发请求,评估系统在高负载条件下的性能表现。 |
故障注入和恢复测试 | 模拟故障(如网络故障、服务宕机等),验证系统在遇到故障时的行为和恢复能力。 |
API测试 | 使用测试桩模拟API的响应,以便在API实现完成之前就可以进行客户端开发和测试。 |
第三方服务测试 | 在开发和测试阶段避免与真实的第三方服务进行交互,降低额外成本和不稳定的测试结果。测试桩用于模拟这些第三方服务,使得在不影响真实服务的情况下进行测试。 |
本文将选取常用的几个场景循序渐进地介绍测试桩的开发和优化。
简单测试桩
如果在测试环境中不方便安装其他的库,我们可以使用Python标准库中的一个模块http.server
模块创建一个简单的HTTP请求测试桩。
# simple_stub.py
# 测试桩接收GET请求并返回JSON数据。
import json
from http.server import BaseHTTPRequestHandler, HTTPServerclass SimpleHTTPRequestHandler(BaseHTTPRequestHandler):def do_GET(self):content = json.dumps({"message": "Hello, this is a test stub!"}).encode("utf-8")self.send_response(200)self.send_header("Content-Type", "application/json")self.send_header("Content-Length", f"{len(content)}")self.end_headers()self.wfile.write(content)if __name__ == "__main__":server_address = ("", 8000)httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)print("Test stub is running on port 8000")httpd.serve_forever()
运行上面的代码,将看到测试桩正在监听8000端口。您可以使用浏览器或curl
命令访问 http://localhost:8000
,将会收到 {'message': 'Hello, this is a test stub!'}
的响应。
http.server
扩展:一行命令实现一个静态文件服务器
http.server
模块可以作为一个简单的静态文件服务器,用于在本地开发和测试静态网站。要启动静态文件服务器,请在命令行中运行以下命令:
python3 -m http.server [port]
其中[port]是可选的端口号,不传递时默认为8000。服务器将在当前目录中提供静态文件。
如在日志文件夹中执行python -m http.server
,就能在web浏览器中访问这个文件夹中的文件和子文件夹的内容:
注意:
http.server
主要用于开发和测试,性能和安全方面不具备在生产环境部署的条件
性能优化:使用异步响应
我们在前面的实现了一个简单的测试桩,但在实际应用中,我们可能需要更高的性能和更复杂的功能。
异步响应
在只有同样的资源的情况下,像这样的有网络I/O的服务,使用异步的方式无疑能更有效地利用系统资源。
说到异步的http框架,目前最火热的当然是FastAPI
,使用FastAPI
实现上面的功能只需两步。
首先,安装FastAPI和Uvicorn:
pip install fastapi uvicorn
接下来,创建一个名为fastapi_stub.py
的文件,其中包含以下内容:
from fastapi import FastAPIapp = FastAPI()@app.get("/")
async def get_request():return {"message": "Hello, this is an optimized test stub!"}if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)
执行代码,这个测试桩也是监听在8000端口。我们可以像之前那样使用浏览器或其他HTTP客户端向测试桩发起请求。
点击查看异步编程优势 介绍
能优化:利用多核
虽然我们前面使用到了异步的方式来提升测试桩的性能,但是代码还是只是跑在一个CPU核心上,如果我们要进行性能压测,可能无法满足我们的性能需求。这个时候我们可以使用 gunicorn
库 来利用上服务器的多核优势。
gunicorn
Gunicorn的主要特点和优势:
特点与优势 | 说明 |
---|---|
简单易用 | Gunicorn易于安装和配置,可以与许多Python Web框架(如Flask、Django、FastAPI等)无缝集成。 |
多进程 | Gunicorn使用预先分叉的工作模式,创建多个子进程处理并发请求。这有助于提高应用程序的性能和响应能力。 |
兼容性 | Gunicorn遵循WSGI规范,这意味着它可以与遵循WSGI规范的任何Python Web应用程序一起使用。 |
可配置性 | Gunicorn提供了许多配置选项,如工作进程数量、工作进程类型(同步、异步)、超时设置等。这使得Gunicorn可以根据具体需求进行灵活配置。 |
部署友好 | Gunicorn在生产环境中非常受欢迎,因为它简化了部署流程。Gunicorn可以与其他工具(如Nginx、Supervisor等)一起使用,以便更好地管理和扩展Web应用程序。 |
安装 gunicorn
pip install gunicorn
使用 gunicorn 启动服务
启动服务:
gunicorn -w 4 fastapi_stub:app
可以看到,上面的命令启动了4个worker 进程,大家也可以使用ps -ef
命令查询一下进程状态。
gunicorn的一些常用参数:
参数 | 说明 |
---|---|
-w, --workers | 设置工作进程的数量。根据系统的CPU核心数和应用程序的负载特征来调整。默认值为1。 |
-k, --worker-class | 设置工作进程的类型。可以是sync (默认)、gevent 、eventlet 等。如果使用异步工作进程,需要安装相应的库。例如,对于FastAPI应用程序,可以使用-k uvicorn.workers.UvicornWorker 。 |
-b, --bind | 设置服务器绑定的地址和端口。格式为address:port 。例如:-b 0.0.0.0:8000 。默认值为127.0.0.1:8000 。 |
--timeout | 设置工作进程的超时时间(以秒为单位)。如果工作进程在指定的时间内没有完成任务,它将被重启。默认值为30秒。 |
--log-level | 设置日志级别。可以是debug 、info 、warning 、error 或critical 。默认值为info 。 |
--access-logfile | 设置访问日志文件的路径。默认情况下,访问日志将输出到标准错误流。要禁用访问日志,请使用- 。例如:--access-logfile - 。 |
--error-logfile | 设置错误日志文件的路径。默认情况下,错误日志将输出到标准错误流。要禁用错误日志,请使用- 。例如:--error-logfile - 。 |
--reload | 在开发环境中使用此选项,当应用程序代码发生更改时,Gunicorn将自动重新加载。不建议在生产环境中使用。 |
--daemon | 使用此选项以守护进程模式运行Gunicorn。在这种模式下,Gunicorn将在后台运行,并在启动时自动分离。 |
Gunicorn提供了许多其他配置选项,可以根据具体需求进行调整。要查看完整的选项列表,可以查看Gunicorn的官方文档:https://docs.gunicorn.org/en/stable/settings.html。
性能优化:使用缓存(functools.lru_cache
)。
当处理重复的计算或数据检索任务时。使用内存缓存(如Python的functools.lru_cache)或外部缓存(如Redis)来缓存经常使用的数据也能极大的提升测试桩的效率。
假设我们的测试桩需要使用到计算计算斐波那契数列这样耗时的功能,那么缓存结果,在下次遇到同样的请求时直接返回而不是先计算再返回,将极大的提高资源的使用率、减少响应的等待时间。
如果仅仅是直接返回数据的,没有进行复杂的计算的测试桩,使用
lru_cache
并没有实际意义。
以下是一个更合适的使用lru_cache
的示例,其中我们将对斐波那契数列进行计算并缓存结果:
from fastapi import FastAPI
from functools import lru_cacheapp = FastAPI()@lru_cache(maxsize=100)
def fibonacci(n: int):if n <= 1:return nelse:return fibonacci(n - 1) + fibonacci(n - 2)@app.get("/fibonacci/{n}")
async def get_fibonacci(n: int):result = fibonacci(n)return {"result": result}if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)
在这个示例中,我们使用FastAPI创建了一个简单的HTTP请求测试桩。我们定义了一个名为fibonacci
的函数,该函数计算斐波那契数列。为了提高性能,我们使用functools.lru_cache
对该函数进行了缓存。
在路由/fibonacci/{n}
中,我们调用fibonacci
函数并返回结果。可以命令访问 http://localhost:8000/fibonacci/{n}
进行调试。
需要注意的是 maxsize
参数是functools.lru_cache
装饰器的一个配置选项,它表示缓存的最大容量。lru_cache
使用字典来存储缓存项,当一个新的结果需要被缓存时,它会检查当前缓存的大小。如果缓存已满(即达到maxsize
),则会根据LRU策略移除最近最少使用的缓存项。如果maxsize
设置为None
,则缓存可以无限制地增长,这可能导致内存问题。
单元测试中的mock
Python unittest.mock
在Python中,unittest模块提供了一个名为unittest.mock的子模块,用于创建mock对象。unittest.mock包含一个名为Mock的类以及一个名为patch的上下文管理器/装饰器,可以用于替换被测试代码中的依赖项。
import requests
from unittest import TestCase
from unittest.mock import patch# 定义一个函数 get_user_name,它使用 requests.get 发起 HTTP 请求以获取用户名称
def get_user_name(user_id):response = requests.get(f"https://api.example.com/users/{user_id}")return response.json()["name"]# 创建一个名为 TestGetUserName 的测试类,它继承自 unittest.TestCase
class TestGetUserName(TestCase):# 使用 unittest.mock.patch 装饰器替换 requests.get 函数@patch("requests.get")# 定义一个名为 test_get_user_name 的测试方法,它接受一个名为 mock_get 的参数def test_get_user_name(self, mock_get):# 配置 mock_get 的返回值,使其在调用 json 方法时返回一个包含 "name": "Alice" 的字典mock_get.return_value.json.return_value = {"name": "Alice"}# 调用 get_user_name 函数,并传入 user_id 参数user_name = get_user_name(1)# 使用 unittest.TestCase 的 assertEqual 方法检查 get_user_name 的返回值是否等于 "Alice"self.assertEqual(user_name, "Alice")# 使用 unittest.mock.Mock 的 assert_called_with 方法检查 mock_get 是否被正确调用mock_get.assert_called_with("https://api.example.com/users/1")
总结
在开发测试桩时,我们需要根据实际需求和后端服务的特点来设计测试桩的行为,为的是使其更接近实际后端服务的行为,确保测试结果具有更高的可靠性和准确性。
可能还有其他的优化方案,欢迎大家提出。希望本文能对大家的工作带来帮助。
如果觉得还不错,就在右下角点个赞吧,感谢!
资料获取方法
【留言777】
各位想获取源码等教程资料的朋友请点赞 + 评论 + 收藏,三连!
三连之后我会在评论区挨个私信发给你们~
相关文章:

【Python】从同步到异步多核:测试桩性能优化,加速应用的开发和验证
目录 测试工作中常用到的测试桩mock能力 应用场景 简单测试桩 http.server扩展:一行命令实现一个静态文件服务器 性能优化:使用异步响应 异步响应 能优化:利用多核 gunicorn 安装 gunicorn 使用 gunicorn 启动服务 性能优化&#…...

使用checkBox组件时,动态设置disabled,仍能触发click事件的原因及解决办法
在使用vant的Checkbox组件时,为了实现复选框组选择一个,禁用掉另一个,同时添加点击事件的功能时。遇到明明disabledtrue,但仍能触发点击事件的情况。为此,分析下触发点击事件的原因及解决方法。 一、原因 1、异步更新…...

【JavaScript】如何进行除法运算且保留小数部分不参与四舍五入【推荐库bignumber.js 】
在 bignumber.js 中进行除法运算并保留小数部分,不参与四舍五入,你可以使用 decimalPlaces 方法来指定保留的小数位数,并使用 ROUND_DOWN 舍入模式来实现截断而不进行四舍五入。 以下是在 bignumber.js 中进行除法运算且保留小数部分&#x…...

掌握Java JDK 1.8 API帮助文档中文版,事半功倍编程
文章目录 1. JDK 1.8 API帮助文档简介2. 如何查阅JDK 1.8 API帮助文档中文版2.1 在线文档2.2 本地文档2.3 集成开发环境(IDE) 3. 如何使用JDK 1.8 API帮助文档中文版3.1 寻找类和方法3.2 阅读文档说明3.3 查看示例代码 4. 总结 引言: Java是一…...

Spring Boot的自动配置原理
一.原理解释 Spring Boot的自动配置是Spring框架的一个重要特性,它旨在简化应用程序的开发和部署过程。自动配置通过基于类路径中的依赖关系和配置文件内容来预先配置Spring应用程序的各种组件和功能。这样,我们可以在无需显式配置大量参数的情况下&…...

NFS服务器
目录 1.nfs简介 2.nfs安装与配置简述 安装包: 配置文件: /etc/exports配置文件的写法 权限:(客户端对共享目录的权限,但是最主要的还是目录本身的权限) 3.nfs配置 服务端 客户端 4.autofs自动挂载…...

说明学习委员之作业管理系统—后端部分
项目背景 学习委员收集作业的过程,繁琐且曲折,作者充分理解并体谅为大家服务的苦逼学习委员,以此为出发点和灵感,设计并开发了此套作业管理系统,希望能帮助各位提高效率,早日摆脱重复机械式的工作…...

质数(判定质数 分解质因数 筛质数)
目录 一、判定质数思路分析代码实现 二、分解质因数思路分析典型题目代码实现 三、质数筛经典题目思路分析1. 朴素筛法2. 埃氏筛法3. 欧拉筛法 一、判定质数 思路分析 由于每个合数的因子是成对出现的,即如果 d d d 是 n n n 的因子,那么 n d \frac…...

SAP数据库表维护视图生成器的使用
在SAP中,经常需要自定义数据库表。而且可能需要人工维护数据库表中的数据,可以通过SM30进行维护数据;但是SM30事务的权限太大,不适宜将SM30直接分配;因此,可以通过给维护表分配事务代码,来达到控…...

数据结构 | 递归
目录 一、何谓递归 1.1 计算一列数之和 1.2 递归三原则 1.3 将整数转换成任意进制的字符串 二、栈帧:实现递归 三、递归可视化 四、谢尔平斯基三角形 五、复杂的递归问题 六、动态规划 一、何谓递归 递归是解决问题的一种办法,它将问题不断地分…...

微信发视频怎么不压缩画质?试试这几招
微信是我们常用的社交工具了,很多朋友都会用它跟好友分享视频,但想必大家都知道,微信为了节省宽带和存储空间,会自动对上传的视频进行压缩处理,甚至过大的视频会被限制发送,怎么才能让微信不自动压缩画质呢…...

【网络安全带你练爬虫-100练】第16练:使用session发送请求
目录 一、目标1:使用seesion进去请求 二、网络安全O 一、目标1:使用seesion进去请求 (1)应用: 通过创建会话(session)对象来请求并爬取返回的数据包 情景:需要登录才能爬取的网…...

论文代码学习—HiFi-GAN(3)——模型损失函数loss解析
文章目录 引言正文生成器损失函数最小二乘损失函数梅尔频谱图损失函数特征匹配损失函数生成器最终损失函数loss生成器loss对应代码 鉴定器损失函数鉴定器损失函数代码 总结引用 引言 这里翻译了HiFi-GAN这篇论文的具体内容,具体链接。这篇文章还是学到了很多东西&a…...

CLion中avcodec_receive_frame()问题
1. 介绍 在提取音视频文件中音频的PCM数据时,使用avcodec_receive_frame()函数进行解码时,遇到了一些问题,代码在Visual Studio 2022中运行结果符合预期,但是在CLion中运行时,获取的AVFrame有错误,和VS中获…...

Linux安装操作(Mac版本)
Parallels Desktop的简介 Parallels Desktop是Mac平台上的虚拟机软件,也是Mac平台最好的虚拟机软件之一。它允许用户在Mac OS X系统上同时运行其他操作系统,例如Windows、Linux等。Parallels Desktop为Mac用户提供了使用其他操作系统和软件的便利性&…...

Linux(四)--包软件管理器与Linux上软件的下载示例
一.包软件管理器【yum和apt】 1.先来学习使用yum命令。yum:RPM包软件管理器,用于自动化安装配置Linux软件,并可以自动解决依赖问题。通过yum命令我们可以轻松实现软件的下载,查找,卸载与更新等管理软件的操作。 最常用…...

HTML <param> 标签
实例 向 HTML 代码添加一个对象: <object classid="clsid:F08DF954-8592-11D1-B16A-00C0F0283628" id="Slider1" width="100" height="50"><param name="BorderStyle" value="1" /><param nam…...

基于ARM+FPGA (STM32+ Cyclone 4)的滚动轴承状态监测系统
状态监测系统能够在故障早期及时发现机械设备的异常状态,避免故障的 进一步恶化造成不必要的损失,滚动轴承是机械设备的易损部件,本文对以滚动 轴承为研究对象的状态监测系统展开研究。现有的监测技术多采用定时上传监 测数据,…...

二、数据结构10:堆 模板题+算法模板(堆排序,模拟堆)
文章目录 算法模板堆题目代码模板堆的原理down操作理解:up操作理解建堆操作关于heap_swap中存的映射数组理解(模拟堆题目中用到) 模板题堆排序原题链接题目思路题解 模拟堆原题链接题目思路题解 算法模板 堆题目代码模板 // h[N]存储堆中的…...

W6100-EVB-PICO做DNS Client进行域名解析
前言 在上一章节中我们用W6100-EVB-PICO通过dhcp获取ip地址(网关,子网掩码,dns服务器)等信息,给我们的开发板配置网络信息,成功的接入网络中,那么本章将教大家如何让我们的开发板进行DNS域名解…...

【linux-网络】4层转发方法-iptable以及nginx
1.背景 有时候远程或者某些业务需要做转发就会用到iptables或者nginx,或者ss都可以 根据自己的情况去适配。 2.方法: 1)iptables -把linux内核转发功能打开 echo "net.ipv4.ip_forward1" >> /etc/sysctl.conf -出入转发…...

vue复制文案,复制图片,黏贴图片
vue 实现复制文案,复制图片,在微信聊天框,黏贴为图片 //安装 cnpm i clipboard-all //引用 import clipboard from clipboard-all<!-- row.url 图片路径 --><div ref"foo" class"hidden"><img :src"…...

Web应急思路
Web应急思路 找到webshell --> 确定攻击者IP --> 回溯攻击者操作 --> 梳理整个攻击过程 1.寻找webshell方法 1.文件内容中的恶意函数 2.web日志中的webshell特征 3.贴合web业务中的URL来分析web日志 4.源码版本管理对比,注重修改或新增的脚本文件 5.统计…...

shell脚本清理redis模糊匹配的多个key,并计算释放内存大小
#!/bin/bash# 定义Redis服务器地址和端口 REDIS_HOST"localhost" REDIS_PORT6380# 获取Redis当前内存使用量(以字节为单位) function get_redis_memory_usage() {redis-cli -h $REDIS_HOST -p $REDIS_PORT INFO memory | grep "used_memo…...

python-MySQL数据库建表语句(需要连接数据库)转存为Excel文档-工作小记
将create table XXXXXX 转为指定Excel文档。该脚本适用于数据库表结构本地文档记录 呈现效果 代码 # -*- coding:utf-8 -*- # Time : 2023/8/2 15:14 # Author: 水兵没月 # File : MySQL建表_2_excel.py import reimport mysql.connector import pandas as pd db 库名 mydb …...

iOS Block介绍
文章目录 一、Block定义二、block为什么用copy修饰三、block使用时的注意事项四、使用 block时什么情况会发生引用循环,如何解决?五、在block内如何修改block外部变量?六、__block与__weak的区别 一、Block定义 目的就是能够直接存储一个代码…...

小程序安全性加固:如何保护用户数据和防止恶意攻击
第一章:引言 在当今数字化时代,移动应用程序的使用已经成为人们日常生活中的重要组成部分。小程序作为一种轻量级的应用程序形式,受到了广泛的欢迎。然而,随着小程序的流行,安全性问题也日益凸显。用户数据泄露和恶意攻…...

Ubuntu的tar命令详解
在 Ubuntu 中压缩文件夹可以使用 tar 命令。tar 可以将多个文件或文件夹打成一个包,并可选是否进行压缩,最常用的压缩方式是 gzip 和 bzip2。 常用的 tar 命令参数如下: -c:创建新的 tar 包; -x:解压 tar…...

使用elementplus实现文本框的粘贴复制
需求: 文本框仅用于显示展示数据并且用户可以进行复制,并不会进行修改和编辑, 注意点: 1.首先且文本为多行。所以不能使用普通的el-input,这种一行超出就会隐藏了,如果多行超出行数也会隐藏(…...

计算机毕设 深度学习卫星遥感图像检测与识别 -opencv python 目标检测
文章目录 0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后 0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长…...