commit 59f99637e9ef2c1824294e90943de8ac241643b9 Author: cheliangzhao Date: Fri Feb 13 18:28:54 2026 +0800 Add Docker-based skills batch installer with multiple execution strategies Includes PowerShell scripts for batch execution with different patterns: - Basic parallel execution (run-docker-batch.ps1) - Scheduled execution with delays (run-docker-batch-scheduled.ps1) - Wave-based execution with progressive concurrency (run-docker-waves.ps1) Each script supports anti-bot testing by generating unique client identities (device ID, UUID, user agent) for each container instance. diff --git a/skills-batch-installer/Dockerfile b/skills-batch-installer/Dockerfile new file mode 100644 index 0000000..72035a2 --- /dev/null +++ b/skills-batch-installer/Dockerfile @@ -0,0 +1,46 @@ +# Use Node.js 18+ as base image +FROM node:18-alpine + +# Set working directory +WORKDIR /app + +# Install required tools +RUN apk add --no-cache \ + git \ + curl \ + openssl \ + bash \ + bc \ + jq \ + wget \ + ca-certificates + +# Copy scripts +COPY install-skills-quick.sh /app/ +COPY http-load-test.sh /app/ + +# Make scripts executable +RUN chmod +x /app/install-skills-quick.sh /app/http-load-test.sh + +# Create logs directory +RUN mkdir -p /app/logs + +# Accept environment variables for anti-bot testing (different client identities) +ENV DEVICE_ID="" +ENV CLIENT_UUID="" +ENV USER_AGENT="" +ENV TARGET_URL="" +ENV REQUEST_METHOD="GET" +ENV TOTAL_REQUESTS="10" +ENV CONCURRENT_REQUESTS="2" +ENV REQUEST_DELAY_MIN="0.5" +ENV REQUEST_DELAY_MAX="2" +ENV TEST_TYPE="skills" + +# Run the appropriate script based on TEST_TYPE +CMD ["sh", "-c", "if [ \"$TEST_TYPE\" = \"http\" ]; then \ + /bin/bash /app/http-load-test.sh; \ + else \ + DEVICE_ID=$DEVICE_ID CLIENT_UUID=$CLIENT_UUID USER_AGENT=$USER_AGENT /bin/bash /app/install-skills-quick.sh; \ + fi && cat /app/logs/*.log 2>/dev/null | tail -100"] + diff --git a/skills-batch-installer/README.md b/skills-batch-installer/README.md new file mode 100644 index 0000000..0e2b4f7 --- /dev/null +++ b/skills-batch-installer/README.md @@ -0,0 +1,190 @@ +# Docker 批量执行脚本 + +这个文件夹包含三个 PowerShell 脚本,用于 Docker 容器的批量执行和分布式反爬虫负载测试。 + +## 脚本概览 + +### 1. run-docker-batch.ps1 - 基础批量执行 + +最简单的批量执行脚本,同时运行指定数量的 Docker 容器。 + +**特性:** +- 指定并发数量(同时运行多少个容器) +- 指定总运行次数 +- 每个容器获得唯一的客户端身份信息 +- 实时进度显示 + +**参数:** +```powershell +-Total # 总运行次数,默认值:20 +-Concurrency # 并发数量,默认值:4 +-Image # Docker 镜像名称,默认值:"skills-installer" +``` + +**使用示例:** +```powershell +# 运行 20 次,4 个并发 +.\run-docker-batch.ps1 + +# 运行 50 次,8 个并发,使用自定义镜像 +.\run-docker-batch.ps1 -Total 50 -Concurrency 8 -Image "my-image" + +# 快速测试:运行 2 次,1 个并发 +.\run-docker-batch.ps1 -Total 2 -Concurrency 1 +``` + +--- + +### 2. run-docker-batch-scheduled.ps1 - 批次执行(带延迟) + +在基础批量执行的基础上增加了批次间延迟功能,模拟真实用户访问模式。 + +**特性:** +- 基础的并发执行 +- 完成一批任务后,等待随机秒数 +- 可配置的延迟时间范围 +- 可选的随机化延迟 +- 完整的统计数据(成功率、执行时间) + +**参数:** +```powershell +-Total # 总运行次数,默认值:20 +-Concurrency # 并发数量,默认值:4 +-MinDelaySeconds # 最小延迟秒数,默认值:1 +-MaxDelaySeconds # 最大延迟秒数,默认值:30 +-Image # Docker 镜像名称,默认值:"skills-installer" +-Randomize # 是否随机化延迟,默认值:$true +``` + +**使用示例:** +```powershell +# 使用默认参数 +.\run-docker-batch-scheduled.ps1 + +# 100 次运行,4 个并发,5-15 秒延迟 +.\run-docker-batch-scheduled.ps1 -Total 100 -Concurrency 4 -MinDelaySeconds 5 -MaxDelaySeconds 15 + +# 不随机化延迟,固定为 10 秒间隔 +.\run-docker-batch-scheduled.ps1 -Total 50 -Concurrency 5 -MinDelaySeconds 10 -MaxDelaySeconds 10 -Randomize $false +``` + +--- + +### 3. run-docker-waves.ps1 - 波次执行(动态并发) + +最高级的脚本,分批处理任务,逐波增加并发数量,模拟渐进式的负载增加。 + +**特性:** +- 波次执行:分批完成任务 +- 动态并发:每个波次增加并发数 +- 从低到高的并发扩展 +- 波次间的随机延迟 +- 详细的性能统计数据 + +**参数:** +```powershell +-TotalRuns # 总运行次数,默认值:100 +-InitialConcurrency # 初始并发数,默认值:2 +-MaxConcurrency # 最大并发数,默认值:8 +-MinWaveDelaySeconds # 波次间最小延迟,默认值:5 +-MaxWaveDelaySeconds # 波次间最大延迟,默认值:60 +-Image # Docker 镜像名称,默认值:"skills-installer" +``` + +**使用示例:** +```powershell +# 使用默认参数 +.\run-docker-waves.ps1 + +# 200 次运行,初始 2 并发,最大 10 并发 +.\run-docker-waves.ps1 -TotalRuns 200 -InitialConcurrency 2 -MaxConcurrency 10 + +# 快速测试:30 次运行,初始 1 并发,最大 3 并发,短延迟 +.\run-docker-waves.ps1 -TotalRuns 30 -InitialConcurrency 1 -MaxConcurrency 3 -MinWaveDelaySeconds 1 -MaxWaveDelaySeconds 5 +``` + +--- + +## 使用场景 + +| 场景 | 推荐脚本 | 原因 | +|------|---------|------| +| 简单的并发测试 | `run-docker-batch.ps1` | 最轻量级,无延迟 | +| 模拟真实用户访问 | `run-docker-batch-scheduled.ps1` | 批次间有停顿,更真实 | +| 压力测试(渐进式负载) | `run-docker-waves.ps1` | 逐波增加压力,观察系统应对 | +| 反爬虫绕过测试 | 任何脚本 | 所有脚本都生成唯一的客户端身份 | + +## 运行前的准备 + +1. **安装 PowerShell**:需要 PowerShell 5.1 或更高版本 +2. **安装 Docker**:需要已安装 Docker 并能正常运行 +3. **准备镜像**:确保指定的 Docker 镜像已存在 + ```powershell + docker images # 查看已有镜像 + ``` + +## 执行权限 + +如果脚本无法执行,可能需要调整执行策略: + +```powershell +# 仅对当前进程生效 +Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process + +# 对当前用户生效 +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +``` + +## 客户端身份信息 + +每个容器运行时会接收以下环境变量,用于模拟不同的客户端: + +- `DEVICE_ID`:唯一的设备标识符(GUID) +- `CLIENT_UUID`:唯一的客户端标识符(GUID) +- `USER_AGENT`:随机分配的浏览器标识符 + +这些信息有助于绕过反爬虫检测。 + +## 输出说明 + +### 颜色编码 +- 🟢 **绿色**:成功、完成状态 +- 🔵 **青色**:部分标题、分隔符 +- 🟡 **黄色**:进度、配置信息、警告 +- 🔴 **红色**:失败、错误 +- 🟣 **紫色**:波次标记(仅 wave 脚本) + +### 统计数据 + +脚本完成后会显示: +- 总运行次数 +- 成功/失败的运行数 +- 成功率百分比 +- 总执行时间 +- 平均每次执行时间 + +## 故障排除 + +| 问题 | 解决方案 | +|------|---------| +| "找不到 Docker 命令" | 确保 Docker 已安装且在 PATH 中 | +| 容器启动失败 | 检查镜像名称是否正确:`docker images` | +| 权限被拒绝 | 使用管理员权限运行 PowerShell | +| 脚本无法执行 | 调整执行策略(见上面的执行权限部分) | + +## 快速开始 + +```powershell +# 进入脚本目录 +cd scripts + +# 测试运行(2 次,1 并发) +.\run-docker-batch.ps1 -Total 2 -Concurrency 1 -Image "your-image-name" + +# 实际运行 +.\run-docker-batch-scheduled.ps1 -Total 50 -Concurrency 4 -Image "your-image-name" +``` + +--- + +**更多信息**:查看根目录的 `AGENTS.md` 了解代码风格和开发指南。 diff --git a/skills-batch-installer/install-skills-quick.ps1 b/skills-batch-installer/install-skills-quick.ps1 new file mode 100644 index 0000000..59a062f --- /dev/null +++ b/skills-batch-installer/install-skills-quick.ps1 @@ -0,0 +1,111 @@ +# PowerShell script: Install skills with configurable interval in random temp directory + +# Configuration +$skillRepo = "https://github.com/fadinglight9291117/arkts_skills" +$skills = @("harmonyos-build-deploy", "arkts-development") +$intervalSeconds = 10 # 0 means execute immediately, otherwise wait (in seconds) +$maxRuns = 1 + +Write-Host "================================================================" -ForegroundColor Cyan +Write-Host "Skills Auto Installation Script (Temp Directory)" -ForegroundColor Green +Write-Host "================================================================" -ForegroundColor Cyan +Write-Host "Repository: $skillRepo" -ForegroundColor Yellow +Write-Host "Skills: $($skills -join ', ')" -ForegroundColor Yellow +if ($intervalSeconds -eq 0) { + Write-Host "Interval: Immediate (no wait)" -ForegroundColor Yellow +} else { + Write-Host "Interval: $intervalSeconds seconds" -ForegroundColor Yellow +} +Write-Host "Max Runs: $maxRuns" -ForegroundColor Yellow +Write-Host "Execution: In random temp directory, deleted after completion" -ForegroundColor Yellow +Write-Host "================================================================`n" -ForegroundColor Cyan + +# Counter +$runCount = 0 + +# Loop +while ($true) { + $runCount++ + + # Check if max runs reached + if ($maxRuns -and $runCount -gt $maxRuns) { + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Write-Host "[$timestamp] Completed $maxRuns runs. Script exits." -ForegroundColor Cyan + break + } + + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Write-Host "[$timestamp] [Run $runCount/$maxRuns] Starting skill installation..." -ForegroundColor Green + + try { + # Create random temp directory + $randomId = [System.Guid]::NewGuid().ToString().Substring(0, 8) + $tempDir = Join-Path -Path $env:TEMP -ChildPath "skills_test_$randomId" + + Write-Host " -> Creating temp directory: $tempDir" -ForegroundColor Cyan + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + + # Change to temp directory + Push-Location $tempDir + Write-Host " OK Switched to temp directory" -ForegroundColor Green + + # Install first skill + Write-Host " -> Installing: harmonyos-build-deploy" -ForegroundColor Cyan + & npx skills add $skillRepo --skill "harmonyos-build-deploy" --yes 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host " OK harmonyos-build-deploy installed" -ForegroundColor Green + } else { + Write-Host " ERROR harmonyos-build-deploy failed" -ForegroundColor Red + } + + # Install second skill + Write-Host " -> Installing: arkts-development" -ForegroundColor Cyan + & npx skills add $skillRepo --skill "arkts-development" --yes 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host " OK arkts-development installed" -ForegroundColor Green + } else { + Write-Host " ERROR arkts-development failed" -ForegroundColor Red + } + + # Remove skills + Write-Host " -> Removing skills" -ForegroundColor Cyan + foreach ($skill in $skills) { + & npx skills remove $skill --yes 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0 -or $LASTEXITCODE -eq 1) { + Write-Host " OK $skill removed" -ForegroundColor Green + } else { + Write-Host " ERROR $skill removal failed" -ForegroundColor Red + } + } + + # Return to original directory + Pop-Location + Write-Host " -> Returned to original directory" -ForegroundColor Cyan + + # Delete temp directory + Write-Host " -> Deleting temp directory: $tempDir" -ForegroundColor Cyan + Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue + if (-not (Test-Path $tempDir)) { + Write-Host " OK Temp directory deleted" -ForegroundColor Green + } else { + Write-Host " WARNING Temp directory still exists" -ForegroundColor Yellow + } + + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Write-Host "[$timestamp] Run $runCount/$maxRuns completed`n" -ForegroundColor Green + + } catch { + Write-Host " ERROR: $_" -ForegroundColor Red + # Ensure we return to original directory even if error occurs + Pop-Location -ErrorAction SilentlyContinue + } + + # Wait for interval before next run (except on last run) + if ($runCount -lt $maxRuns -and $intervalSeconds -gt 0) { + Write-Host "Waiting $intervalSeconds seconds before next run..." -ForegroundColor Yellow + Start-Sleep -Seconds $intervalSeconds + } +} + +$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" +Write-Host "`n[$timestamp] All tasks completed!" -ForegroundColor Green diff --git a/skills-batch-installer/run-docker-batch-scheduled.ps1 b/skills-batch-installer/run-docker-batch-scheduled.ps1 new file mode 100644 index 0000000..37c3721 --- /dev/null +++ b/skills-batch-installer/run-docker-batch-scheduled.ps1 @@ -0,0 +1,177 @@ +param( + [int]$Total = 20, + [int]$Concurrency = 4, + [int]$MinDelaySeconds = 1, + [int]$MaxDelaySeconds = 30, + [string]$Image = "skills-installer", + [bool]$Randomize = $true +) + +if ($Total -lt 1) { + Write-Error "Total must be >= 1" + exit 1 +} +if ($Concurrency -lt 1) { + Write-Error "Concurrency must be >= 1" + exit 1 +} +if ($MinDelaySeconds -lt 0 -or $MaxDelaySeconds -lt 0) { + Write-Error "Delay values must be >= 0" + exit 1 +} +if ($MinDelaySeconds -gt $MaxDelaySeconds) { + Write-Error "MinDelaySeconds must be <= MaxDelaySeconds" + exit 1 +} + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "Distributed Anti-Bot Load Testing" -ForegroundColor Green +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "Total Runs: $Total" -ForegroundColor Yellow +Write-Host "Concurrency: $Concurrency" -ForegroundColor Yellow +Write-Host "Delay Range: $MinDelaySeconds-$MaxDelaySeconds seconds" -ForegroundColor Yellow +Write-Host "Randomization: $Randomize" -ForegroundColor Yellow +Write-Host "Image: $Image" -ForegroundColor Yellow +Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +$started = 0 +$completed = 0 +[System.Collections.ArrayList]$jobs = @() +[System.Collections.ArrayList]$completedRuns = @() + +function Start-RunJob { + param([int]$Index, [string]$ImageName) + + # Generate unique client identifiers for this run + $deviceId = [System.Guid]::NewGuid().ToString() + $clientUuid = [System.Guid]::NewGuid().ToString() + $userAgents = @( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36", + "Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15", + "Mozilla/5.0 (Android 11; SM-G991B) AppleWebKit/537.36", + "Mozilla/5.0 (iPad; CPU OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15" + ) + $userAgent = $userAgents[$Index % $userAgents.Count] + + Start-Job -Name "run-$Index" -ScriptBlock { + param($i, $img, $devId, $clUuid, $ua) + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Write-Host "[$timestamp] Run $i launched [Device: $($devId.Substring(0,8))... UUID: $($clUuid.Substring(0,8))...]" -ForegroundColor Green + + # Run container with different client identities + & docker run --rm ` + -e "DEVICE_ID=$devId" ` + -e "CLIENT_UUID=$clUuid" ` + -e "USER_AGENT=$ua" ` + $img 2>&1 | Out-Null + + $exitCode = $LASTEXITCODE + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + if ($exitCode -eq 0) { + Write-Host "[$timestamp] Run $i completed successfully" -ForegroundColor Green + } else { + Write-Host "[$timestamp] Run $i failed (exit code: $exitCode)" -ForegroundColor Red + } + return @{ + Index = $i + ExitCode = $exitCode + Timestamp = $timestamp + DeviceId = $devId + } + } -ArgumentList $Index, $ImageName, $deviceId, $clientUuid, $userAgent +} + +function Get-RandomDelay { + param([int]$Min, [int]$Max) + if ($Min -eq 0 -and $Max -eq 0) { + return 0 + } + $random = Get-Random -Minimum $Min -Maximum ($Max + 1) + return $random +} + +$batchCount = 0 +$startTime = Get-Date + +while ($completed -lt $Total) { + # Start new jobs if we have capacity + while ($started -lt $Total -and $jobs.Count -lt $Concurrency) { + $started++ + $job = Start-RunJob -Index $started -ImageName $Image + $jobs.Add($job) | Out-Null + } + + # Wait for at least one job to complete + if ($jobs.Count -gt 0) { + $done = Wait-Job -Job $jobs[0] + $result = Receive-Job -Job $done + Remove-Job -Job $done + $jobs.RemoveAt(0) + $completed++ + + $completedRuns.Add($result) | Out-Null + Write-Host "Progress: $completed/$Total completed" -ForegroundColor Yellow + + # If more runs remain and we need to introduce delay + if ($completed -lt $Total -and $completed % $Concurrency -eq 0) { + if ($Randomize) { + $delaySeconds = Get-RandomDelay -Min $MinDelaySeconds -Max $MaxDelaySeconds + } else { + $delaySeconds = $MinDelaySeconds + } + + if ($delaySeconds -gt 0) { + $batchCount++ + $nextBatchTime = (Get-Date).AddSeconds($delaySeconds) + Write-Host "" + Write-Host "========================================" -ForegroundColor Cyan + Write-Host "Batch $batchCount completed. Waiting $delaySeconds seconds..." -ForegroundColor Yellow + Write-Host "Next batch will start at: $($nextBatchTime.ToString('HH:mm:ss'))" -ForegroundColor Yellow + Write-Host "========================================" -ForegroundColor Cyan + Write-Host "" + + Start-Sleep -Seconds $delaySeconds + } + } + } +} + +# Wait for any remaining jobs +while ($jobs.Count -gt 0) { + $done = Wait-Job -Job $jobs[0] + $result = Receive-Job -Job $done + Remove-Job -Job $done + $jobs.RemoveAt(0) + $completed++ +} + +$endTime = Get-Date +$totalDuration = $endTime - $startTime + +Write-Host "" +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "Test Complete!" -ForegroundColor Green +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "Total Runs: $Total" -ForegroundColor Yellow +Write-Host "Batches: $batchCount" -ForegroundColor Yellow +Write-Host "Start Time: $($startTime.ToString('yyyy-MM-dd HH:mm:ss'))" -ForegroundColor Yellow +Write-Host "End Time: $($endTime.ToString('yyyy-MM-dd HH:mm:ss'))" -ForegroundColor Yellow +$totalSeconds = [math]::Round($totalDuration.TotalSeconds, 2) +Write-Host "Total Duration: $totalSeconds seconds" -ForegroundColor Yellow +Write-Host "Average Time per Run: $([math]::Round($totalDuration.TotalSeconds / $Total, 2)) seconds" -ForegroundColor Yellow +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# Summary statistics +$successCount = ($completedRuns | Where-Object { $_.ExitCode -eq 0 }).Count +$failureCount = ($completedRuns | Where-Object { $_.ExitCode -ne 0 }).Count +$successPercent = [math]::Round($successCount / $Total * 100, 2) + +Write-Host "Success Rate: $successCount/$Total ($successPercent%)" -ForegroundColor Green +if ($failureCount -gt 0) { + Write-Host "Failures: $failureCount" -ForegroundColor Red +} diff --git a/skills-batch-installer/run-docker-batch.ps1 b/skills-batch-installer/run-docker-batch.ps1 new file mode 100644 index 0000000..32ead41 --- /dev/null +++ b/skills-batch-installer/run-docker-batch.ps1 @@ -0,0 +1,79 @@ +param( + [int]$Total = 20, + [int]$Concurrency = 4, + [string]$Image = "skills-installer" +) + +if ($Total -lt 1) { + Write-Error "Total must be >= 1" + exit 1 +} +if ($Concurrency -lt 1) { + Write-Error "Concurrency must be >= 1" + exit 1 +} + +Write-Host "Starting $Total runs with concurrency $Concurrency" -ForegroundColor Cyan +Write-Host "Image: $Image" -ForegroundColor Cyan +Write-Host "Each container will use a different client identity for anti-bot testing" -ForegroundColor Yellow + +$started = 0 +$completed = 0 +[System.Collections.ArrayList]$jobs = @() + +function Start-RunJob { + param([int]$Index, [string]$ImageName) + + # Generate unique client identifiers for this run + $deviceId = [System.Guid]::NewGuid().ToString() + $clientUuid = [System.Guid]::NewGuid().ToString() + $userAgents = @( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36", + "Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15", + "Mozilla/5.0 (Android 11; SM-G991B) AppleWebKit/537.36" + ) + $userAgent = $userAgents[$Index % $userAgents.Count] + + Start-Job -Name "run-$Index" -ScriptBlock { + param($i, $img, $devId, $clUuid, $ua) + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Write-Host "[$timestamp] Run $i start [Device: $($devId.Substring(0,8))... UUID: $($clUuid.Substring(0,8))...]" -ForegroundColor Green + + # Run container with different client identities + & docker run --rm ` + -e "DEVICE_ID=$devId" ` + -e "CLIENT_UUID=$clUuid" ` + -e "USER_AGENT=$ua" ` + $img 2>&1 + + $exitCode = $LASTEXITCODE + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + if ($exitCode -eq 0) { + Write-Host "[$timestamp] Run $i success" -ForegroundColor Green + } else { + Write-Host "[$timestamp] Run $i failed (exit $exitCode)" -ForegroundColor Red + } + return $exitCode + } -ArgumentList $Index, $ImageName, $deviceId, $clientUuid, $userAgent +} + +while ($completed -lt $Total) { + while ($started -lt $Total -and $jobs.Count -lt $Concurrency) { + $started++ + $job = Start-RunJob -Index $started -ImageName $Image + $jobs.Add($job) | Out-Null + } + + if ($jobs.Count -gt 0) { + $done = Wait-Job -Job $jobs[0] + Receive-Job -Job $done | Out-Host + Remove-Job -Job $done + $jobs.RemoveAt(0) + $completed++ + Write-Host "Progress: $completed/$Total completed" -ForegroundColor Yellow + } +} + +Write-Host "All runs completed with different client identities." -ForegroundColor Green diff --git a/skills-batch-installer/run-docker-waves.ps1 b/skills-batch-installer/run-docker-waves.ps1 new file mode 100644 index 0000000..7343a57 --- /dev/null +++ b/skills-batch-installer/run-docker-waves.ps1 @@ -0,0 +1,133 @@ +param( + [int]$TotalRuns = 100, + [int]$InitialConcurrency = 2, + [int]$MaxConcurrency = 8, + [int]$MinWaveDelaySeconds = 5, + [int]$MaxWaveDelaySeconds = 60, + [string]$Image = "skills-installer" +) + +Write-Host "" +Write-Host "Wave-Based Distributed Anti-Bot Load Testing" -ForegroundColor Cyan +Write-Host "==============================================" -ForegroundColor Cyan +Write-Host "" +Write-Host "Configuration:" -ForegroundColor Yellow +Write-Host " Total Runs: $TotalRuns" -ForegroundColor Yellow +Write-Host " Initial Concurrency: $InitialConcurrency" -ForegroundColor Yellow +Write-Host " Max Concurrency: $MaxConcurrency" -ForegroundColor Yellow +Write-Host " Wave Delay Range: $MinWaveDelaySeconds-$MaxWaveDelaySeconds seconds" -ForegroundColor Yellow +Write-Host " Docker Image: $Image" -ForegroundColor Yellow +Write-Host "" + +$startTime = Get-Date +$completed = 0 +$waveNumber = 0 +[System.Collections.ArrayList]$allRuns = @() + +function Start-RunJob { + param([int]$Index, [string]$ImageName) + + $deviceId = [System.Guid]::NewGuid().ToString() + $clientUuid = [System.Guid]::NewGuid().ToString() + $userAgents = @( + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36', + 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15', + 'Mozilla/5.0 (Android 11; SM-G991B) AppleWebKit/537.36' + ) + $userAgent = $userAgents[$Index % $userAgents.Count] + + Start-Job -Name "run-$Index" -ScriptBlock { + param($i, $img, $devId, $clUuid, $ua) + $runStartTime = Get-Date + & docker run --rm ` + -e "DEVICE_ID=$devId" ` + -e "CLIENT_UUID=$clUuid" ` + -e "USER_AGENT=$ua" ` + $img 2>&1 | Out-Null + + return @{ + Index = $i + ExitCode = $LASTEXITCODE + Duration = ((Get-Date) - $runStartTime).TotalSeconds + DeviceId = $devId + } + } -ArgumentList $Index, $ImageName, $deviceId, $clientUuid, $userAgent +} + +[System.Collections.ArrayList]$jobs = @() +$started = 0 + +while ($completed -lt $TotalRuns) { + $waveNumber++ + + $currentConcurrency = [Math]::Min($InitialConcurrency + ($waveNumber - 1), $MaxConcurrency) + $waveSize = [Math]::Min($currentConcurrency, $TotalRuns - $started) + + Write-Host "[WAVE $waveNumber] Starting $waveSize concurrent runs" -ForegroundColor Magenta + + $waveStartTime = Get-Date + + for ($i = 0; $i -lt $waveSize; $i++) { + if ($started -lt $TotalRuns) { + $started++ + $job = Start-RunJob -Index $started -ImageName $Image + $jobs.Add($job) | Out-Null + } + } + + while ($jobs.Count -gt 0) { + $done = Wait-Job -Job $jobs[0] + $result = Receive-Job -Job $done + Remove-Job -Job $done + $jobs.RemoveAt(0) + $completed++ + + $allRuns.Add($result) | Out-Null + + $timestamp = Get-Date -Format "HH:mm:ss" + Write-Host " [$timestamp] Run $($result.Index) done (Duration: $([Math]::Round($result.Duration, 2))s) | Progress: $completed/$TotalRuns" -ForegroundColor Green + } + + $waveEndTime = Get-Date + $waveDuration = $waveEndTime - $waveStartTime + + Write-Host "[WAVE $waveNumber COMPLETE] Duration: $([Math]::Round($waveDuration.TotalSeconds, 2))s" -ForegroundColor Green + Write-Host "" + + if ($completed -lt $TotalRuns) { + $nextWaveDelay = Get-Random -Minimum $MinWaveDelaySeconds -Maximum ($MaxWaveDelaySeconds + 1) + $nextWaveTime = (Get-Date).AddSeconds($nextWaveDelay) + + Write-Host "[DELAY] Waiting $nextWaveDelay seconds before Wave $($waveNumber + 1)..." -ForegroundColor Yellow + Write-Host " Next wave starts at: $($nextWaveTime.ToString('HH:mm:ss'))" -ForegroundColor Yellow + Write-Host "" + + Start-Sleep -Seconds $nextWaveDelay + } +} + +$endTime = Get-Date +$totalDuration = $endTime - $startTime + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "TEST COMPLETED" -ForegroundColor Green +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" +Write-Host "Statistics:" -ForegroundColor Yellow +Write-Host " Total Runs: $TotalRuns" -ForegroundColor Yellow +Write-Host " Total Waves: $waveNumber" -ForegroundColor Yellow +Write-Host " Total Duration: $([Math]::Round($totalDuration.TotalSeconds, 2)) seconds" -ForegroundColor Yellow +Write-Host " Average per Run: $([Math]::Round($totalDuration.TotalSeconds / $TotalRuns, 2)) seconds" -ForegroundColor Yellow +Write-Host "" + +$successCount = ($allRuns | Where-Object { $_.ExitCode -eq 0 }).Count +$failureCount = $TotalRuns - $successCount + +Write-Host "Results:" -ForegroundColor Yellow +Write-Host " Successful: $successCount/$TotalRuns" -ForegroundColor Green +if ($failureCount -gt 0) { + Write-Host " Failed: $failureCount/$TotalRuns" -ForegroundColor Red +} +Write-Host ""