update pay ui
This commit is contained in:
@@ -37,9 +37,6 @@ import BrandLogo from './components/BrandLogo';
|
||||
import LoginButton from './components/LoginButton';
|
||||
import CalendarButton from './components/CalendarButton';
|
||||
|
||||
// Phase 2 优化: 使用 Redux 管理订阅数据
|
||||
import { useSubscription } from '../../hooks/useSubscription';
|
||||
|
||||
// Phase 3 优化: 提取的用户菜单组件
|
||||
import { DesktopUserMenu, TabletUserMenu } from './components/UserMenu';
|
||||
|
||||
@@ -125,13 +122,7 @@ export default function HomeNavbar() {
|
||||
}
|
||||
};
|
||||
|
||||
// Phase 2: 使用 Redux 订阅数据
|
||||
const {
|
||||
subscriptionInfo,
|
||||
isSubscriptionModalOpen,
|
||||
openSubscriptionModal,
|
||||
closeSubscriptionModal
|
||||
} = useSubscription();
|
||||
// Phase 2: 订阅数据管理已移至 UserMenu 子组件内部
|
||||
|
||||
// Phase 6: loadWatchlistQuotes, loadFollowingEvents, handleRemoveFromWatchlist,
|
||||
// handleUnfollowEvent 已移至自定义 Hooks 中,由各自组件内部管理
|
||||
|
||||
@@ -1,61 +1,51 @@
|
||||
// src/components/Navbars/components/UserMenu/DesktopUserMenu.js
|
||||
// 桌面版用户菜单 - 头像 + Tooltip + 订阅弹窗
|
||||
// 桌面版用户菜单 - 头像点击跳转到订阅页面
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { Tooltip, useColorModeValue } from '@chakra-ui/react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import UserAvatar from './UserAvatar';
|
||||
import SubscriptionModal from '../../../Subscription/SubscriptionModal';
|
||||
import { TooltipContent } from '../../../Subscription/CrownTooltip';
|
||||
import { useSubscription } from '../../../../hooks/useSubscription';
|
||||
|
||||
/**
|
||||
* 桌面版用户菜单组件
|
||||
* 大屏幕 (md+) 显示,头像点击打开订阅弹窗
|
||||
* 大屏幕 (md+) 显示,头像点击跳转到订阅页面
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Object} props.user - 用户信息
|
||||
*/
|
||||
const DesktopUserMenu = memo(({ user }) => {
|
||||
const {
|
||||
subscriptionInfo,
|
||||
isSubscriptionModalOpen,
|
||||
openSubscriptionModal,
|
||||
closeSubscriptionModal
|
||||
} = useSubscription();
|
||||
const navigate = useNavigate();
|
||||
const { subscriptionInfo } = useSubscription();
|
||||
|
||||
const tooltipBg = useColorModeValue('white', 'gray.800');
|
||||
const tooltipBorderColor = useColorModeValue('gray.200', 'gray.600');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
label={<TooltipContent subscriptionInfo={subscriptionInfo} />}
|
||||
placement="bottom"
|
||||
hasArrow
|
||||
bg={tooltipBg}
|
||||
borderRadius="lg"
|
||||
border="1px solid"
|
||||
borderColor={tooltipBorderColor}
|
||||
boxShadow="lg"
|
||||
p={3}
|
||||
>
|
||||
<span>
|
||||
<UserAvatar
|
||||
user={user}
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
onClick={openSubscriptionModal}
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
const handleAvatarClick = () => {
|
||||
navigate('/home/pages/account/subscription');
|
||||
};
|
||||
|
||||
{isSubscriptionModalOpen && (
|
||||
<SubscriptionModal
|
||||
isOpen={isSubscriptionModalOpen}
|
||||
onClose={closeSubscriptionModal}
|
||||
return (
|
||||
<Tooltip
|
||||
label={<TooltipContent subscriptionInfo={subscriptionInfo} />}
|
||||
placement="bottom"
|
||||
hasArrow
|
||||
bg={tooltipBg}
|
||||
borderRadius="lg"
|
||||
border="1px solid"
|
||||
borderColor={tooltipBorderColor}
|
||||
boxShadow="lg"
|
||||
p={3}
|
||||
>
|
||||
<span>
|
||||
<UserAvatar
|
||||
user={user}
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
onClick={handleAvatarClick}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
162
stress_test/README.md
Normal file
162
stress_test/README.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 压力测试指南
|
||||
|
||||
## 测试工具选择
|
||||
|
||||
| 工具 | 特点 | 适用场景 |
|
||||
|------|------|---------|
|
||||
| **Locust** | Python 编写,Web UI,可模拟复杂用户行为 | 综合压测(推荐) |
|
||||
| **wrk** | C 语言,性能极高,轻量级 | 极限性能测试 |
|
||||
| **ab** | Apache 自带,简单易用 | 快速测试 |
|
||||
| **k6** | 现代化,JavaScript 脚本 | CI/CD 集成 |
|
||||
|
||||
---
|
||||
|
||||
## 方案一:Locust(推荐)
|
||||
|
||||
### 安装
|
||||
```bash
|
||||
pip install locust
|
||||
```
|
||||
|
||||
### 启动 Web UI
|
||||
```bash
|
||||
cd stress_test
|
||||
locust -f locustfile.py --host=https://valuefrontier.cn
|
||||
```
|
||||
然后访问 http://localhost:8089
|
||||
|
||||
### 命令行模式(无 UI)
|
||||
```bash
|
||||
# 1000 用户,每秒增加 50 用户,运行 5 分钟
|
||||
locust -f locustfile.py --host=https://valuefrontier.cn \
|
||||
--users 1000 --spawn-rate 50 --run-time 5m --headless
|
||||
```
|
||||
|
||||
### 分布式压测(多机)
|
||||
```bash
|
||||
# 主节点
|
||||
locust -f locustfile.py --master --host=https://valuefrontier.cn
|
||||
|
||||
# 从节点(在其他机器执行)
|
||||
locust -f locustfile.py --worker --master-host=<主节点IP>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 方案二:wrk(极限性能测试)
|
||||
|
||||
### 安装(Linux)
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
apt-get install wrk
|
||||
|
||||
# CentOS/RHEL
|
||||
yum install wrk
|
||||
|
||||
# 或从源码编译
|
||||
git clone https://github.com/wg/wrk.git
|
||||
cd wrk && make
|
||||
```
|
||||
|
||||
### 基础测试
|
||||
```bash
|
||||
# 12 线程,400 连接,持续 30 秒
|
||||
wrk -t12 -c400 -d30s https://valuefrontier.cn/api/stocks
|
||||
|
||||
# 带 Lua 脚本的复杂测试
|
||||
wrk -t12 -c400 -d30s -s post.lua https://valuefrontier.cn/api/endpoint
|
||||
```
|
||||
|
||||
### 逐步加压测试
|
||||
```bash
|
||||
# 测试脚本
|
||||
for connections in 100 500 1000 2000 5000 10000; do
|
||||
echo "=== Testing with $connections connections ==="
|
||||
wrk -t12 -c$connections -d30s https://valuefrontier.cn/api/stocks
|
||||
sleep 10
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 方案三:ab(Apache Bench)
|
||||
|
||||
### 安装
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
apt-get install apache2-utils
|
||||
|
||||
# CentOS/RHEL
|
||||
yum install httpd-tools
|
||||
```
|
||||
|
||||
### 基础测试
|
||||
```bash
|
||||
# 1000 请求,100 并发
|
||||
ab -n 1000 -c 100 https://valuefrontier.cn/api/stocks
|
||||
|
||||
# 持续测试 60 秒
|
||||
ab -t 60 -c 100 https://valuefrontier.cn/api/stocks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 测试指标解读
|
||||
|
||||
### 关键指标
|
||||
|
||||
| 指标 | 含义 | 理想值 |
|
||||
|------|------|--------|
|
||||
| **RPS (Requests/sec)** | 每秒请求数 | 越高越好 |
|
||||
| **Latency (ms)** | 响应延迟 | P99 < 500ms |
|
||||
| **Error Rate** | 错误率 | < 1% |
|
||||
| **Throughput** | 吞吐量 | 越高越好 |
|
||||
|
||||
### 性能等级参考
|
||||
|
||||
| 等级 | RPS | 延迟 P99 | 说明 |
|
||||
|------|-----|---------|------|
|
||||
| 优秀 | > 10,000 | < 100ms | 极限性能 |
|
||||
| 良好 | 5,000-10,000 | < 200ms | 高性能 |
|
||||
| 合格 | 1,000-5,000 | < 500ms | 正常水平 |
|
||||
| 较差 | < 1,000 | > 500ms | 需要优化 |
|
||||
|
||||
---
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 测试前准备
|
||||
1. 确保服务器系统优化已完成(sysctl、limits.conf)
|
||||
2. 关闭不必要的日志输出(loglevel 改为 warning)
|
||||
3. 确保 Redis 正常运行
|
||||
4. 准备监控工具(htop、iotop、nethogs)
|
||||
|
||||
### 测试步骤
|
||||
1. **预热阶段**:100 用户,运行 2 分钟
|
||||
2. **正常负载**:500 用户,运行 5 分钟
|
||||
3. **高负载**:2000 用户,运行 5 分钟
|
||||
4. **极限测试**:5000+ 用户,运行 10 分钟
|
||||
5. **恢复测试**:降到 100 用户,观察系统恢复
|
||||
|
||||
### 监控命令
|
||||
```bash
|
||||
# 服务器上同时运行
|
||||
htop # CPU/内存监控
|
||||
iotop # 磁盘 IO 监控
|
||||
nethogs # 网络流量监控
|
||||
watch -n 1 'ss -s' # 连接数统计
|
||||
tail -f /var/log/nginx/error.log # Nginx 错误日志
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 预期性能
|
||||
|
||||
基于 110.42.32.207(48核 128GB)+ Eventlet 16 Workers 配置:
|
||||
|
||||
| 场景 | 预期 RPS | 预期并发 |
|
||||
|------|---------|---------|
|
||||
| 静态页面 | 50,000+ | 100,000+ |
|
||||
| API 请求 | 10,000-20,000 | 50,000+ |
|
||||
| WebSocket | - | 100,000+ 连接 |
|
||||
| 混合场景 | 5,000-10,000 | 30,000+ |
|
||||
150
stress_test/locustfile.py
Normal file
150
stress_test/locustfile.py
Normal file
@@ -0,0 +1,150 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Locust 压力测试脚本 - VF React 网站
|
||||
|
||||
使用方式:
|
||||
# 安装依赖
|
||||
pip install locust
|
||||
|
||||
# 启动 Web UI 模式(推荐)
|
||||
locust -f locustfile.py --host=https://valuefrontier.cn
|
||||
|
||||
# 命令行模式(无 UI)
|
||||
locust -f locustfile.py --host=https://valuefrontier.cn \
|
||||
--users 1000 --spawn-rate 50 --run-time 5m --headless
|
||||
|
||||
# 分布式模式(多机压测)
|
||||
# 主节点
|
||||
locust -f locustfile.py --master --host=https://valuefrontier.cn
|
||||
# 从节点(在其他机器运行)
|
||||
locust -f locustfile.py --worker --master-host=<master-ip>
|
||||
|
||||
Web UI 访问: http://localhost:8089
|
||||
"""
|
||||
|
||||
from locust import HttpUser, task, between, events
|
||||
import random
|
||||
import time
|
||||
import json
|
||||
|
||||
|
||||
class WebsiteUser(HttpUser):
|
||||
"""模拟普通网站用户行为"""
|
||||
|
||||
# 用户请求间隔(1-3秒,模拟真实用户)
|
||||
wait_time = between(1, 3)
|
||||
|
||||
def on_start(self):
|
||||
"""用户启动时执行(可用于登录)"""
|
||||
self.stock_codes = [
|
||||
"600000", "600036", "601318", "000001", "000002",
|
||||
"300750", "002594", "601888", "600519", "000858"
|
||||
]
|
||||
|
||||
@task(10)
|
||||
def view_homepage(self):
|
||||
"""访问首页(权重 10)"""
|
||||
self.client.get("/", name="首页")
|
||||
|
||||
@task(8)
|
||||
def get_stock_list(self):
|
||||
"""获取股票列表(权重 8)"""
|
||||
self.client.get("/api/stocks", name="股票列表 API")
|
||||
|
||||
@task(5)
|
||||
def get_stock_detail(self):
|
||||
"""获取个股详情(权重 5)"""
|
||||
code = random.choice(self.stock_codes)
|
||||
self.client.get(f"/api/stocks/{code}", name="个股详情 API")
|
||||
|
||||
@task(3)
|
||||
def get_community_events(self):
|
||||
"""获取社区事件(权重 3)"""
|
||||
self.client.get("/api/community/events", name="社区事件 API")
|
||||
|
||||
@task(2)
|
||||
def get_market_overview(self):
|
||||
"""获取市场概览(权重 2)"""
|
||||
self.client.get("/api/market/overview", name="市场概览 API")
|
||||
|
||||
|
||||
class APIUser(HttpUser):
|
||||
"""模拟 API 密集型用户(重度用户)"""
|
||||
|
||||
wait_time = between(0.5, 1.5)
|
||||
|
||||
def on_start(self):
|
||||
self.stock_codes = [
|
||||
"600000", "600036", "601318", "000001", "000002",
|
||||
"300750", "002594", "601888", "600519", "000858"
|
||||
]
|
||||
|
||||
@task(5)
|
||||
def get_realtime_quote(self):
|
||||
"""获取实时行情"""
|
||||
code = random.choice(self.stock_codes)
|
||||
self.client.get(f"/api/quote/{code}", name="实时行情 API")
|
||||
|
||||
@task(3)
|
||||
def get_kline_data(self):
|
||||
"""获取 K 线数据"""
|
||||
code = random.choice(self.stock_codes)
|
||||
self.client.get(f"/api/kline/{code}?period=day", name="K线数据 API")
|
||||
|
||||
@task(2)
|
||||
def get_concept_stocks(self):
|
||||
"""获取概念板块股票"""
|
||||
self.client.get("/api/concepts", name="概念板块 API")
|
||||
|
||||
|
||||
class AuthenticatedUser(HttpUser):
|
||||
"""模拟登录用户行为"""
|
||||
|
||||
wait_time = between(2, 5)
|
||||
|
||||
def on_start(self):
|
||||
"""登录获取 session"""
|
||||
# 如果有测试账号,可以在这里登录
|
||||
# response = self.client.post("/api/auth/login", json={
|
||||
# "username": "test_user",
|
||||
# "password": "test_pass"
|
||||
# })
|
||||
pass
|
||||
|
||||
@task(3)
|
||||
def view_portfolio(self):
|
||||
"""查看投资组合"""
|
||||
self.client.get("/api/portfolio", name="投资组合 API")
|
||||
|
||||
@task(2)
|
||||
def view_watchlist(self):
|
||||
"""查看自选股"""
|
||||
self.client.get("/api/watchlist", name="自选股 API")
|
||||
|
||||
|
||||
# ==================== 自定义统计 ====================
|
||||
|
||||
@events.request.add_listener
|
||||
def on_request(request_type, name, response_time, response_length, exception, **kwargs):
|
||||
"""请求完成后的回调(可用于自定义监控)"""
|
||||
if exception:
|
||||
print(f"❌ 请求失败: {name} - {exception}")
|
||||
elif response_time > 1000: # 超过 1 秒的慢请求
|
||||
print(f"⚠️ 慢请求: {name} - {response_time:.0f}ms")
|
||||
|
||||
|
||||
@events.test_start.add_listener
|
||||
def on_test_start(environment, **kwargs):
|
||||
"""测试开始时执行"""
|
||||
print("=" * 60)
|
||||
print("🚀 压力测试开始!")
|
||||
print(f" 目标: {environment.host}")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
@events.test_stop.add_listener
|
||||
def on_test_stop(environment, **kwargs):
|
||||
"""测试结束时执行"""
|
||||
print("=" * 60)
|
||||
print("🛑 压力测试结束!")
|
||||
print("=" * 60)
|
||||
46
stress_test/quick_test.sh
Normal file
46
stress_test/quick_test.sh
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
# 快速压力测试脚本
|
||||
|
||||
# 配置
|
||||
HOST="${1:-https://valuefrontier.cn}"
|
||||
DURATION="${2:-30s}"
|
||||
|
||||
echo "=============================================="
|
||||
echo "🚀 快速压力测试"
|
||||
echo " 目标: $HOST"
|
||||
echo " 时长: $DURATION"
|
||||
echo "=============================================="
|
||||
|
||||
# 检查 wrk 是否安装
|
||||
if ! command -v wrk &> /dev/null; then
|
||||
echo "❌ wrk 未安装,正在安装..."
|
||||
if command -v apt-get &> /dev/null; then
|
||||
apt-get update && apt-get install -y wrk
|
||||
elif command -v yum &> /dev/null; then
|
||||
yum install -y wrk
|
||||
else
|
||||
echo "请手动安装 wrk: https://github.com/wg/wrk"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 测试 1: 首页 (100 连接) ==="
|
||||
wrk -t4 -c100 -d$DURATION "$HOST/"
|
||||
|
||||
echo ""
|
||||
echo "=== 测试 2: API 接口 (500 连接) ==="
|
||||
wrk -t8 -c500 -d$DURATION "$HOST/api/stocks"
|
||||
|
||||
echo ""
|
||||
echo "=== 测试 3: 高并发 (2000 连接) ==="
|
||||
wrk -t12 -c2000 -d$DURATION "$HOST/api/stocks"
|
||||
|
||||
echo ""
|
||||
echo "=== 测试 4: 极限测试 (5000 连接) ==="
|
||||
wrk -t12 -c5000 -d$DURATION "$HOST/api/stocks"
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo "✅ 压力测试完成!"
|
||||
echo "=============================================="
|
||||
177
stress_test/websocket_test.py
Normal file
177
stress_test/websocket_test.py
Normal file
@@ -0,0 +1,177 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
WebSocket 压力测试脚本
|
||||
|
||||
使用方式:
|
||||
pip install python-socketio[client] websocket-client
|
||||
|
||||
# 测试 1000 个 WebSocket 连接
|
||||
python websocket_test.py --url wss://valuefrontier.cn --connections 1000
|
||||
|
||||
# 测试 5000 个连接,持续 5 分钟
|
||||
python websocket_test.py --url wss://valuefrontier.cn --connections 5000 --duration 300
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import time
|
||||
import statistics
|
||||
from datetime import datetime
|
||||
import socketio
|
||||
|
||||
# 统计数据
|
||||
stats = {
|
||||
"connected": 0,
|
||||
"disconnected": 0,
|
||||
"messages_received": 0,
|
||||
"errors": 0,
|
||||
"connect_times": [],
|
||||
}
|
||||
|
||||
|
||||
async def create_client(client_id, url, namespace="/"):
|
||||
"""创建单个 WebSocket 客户端"""
|
||||
sio = socketio.AsyncClient(
|
||||
reconnection=False,
|
||||
logger=False,
|
||||
engineio_logger=False
|
||||
)
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
@sio.event
|
||||
async def connect():
|
||||
connect_time = (time.time() - start_time) * 1000
|
||||
stats["connected"] += 1
|
||||
stats["connect_times"].append(connect_time)
|
||||
|
||||
@sio.event
|
||||
async def disconnect():
|
||||
stats["disconnected"] += 1
|
||||
|
||||
@sio.event
|
||||
async def message(data):
|
||||
stats["messages_received"] += 1
|
||||
|
||||
@sio.on("*")
|
||||
async def catch_all(event, data):
|
||||
stats["messages_received"] += 1
|
||||
|
||||
try:
|
||||
await sio.connect(url, namespaces=[namespace])
|
||||
return sio
|
||||
except Exception as e:
|
||||
stats["errors"] += 1
|
||||
return None
|
||||
|
||||
|
||||
async def run_test(url, num_connections, duration, batch_size=100):
|
||||
"""运行 WebSocket 压力测试"""
|
||||
print("=" * 60)
|
||||
print(f"🚀 WebSocket 压力测试")
|
||||
print(f" 目标: {url}")
|
||||
print(f" 连接数: {num_connections}")
|
||||
print(f" 持续时间: {duration} 秒")
|
||||
print(f" 批量大小: {batch_size}")
|
||||
print("=" * 60)
|
||||
|
||||
clients = []
|
||||
start_time = time.time()
|
||||
|
||||
# 分批创建连接
|
||||
print(f"\n📡 开始创建 {num_connections} 个 WebSocket 连接...")
|
||||
|
||||
for i in range(0, num_connections, batch_size):
|
||||
batch_end = min(i + batch_size, num_connections)
|
||||
batch_tasks = [
|
||||
create_client(j, url)
|
||||
for j in range(i, batch_end)
|
||||
]
|
||||
|
||||
batch_results = await asyncio.gather(*batch_tasks, return_exceptions=True)
|
||||
|
||||
for result in batch_results:
|
||||
if result and not isinstance(result, Exception):
|
||||
clients.append(result)
|
||||
|
||||
# 打印进度
|
||||
progress = (batch_end / num_connections) * 100
|
||||
print(f" 进度: {batch_end}/{num_connections} ({progress:.1f}%) - "
|
||||
f"成功: {stats['connected']}, 失败: {stats['errors']}")
|
||||
|
||||
# 短暂暂停,避免连接风暴
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
connect_duration = time.time() - start_time
|
||||
print(f"\n✅ 连接阶段完成!")
|
||||
print(f" 耗时: {connect_duration:.2f} 秒")
|
||||
print(f" 成功连接: {stats['connected']}")
|
||||
print(f" 连接失败: {stats['errors']}")
|
||||
|
||||
if stats["connect_times"]:
|
||||
print(f" 平均连接时间: {statistics.mean(stats['connect_times']):.2f} ms")
|
||||
print(f" P99 连接时间: {statistics.quantiles(stats['connect_times'], n=100)[98]:.2f} ms")
|
||||
|
||||
# 保持连接一段时间
|
||||
print(f"\n⏳ 保持连接 {duration} 秒...")
|
||||
messages_before = stats["messages_received"]
|
||||
|
||||
await asyncio.sleep(duration)
|
||||
|
||||
messages_after = stats["messages_received"]
|
||||
messages_during = messages_after - messages_before
|
||||
|
||||
# 断开所有连接
|
||||
print("\n📴 断开所有连接...")
|
||||
disconnect_tasks = [
|
||||
client.disconnect()
|
||||
for client in clients
|
||||
if client and client.connected
|
||||
]
|
||||
await asyncio.gather(*disconnect_tasks, return_exceptions=True)
|
||||
|
||||
# 打印最终统计
|
||||
total_duration = time.time() - start_time
|
||||
print("\n" + "=" * 60)
|
||||
print("📊 测试结果")
|
||||
print("=" * 60)
|
||||
print(f" 总耗时: {total_duration:.2f} 秒")
|
||||
print(f" 成功连接: {stats['connected']}")
|
||||
print(f" 连接失败: {stats['errors']}")
|
||||
print(f" 连接成功率: {(stats['connected'] / num_connections * 100):.2f}%")
|
||||
print(f" 收到消息数: {messages_during}")
|
||||
print(f" 消息速率: {(messages_during / duration):.2f} msg/s")
|
||||
|
||||
if stats["connect_times"]:
|
||||
print(f"\n 连接延迟统计:")
|
||||
print(f" 最小: {min(stats['connect_times']):.2f} ms")
|
||||
print(f" 最大: {max(stats['connect_times']):.2f} ms")
|
||||
print(f" 平均: {statistics.mean(stats['connect_times']):.2f} ms")
|
||||
print(f" 中位数: {statistics.median(stats['connect_times']):.2f} ms")
|
||||
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="WebSocket 压力测试")
|
||||
parser.add_argument("--url", default="wss://valuefrontier.cn",
|
||||
help="WebSocket URL (default: wss://valuefrontier.cn)")
|
||||
parser.add_argument("--connections", type=int, default=1000,
|
||||
help="并发连接数 (default: 1000)")
|
||||
parser.add_argument("--duration", type=int, default=60,
|
||||
help="测试持续时间(秒) (default: 60)")
|
||||
parser.add_argument("--batch", type=int, default=100,
|
||||
help="批量创建连接数 (default: 100)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
asyncio.run(run_test(
|
||||
url=args.url,
|
||||
num_connections=args.connections,
|
||||
duration=args.duration,
|
||||
batch_size=args.batch
|
||||
))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user