【测试联调】如何在前后端测试联调时优雅的构造异常场景

目录

背景

使用iptables实现

利用iptables丢弃某ip数据包

使用 -L 列出所有规则

IP 连通性 通信 测试

插入一条规则,丢弃此ip 的所有协议请求

列出所有规则

测试 丢弃规则内的IP 连通性

清除 规则列表的 限制

模拟ip进行丢包50%的处理。

mysql proxy 代理

proxy代码

直接使用pymysql 测试

Python 版本低于3.7

其他扩展

总结

资料获取方法


背景

当前的应用都使用了前后端分离的架构,前后端系统需要协同以实现各种功能。后端系统通常负责处理业务逻辑、数据存储和与其他服务的交互,而前端则负责用户界面和用户交互。而在前后端数据交互的过程中,各种异常和错误都有可能发生,确认异常发生时前后端系统的处理是否合理是测试验证中非常重要的一环。

在上一篇博客中我介绍了如何使用测试桩来隔离对环境的依赖,这次我们一起看看如何使用异常注入来应对联调中的异常场景。

使用iptables实现

在系统异常中,数据库连接失败、第三方服务不可用等都是比较典型的场景。常见的验证手段往往是前端的同学告知后台同学开启网络隔离,然后再进行验证。

利用iptables丢弃某ip数据包

使用 -L 列出所有规则

具体操作:

$  iptables     -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destinationChain FORWARD (policy ACCEPT)
target     prot opt source               destinationChain OUTPUT (policy ACCEPT)
target     prot opt source               destination
IP 连通性 通信 测试
#  检查发现能 是否能正常 ping通
$  ping {数据库/后端地址IP}
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.*: icmp_seq=1 ttl=61 time=0.704 ms
64 bytes from 1.1.1.*: icmp_seq=2 ttl=61 time=0.802 ms           
^C
--- 1.1.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.704/0.753/0.802/0.049 ms
插入一条规则,丢弃此ip 的所有协议请求
$  iptables  -I   INPUT   -p   all   -s {数据库/后端地址IP}   -j   DROP
列出所有规则
$ iptables  -LChain INPUT (policy ACCEPT)
target     prot opt source               destination
DROP       all  --  1.1.1.*        anywhereChain FORWARD (policy ACCEPT)
target     prot opt source               destinationChain OUTPUT (policy ACCEPT)
target     prot opt source               destination
测试 丢弃规则内的IP 连通性
$ ping 1.1.1.*
PING 1.1.1.1 (1.1.1.*) 56(84) bytes of data.
^C
--- 1.1.1.1 ping statistics ---
85 packets transmitted, 0 received, 100% packet loss, time 84312ms
清除 规则列表的 限制
$  iptables     -F
模拟ip进行丢包50%的处理。
iptables -I INPUT -s {后端IP} -m statistic --mode random --probability 0.5 -j DROP

上面这种方式能实现相关的联调,但是有两点可以改进的地方:

  • 这个方式最大的限制是会影响所有的调用这个系统的测试人员。
  • 需要人为的介入,每次都需要人为操作

那么有没有侵入更小,更优雅的异常场景验证方式呢?答案是肯定的。

mysql proxy 代理

更优雅的方式:写一个mysql proxy代理,让后端svr 直接连接这个proxy,proxy再连接真实的mysql。

  • 普通请求经过proxy时,proxy直接转发给真实mysql,并把mysql 的回包正常返回给调用端。
  • 当收到某个关键字(timeout)时,直接断开TCP连接,模拟连接DB超时场景

proxy代码

