简介:这是一套面向PHP开发者与安全测试人员的开源短信压力测试工具源码,基于ThinkPHP框架构建,适用于学习接口压测原理、定制化短信轰炸防护验证及分布式脚本监控实践。资源共808个文件,主体为488个PHP核心逻辑与控制器文件,辅以75个GIF图标、42个JS交互脚本、27个HTML前端页面及2个SQL数据库脚本(含dkewl.sql),CSS/SCSS/LESS等样式文件保障后台管理界面可用性,整体压缩包仅2.68MB,轻量易部署。已有2913人学习下载,体现其在教学演示与实验环境搭建中的实用价值。读者可直接获取完整后台管理系统(默认路径/admins,账号admin/123456)、多服务器协同压测架构设计、计划任务Shell监控脚本、CDN白名单配置方案,以及API订单提交接口(/api/index/submit)的完整实现逻辑,涵盖从环境配置、数据库初始化到分布式压力分发的全流程技术细节。
1. LetSMS 是什么?不是“短信轰炸”,而是可审计、可管控的通信压力测试工具
LetSMS 这个名字常被误读为“发短信压测”或“短信轰炸工具”,但实际它是一套面向通信系统交付验收与容灾演练场景的开源短信压力测试框架——核心目标是模拟真实业务流量下的短信网关、SMSC、SP平台、运营商通道等链路在高并发、长连接、混合模板、异常编码等条件下的承载能力。它不提供任何绕过鉴权、伪造签名、突破频控的“黑产功能”,所有请求均需携带合法 AppKey、签名、模板ID 和手机号白名单,且默认禁用真实发送(仅 Mock 响应或对接沙箱环境)。适合通信中间件开发团队做单元压测、云短信服务商做 SLA 验证、政企客户做等保测评前的接口健壮性验证。如果你正在维护一套自建短信中台,或需要向甲方交付《接口性能测试报告》,LetSMS 提供的是可追溯、可复现、可嵌入 CI/CD 的标准化压测路径,而不是一段随手改改就能发垃圾短信的 Python 脚本。它的“全开源”体现在:协议解析层(CMPP/SMPP/HTTP API)、压测引擎(基于 asyncio + aiohttp 的协程调度)、数据驱动模块(支持 CSV/JSON/YAML 模板参数化)、结果聚合器(含 QPS、95% 延迟、失败率、通道抖动热力图)全部公开,无隐藏二进制模块,无域名绑定或 license 校验逻辑。
2. 从源码包解压到本地跑通:三步完成最小可用验证
LetSMS 的源码结构清晰,主干由core/(压测引擎)、drivers/(协议驱动)、templates/(短信模板库)、configs/(环境配置)和tests/(回归用例)组成。它不依赖 Docker 或 Kubernetes,纯 Python 实现,最低运行环境为 Python 3.8+,无需编译。以下步骤基于LetSMS全开源下载.zip解压后的目录执行,全程离线可用。
2.1 解压并确认目录结构与依赖声明
unzip "LetSMS全开源下载.zip" -d letsms-root cd letsms-root ls -F # 输出应包含:core/ drivers/ templates/ configs/ tests/ requirements.txt README.md run.py cat requirements.txt # 应看到:aiohttp==3.9.5, pydantic==2.7.1, python-dotenv==1.0.1, click==8.1.7, rich==13.7.1提示:该仓库未使用
pyproject.toml,而是传统requirements.txt管理依赖。rich用于终端彩色日志输出,click提供命令行参数解析,pydantic负责配置校验——这些都不是可选组件,漏装会导致run.py启动即报错。
2.2 初始化配置:用configs/example.env快速生成本地测试环境
LetSMS 不允许无配置运行。必须先复制示例配置并填写基础参数:
cp configs/example.env .env nano .env关键字段说明(仅需填这 4 项即可启动 Mock 模式):
| 字段 | 示例值 | 说明 |
|---|---|---|
SMS_DRIVER | mock | 必填。设为mock表示不发真实短信,只模拟响应;设为http则走标准 REST API;smpp和cmpp需额外安装对应驱动包 |
MOCK_DELAY_MS | 120 | mock 模式下每条请求的模拟延迟(毫秒),用于测试不同 RTT 场景 |
CONCURRENCY | 50 | 并发连接数,非请求数。LetSMS 使用连接池复用,单连接可串行发多条 |
DURATION_SEC | 30 | 压测总时长(秒),时间到自动停止,不按请求数截断 |
保存后执行:
python run.py --mode stress --target mock # 输出应含:[INFO] Starting stress test with 50 concurrent connections... # [INFO] Mock driver initialized: delay=120ms, jitter=±15% # [INFO] Running for 30 seconds... # [SUMMARY] Total: 1247 reqs, Success: 1247 (100.0%), Avg RTT: 124.3ms逻辑说明:
run.py是主入口,--mode stress指定压测模式(另有verify模式用于单条请求调试),--target mock显式指定驱动类型。即使.env中已设SMS_DRIVER=mock,命令行参数仍具更高优先级——这是 LetSMS 的设计原则:环境变量管默认值,命令行管本次意图。
2.3 查看压测报告:JSON 结构化输出与终端实时流
LetSMS 默认将结果同时输出到终端(rich渲染的动态表格)和reports/目录下的 JSON 文件:
ls -t reports/ # 最新文件如:stress_20240615_142238.json jq '.summary' reports/stress_*.json # 输出: # { # "total_requests": 1247, # "success_count": 1247, # "failure_count": 0, # "avg_rtt_ms": 124.32, # "p95_rtt_ms": 142.8, # "max_rtt_ms": 189.2, # "min_rtt_ms": 101.5, # "qps": 41.57, # "throughput_kbps": 3.21 # }参数说明:
throughput_kbps是按短信平均长度(默认 70 字节 UTF-8 编码)换算的字节吞吐量,非运营商计费维度的“条数”,而是网络链路实际承载能力指标。若需调整短信内容长度,修改templates/mock_template.json中的"content"字段即可——LetSMS 在压测中会严格按模板长度计算带宽占用。
3. 接入真实短信通道:HTTP 驱动配置与签名算法适配
LetSMS 的drivers/http.py是最常用的真实通道接入方式,它不预设任何厂商 SDK,而是通过可插拔的Signer类实现签名逻辑。这意味着你无需修改核心引擎,只需按规范实现一个签名类,即可对接阿里云、腾讯云、华为云、容联云等任意 HTTP 短信 API。
3.1 HTTP 驱动基础配置:URL、Headers 与超时控制
编辑.env,启用 HTTP 模式:
SMS_DRIVER=http HTTP_API_URL=https://sms-api.example.com/v2/send HTTP_TIMEOUT_SEC=15 HTTP_HEADERS={"Content-Type":"application/json","Authorization":"Bearer ${API_TOKEN}"}注意${API_TOKEN}是 dotenv 支持的变量引用语法,需在同文件中定义API_TOKEN=your_actual_token。HTTP_TIMEOUT_SEC是单次请求超时,非整个压测周期——这是防止某条请求卡死拖垮全局的关键参数。
3.2 实现自定义 Signer:以 HMAC-SHA256 签名为例
LetSMS 要求所有签名类继承core.signers.BaseSigner,并实现sign(self, params: dict) -> dict方法。假设某厂商要求对phone,template_id,params三个字段按字典序拼接后 HMAC-SHA256:
# drivers/custom_signer.py from core.signers import BaseSigner import hmac import hashlib import json class HmacSha256Signer(BaseSigner): def __init__(self, secret_key: str): self.secret_key = secret_key.encode() def sign(self, params: dict) -> dict: # 按 key 字典序排序并拼接 sorted_items = sorted(params.items()) payload = "&".join([f"{k}={v}" for k, v in sorted_items]) signature = hmac.new( self.secret_key, payload.encode(), hashlib.sha256 ).hexdigest() return {"signature": signature}然后在.env中声明:
HTTP_SIGNER_CLASS=drivers.custom_signer:HmacSha256Signer HTTP_SIGNER_INIT={"secret_key": "your_vendor_secret"}逻辑说明:
HTTP_SIGNER_CLASS格式为模块路径:类名,HTTP_SIGNER_INIT是 JSON 字符串,用于初始化 signer 实例。LetSMS 在加载驱动时会自动反射调用,无需修改run.py。这种设计让签名逻辑完全解耦,避免因厂商升级签名规则而被迫更新整个压测框架。
3.3 模板参数化:用 JSON Schema 校验动态内容
真实短信常含变量,如【{company}】您的验证码是{code},{expire}分钟内有效。LetSMS 要求模板文件(如templates/aliyun_vcode.json)必须符合预设 Schema:
{ "template_id": "SMS_123456789", "sign_name": "某某科技", "content": "【{company}】您的验证码是{code},{expire}分钟内有效", "params_schema": { "company": {"type": "string", "minLength": 2, "maxLength": 12}, "code": {"type": "string", "pattern": "^[0-9]{6}$"}, "expire": {"type": "integer", "minimum": 1, "maximum": 30} } }压测时,LetSMS 会根据params_schema自动生成合法参数组合,并在发送前校验——若code字段传入"abc123",则直接拒绝该请求并计入validation_error统计,而非发给上游导致扣费。这是防止因参数错误引发资损的核心防护。
4. 常见问题排查:5 条血泪经验总结
LetSMS 开源包虽小,但在真实环境中部署时,有 5 类问题高频出现,几乎每个首次使用者都会踩一次。以下是现象、根因与解法,按发生概率倒序排列:
4.1 现象:压测启动后 QPS 为 0,日志显示Connection refused
原因:.env中HTTP_API_URL末尾多了斜杠(如https://api.xxx.com//v2/send),部分厂商 Nginx 配置对双斜杠返回 400,而 LetSMS 默认将 4xx 视为客户端错误不重试。
解决:检查 URL 是否符合 RFC 3986,用curl -I $HTTP_API_URL验证状态码;或在.env中添加HTTP_RETRY_CODES=400,401,403显式开启重试。
4.2 现象:Mock 模式下延迟恒为 0,p95_rtt_ms始终等于min_rtt_ms
原因:MOCK_DELAY_MS设为整数,但代码中实际使用random.uniform(delay * 0.85, delay * 1.15)计算抖动,若delay=0,则区间为(0,0),uniform 抛出ValueError后降级为固定 0。
解决:MOCK_DELAY_MS必须 ≥ 1;若需测试零延迟场景,设为1并在报告中忽略抖动项。
4.3 现象:并发数设为 100,但htop显示 Python 进程 CPU 占用仅 120%,远未打满
原因:LetSMS 默认启用aiohttp.TCPConnector(limit_per_host=30),即单 host 最大连接数为 30,超出的请求排队等待空闲连接,形成隐式瓶颈。
解决:在.env中添加HTTP_CONN_LIMIT_PER_HOST=100,或根据目标服务实际连接池大小调整。
4.4 现象:使用smpp驱动时报错ModuleNotFoundError: No module named 'pysmpp'
原因:SMPP/CMPP 驱动属于可选依赖,默认不安装。requirements.txt中相关包被注释掉。
解决:执行pip install pysmpp==4.1.0(注意版本锁定,pysmpp 4.2+ 有 asyncio 兼容问题);若需 CMPP,则pip install cmpp==1.0.2。
4.5 现象:压测结束后reports/下无 JSON 文件,终端只显示Summary saved to ...但路径不存在
原因:reports/目录权限不足(如挂载在 NFS 上且 umask 为 077),或磁盘空间不足导致open()失败,异常被静默吞没。
解决:手动创建mkdir -p reports并chmod 755 reports;检查df -h;启用 debug 日志:python run.py --log-level debug查看文件写入栈。
5. 进阶技巧:用verify模式做接口契约测试与自动化回归
LetSMS 的--mode verify模式常被低估,但它才是日常开发中最实用的功能——它不是压测,而是单请求契约验证器。你可以把它当作 Postman 的命令行替代品,但优势在于:能批量跑、能校验响应 Schema、能集成进 pytest。
5.1 构建可复用的验证用例集
在tests/verify_cases/下新建aliyun_login.json:
{ "name": "阿里云登录短信发送", "driver": "http", "template": "templates/aliyun_login.json", "params": { "user_id": "U123456", "login_time": "2024-06-15T15:30:00Z" }, "expected_status": 200, "expected_fields": ["RequestId", "Code", "Message"], "response_schema": { "type": "object", "properties": { "Code": {"const": "OK"}, "Message": {"type": "string"}, "RequestId": {"type": "string", "minLength": 16} } } }执行验证:
python run.py --mode verify --case tests/verify_cases/aliyun_login.json # 输出:✅ PASS: 阿里云登录短信发送 (200 OK, 124ms) # ✅ Field 'RequestId' present # ✅ Field 'Code' = 'OK' # ✅ Response matches schema价值点:每次上线前,用
find tests/verify_cases -name "*.json" | xargs -I{} python run.py --mode verify --case {}批量跑全量用例,5 秒内完成所有短信接口的冒烟测试。比人工点 Postman 快 10 倍,且结果可存档比对。
5.2 与 pytest 集成:把验证用例变成单元测试
新建test_sms_api.py:
# test_sms_api.py import json import subprocess import pytest def load_verify_case(path): with open(path) as f: return json.load(f) @pytest.mark.parametrize("case_path", [ "tests/verify_cases/aliyun_login.json", "tests/verify_cases/tencent_reset.json", ]) def test_sms_contract(case_path): result = subprocess.run( ["python", "run.py", "--mode", "verify", "--case", case_path], capture_output=True, text=True, timeout=30 ) assert result.returncode == 0, f"Verify failed: {result.stderr}" assert "✅ PASS" in result.stdout, f"Unexpected output: {result.stdout}"运行:pytest test_sms_api.py -v。CI 流水线中加入此步骤,即可确保每次 PR 都不破坏短信接口契约。
5.3 定制化报告:用--report-format html生成交付文档
LetSMS 内置 HTML 报告生成器,专为交付给甲方设计:
python run.py --mode stress --target http --report-format html --report-output delivery_report.html生成的delivery_report.html包含:
- 动态折线图(QPS / 延迟随时间变化)
- 通道成功率热力图(按小时/模板维度)
- 错误码分布饼图(400/401/429/500 等占比)
- 原始 JSON 数据下载链接(供甲方自行解析)
- 签名算法说明附录(自动提取
HTTP_SIGNER_CLASS文档字符串)
我的习惯:每次交付前,我会把
delivery_report.html和reports/stress_*.json打包,命名为SMS_PERF_REPORT_20240615_v2.3.zip,并附上CHANGELOG.md记录本次压测相比上次的改进点(如“新增 SMPP 3.4 协议兼容性测试”)。甲方技术负责人打开 HTML 就能看到结论,不用翻日志——这省下的 2 小时解释时间,足够我喝完两杯咖啡。希望帮到你。
本文还有配套的精品资源,点击获取