import asyncio# 真实mysqlIP端口
mysql_host = settings.MYSQL_HOST
mysql_port = settings.MYSQL_PORT# 处理客户端连接
async def handle_connection(client_reader, client_writer):# 连接到实际的 MySQL 服务器mysql_reader, mysql_writer = await asyncio.open_connection(mysql_host, mysql_port)# 转发握手包handshake_packet = await mysql_reader.read(4096)client_writer.write(handshake_packet)await client_writer.drain()# 处理客户端认证请求auth_packet = await client_reader.read(4096)mysql_writer.write(auth_packet)await mysql_writer.drain()# 转发认证响应auth_response = await mysql_reader.read(4096)client_writer.write(auth_response)await client_writer.drain()# 转发请求和响应while True:data = await client_reader.read(4096)if not data:breaksql_query = data[5:].decode('utf-8')if "timeout" in sql_query:  # sql 包含timeout 关键字时,直接关闭连接await asyncio.sleep(1)breakelse:mysql_writer.write(data)await mysql_writer.drain()response = await mysql_reader.read(4096)client_writer.write(response)await client_writer.drain()# 关闭连接client_writer.close()mysql_writer.close()async def main(host, port):server = await asyncio.start_server(handle_connection, host, port)async with server:await server.serve_forever()# 使用示例
if __name__ == "__main__":# 本地监听 3307, svr 连接到3307host = "0.0.0.0"port = 3307asyncio.run(main(host, port))
直接使用pymysql 测试

下面的代码会在执行到第二条select 语句时超时:

import pymysqlconnection = pymysql.connect(host="192.168.31.76",port=8899,# 真实mysql 账户信息user="{user}",password="{password}",database="mydb",)curs = connection.cursor()
curs.execute("select * from user where name='bingo';")
print(curs.fetchall())curs.execute("insert into user set name='bingtime';")
connection.commit()curs.execute("select * from user where name='timeoutbingo';")
print(curs.fetchall())
curs.close()
connection.close()
Python 版本低于3.7

低版本的Python没有asyncio.run 和server.serve_forever()需要修改main函数.

# 主函数,启动服务器并监听连接
async def main(host, port):server = await asyncio.start_server(handle_connection, host, port)try:# Python 3.6.5 中没有 server.serve_forever() 方法,所以需要使用一个无限循环来保持服务器运行。# 我们使用 asyncio.sleep() 来避免阻塞事件循环,使其可以处理其他任务,如新连接。while True:await asyncio.sleep(3600)  # 1 hourexcept KeyboardInterrupt:passfinally:server.close()await server.wait_closed()# 使用示例
if __name__ == "__main__":host = "0.0.0.0"port = 3307loop = asyncio.get_event_loop()try:loop.run_until_complete(main(host, port))except KeyboardInterrupt:passfinally:loop.close()

其他扩展

写一个proxy,监听来往的SQL 语句:

import pymysql
import socket
import threading
# 配置
SERVER_ADDRESS = (settings.MYSQL_HOST, settings.MYSQL_PORT)  # 真实MySQL 服务器地址
PROXY_ADDRESS = ('0.0.0.0', 8899)  # 监听代理服务器地址def print_query(data):try:command = data[4]if command == pymysql.constants.COMMAND.COM_QUERY:query = data[5:].decode("utf-8")print(f"SQL: {query}")except Exception as e:print(f"Error parsing packet: {e}")def forward_data(src_socket, dst_socket, parser=None):while True:try:data = src_socket.recv(4096)if not data:breakif parser:parser(data)dst_socket.sendall(data)except OSError as e:if e.errno == 9:breakelse:raisedef main():# 创建代理服务器套接字proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)proxy_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)proxy_socket.bind(PROXY_ADDRESS)proxy_socket.listen(5)print(f"Proxy server listening on {PROXY_ADDRESS}")while True:client_socket, client_address = proxy_socket.accept()print(f"Client {client_address} connected")# 连接到 MySQL 服务器server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.connect(SERVER_ADDRESS)try:# 创建线程转发客户端数据到服务器client_to_server = threading.Thread(target=forward_data, args=(client_socket, server_socket, print_query))client_to_server.start()# 创建线程转发服务器数据到客户端server_to_client = threading.Thread(target=forward_data, args=(server_socket, client_socket))server_to_client.start()# 等待线程完成client_to_server.join()server_to_client.join()finally:client_socket.close()server_socket.close()print(f"Client {client_address} disconnected")if __name__ == "__main__":main()

如果使用上面的pymysql测试代码执行,会打印如下的信息:

Client ('127.0.0.1', 57184) connected
SQL: SET AUTOCOMMIT = 0
SQL: select * from user where name='bingo';
SQL: insert into user set name='bing21211';
SQL: COMMIT
SQL: select * from user where name='bingo';
SQL: select * from user where name='bingo';
SQL: select * from user where name='timeoutbingo';
Client ('127.0.0.1', 57184) disconnected

总结

通过实现合适的异常处理机制,可以确保用户在遇到问题时获得有用的反馈,验证这些处理机制能提高系统的稳定性和安全性。iptables 功能强大但是需要手动操作,mysql proxy代理功能直接,但是应用场景较为有限,大家可以根据实际情况进行选择。


资料获取方法

【留言777】

各位想获取源码等教程资料的朋友请点赞 + 评论 + 收藏,三连!

三连之后我会在评论区挨个私信发给你们~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/20675.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

脑电信号处理与特征提取——6.运用机器学习技术和脑电进行大脑解码(涂毅恒)

目录 六、运用机器学习技术和脑电进行大脑解码 6.1 前言 6.2 基于脑电数据的机器学习基础分析 6.3 基于脑电数据的机器学习进阶分析 6.4 代码解读 六、运用机器学习技术和脑电进行大脑解码 6.1 前言 6.2 基于脑电数据的机器学习基础分析 6.3 基于脑电数据的机器学习进阶分…

快速增加Shopee,lazada店铺销量的秘籍大揭秘

在竞争激烈的电商市场中,如何快速提高Shopee。lazada店铺的销量一直是卖家们关注的焦点。 优化产品信息:在Shopee平台上,完整填写产品标题、描述和关键词等信息非常重要。确保您的产品信息准确、清晰,并包含与目标买家搜索相关的…

C语言笔试题训练【第一天】

目录 第一题 第二题 第三题 第四题 第五题 大家好,我是纪宁。 从今天开始博主会日更一些经典的C语言笔试题,持续20天左右。题目类型为5道选择题加2道编程题,希望能和大家一起进步。 第一题 1.读程序,下面程序正确的输出是&…

如何在Visual Studio Code中用Mocha对TypeScript进行测试

目录 使用TypeScript编写测试用例 在Visual Studio Code中使用调试器在线调试代码 首先,本文不是一篇介绍有关TypeScript、JavaScript或其它编程语言数据结构和算法的文章。如果你正在准备一场面试,或者学习某一个课程,互联网上可以找到许多…

微信小程序wx.getlocation接口权限申请总结

先附上申请通过截图 插播内容:可代开通,保证通过。wx.getLocation接口(获取当前的地址位置) qq: 308205428 如何申请 当申请微信小程序的wx.getLocation接口权限时,你可以…

Go语言进阶语法八万字详解,通俗易懂

文章目录 File文件操作FileInfo接口权限打开模式File操作文件读取 I/O操作io包 文件复制io包下的Read()和Write()io包下的Copy()ioutil包总结 断点续传Seeker接口断点续传 bufio包bufio包原理Reader对象Writer对象 bufio包bufio.Readerbufio.Writer ioutil包ioutil包的方法示例…

gitlab CI/CD 安装 gitlab runner

一、为什么需要安装gitlab runner ? 极狐GitLab Runner 极狐GitLab Runner 是在流水线中运行作业的应用,与极狐GitLab CI/CD 配合运作。 说白了就是你部署的一个agent。 二、如何安装? 1.介绍通过helm部署github runner 2.helm添加仓库 h…

游戏引擎:打造梦幻游戏世界的秘密武器

介绍 游戏引擎是游戏开发中不可或缺的工具,它为开发者提供了构建游戏世界所需的各种功能和工具。本文将介绍游戏引擎的概念、使用方法以及一个完整的游戏项目示例。 游戏引擎的概念 游戏引擎是一种软件框架,它提供了游戏开发所需的各种功能和工具&…

【Java可执行命令】(十四)脚本执行工具jrunscript :在命令行环境下交互式执行一些简单的脚本或测试代码片段~

Java可执行命令之jrunscript 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 语法格式3.2 启动 jrunscript 直接执行脚本3.3 可选参数&#xff1a;-l < language>3.4 可选参数&#xff1a;-e < script>3.5 可选参数&#xff1a;-f < script file>3.6 注意事项 4️…

大模型开发(十五):从0到1构建一个高度自动化的AI项目开发流程(上)

全文共5600余字&#xff0c;预计阅读时间约13~20分钟 | 满满干货(附全部代码)&#xff0c;建议收藏&#xff01; 本文目标&#xff1a;提出一种利用大语言模型(LLMs)加快项目的开发效率的解决思路&#xff0c;本文作为第一部分&#xff0c;主要集中在如何完整的执行引导Chat模…

VSCode新手快速下载、安装、使用

目录 下载 安装 1、许可协议 2、安装位置 3、开始菜单文件夹 4、附加任务 5、确认安装 6、完成 使用 1、汉化&#xff08;设置中文界面&#xff09; 2、设置 下载 进入VSCode官方页面&#xff0c;选择自己系统对应的下载链接VSCode默认提供的User Installer版本。但…

如何使用大模型处理生活繁琐的工作

如果每封电子邮件、每个带有订单、发票、投诉、录用请求或工作申请的 PDF 都可以翻译成机器可读的数据&#xff0c;会怎样&#xff1f;然后可以由 ERP / CRM / LMS / TMS 自动处理吗&#xff1f;无需编程特殊接口。 听起来很神奇&#xff1f;它确实有一些魔力。但最近已成为可…

SpringBoot引入MyBatisGenerator

1.引入插件 <plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.5</version><configuration><!--generator配置文件所在位置--><configuratio…

Python web实战之 Django 的 ORM 框架详解

本文关键词&#xff1a;Python、Django、ORM。 概要 在 Python Web 开发中&#xff0c;ORM&#xff08;Object-Relational Mapping&#xff0c;对象关系映射&#xff09;是一个非常重要的概念。ORM 框架可以让我们不用编写 SQL 语句&#xff0c;就能够使用对象的方式来操作数据…

STM32CUBUMX配置RS485 modbus STM32(从机)亲测可用

———————————————————————————————————— ⏩ 大家好哇&#xff01;我是小光&#xff0c;嵌入式爱好者&#xff0c;一个想要成为系统架构师的大三学生。 ⏩最近在开发一个STM32H723ZGT6的板子&#xff0c;使用STM32CUBEMX做了很多驱动&#x…

怎么才能远程控制笔记本电脑?

为什么选择AnyViewer远程控制软件&#xff1f; 为什么AnyViewer是远程控制笔记本电脑软件的首选&#xff1f;以下是选择AnyViewer成为笔记本电脑远程控制软件的主要因素。 跨平台能力 AnyViewer作为一款跨平台远程控制软件&#xff0c;不仅可以用于从一台Windows电…

【BEV感知】3-BEV开源数据集

3-BEV开源数据集 1 KITTI1.1 KITTI数据怎么采集?1.2 KITTI数据规模有多大?1.3 KITTI标注了哪些目标?1.4 转换矩阵1.5 标签文件 2 nuScenes2.1 nuScenes Vs KITTI2.2 标注文件 1 KITTI KITTI 1.1 KITTI数据怎么采集? 通过车载相机、激光雷达等传感器采集。 只提供了相机正…

蓝桥杯上岸每日N题 第七期(小猫爬山)!!!

蓝桥杯上岸每日N题 第七期(小猫爬山)&#xff01;&#xff01;&#xff01; 同步收录 &#x1f447; 蓝桥杯上岸必背&#xff01;&#xff01;&#xff01;(第四期DFS) 大家好 我是寸铁&#x1f4aa; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &a…

(深度学习,自监督、半监督、无监督!!!)神经网络修改网络结构如何下手???

修改神经网络结构&#xff0c;我们可以根据这个进行添加&#xff1a; 卷积层&#xff08;Convolutional Layers&#xff09;&#xff1a;标准的卷积层用于提取特征并进行特征映射。 池化层&#xff08;Pooling Layers&#xff09;&#xff1a;用于减少特征图的空间维度&…

QT在label上透明绘图(二)

前面步骤参考前一篇文章 QT在label上透明绘图 一、给TransparentLabel类添加double transparency;变量&#xff0c; 二、ui添加doublespinbox&#xff0c;调整透明参数 void MainWindow::on_doubleSpinBox_valueChanged(double arg1) {transparentLabel->transparencyarg1;…