壓力測試報告

{{ $test->school_name }} · 測試時間:{{ \Carbon\Carbon::parse($test->test_time)->format('Y-m-d H:i:s') }} · 報告產出:{{ now()->format('Y-m-d H:i') }}
{{-- ===== 壹、測試說明 ===== --}}

壹、測試說明

本報告為網站壓力測試結果,旨在評估目標網站在不同負載條件下的效能表現與穩定性。
測試工具:K6(Grafana Labs 開源負載測試工具),透過模擬多個虛擬使用者(Virtual Users, VUs)同時對目標網站發送 HTTP 請求,量測回應時間、吞吐量、錯誤率等關鍵效能指標。
測試方式:每組 VUs 測試持續 30 秒,依序執行不同人數階段,觀察系統在逐步加壓下的效能變化。
主要指標: 平均回應時間(avg)、中位數回應時間(med)、P90/P95 回應時間、最大回應時間(max)、 HTTP 請求總數、每秒請求數(RPS)、請求失敗率、檢查通過率。
{{-- ===== 貳、測試對象 ===== --}}

貳、測試對象

學校 / 單位名稱
{{ $test->school_name }}
測試網址
{{ $test->url }}
@if ($test->login_path)
登入路徑
{{ $test->login_path }}
@endif @if ($test->email)
測試帳號
{{ $test->email }}
@endif
測試人數(VUs)
{{ collect($stages)->implode('、') }} 人
每階段時長
30 秒
測試時間
{{ \Carbon\Carbon::parse($test->test_time)->format('Y-m-d H:i:s') }}
測試工具
K6 (Grafana Labs)
{{-- ===== 參、測試結果總覽 ===== --}}

參、測試結果總覽

@php $validResults = collect($stageResults)->filter(fn($s) => isset($s['http_reqs'])); $count = $validResults->count(); @endphp @if ($count > 0) @foreach ($validResults as $sr) @endforeach
VUs HTTP 請求數 RPS 平均 (ms) 中位數 (ms) P90 (ms) P95 (ms) 最大 (ms) 失敗率 通過率
{{ $sr['vus'] }} {{ number_format($sr['http_reqs']) }} {{ $sr['http_reqs_rate'] }} {{ $sr['avg_duration'] }} {{ $sr['med_duration'] }} {{ $sr['p90_duration'] }} {{ $sr['p95_duration'] }} {{ $sr['max_duration'] }} @if ($sr['fail_rate'] == 0) 0% @elseif ($sr['fail_rate'] < 0.05) {{ round($sr['fail_rate'] * 100, 2) }}% @else {{ round($sr['fail_rate'] * 100, 2) }}% @endif @if ($sr['check_pass_rate'] >= 90) {{ $sr['check_pass_rate'] }}% @elseif ($sr['check_pass_rate'] >= 60) {{ $sr['check_pass_rate'] }}% @else {{ $sr['check_pass_rate'] }}% @endif
{{-- ===== 圖表區 ===== --}} @php // 共用尺寸 — 兩欄時每張寬 245px,單張滿版 520px $fullW = 520; $halfW = 245; $chartH = 170; $barAreaH = 130; $topPad = 18; $leftPad = 42; $maxDuration = $validResults->max('max_duration') ?: 1; $maxRps = $validResults->max('http_reqs_rate') ?: 1; $failRates = $validResults->map(fn($s) => round($s['fail_rate'] * 100, 2)); $maxFail = max($failRates->max(), 1); $groupW_half = ($halfW - $leftPad) / max($count, 1); $barW_half = $groupW_half / 5.5; $rpsBarW_half = ($halfW - $leftPad) / max($count, 1) * 0.55; @endphp {{-- 長條圖:2x2 並排 --}}
回應時間比較 (ms)
@php $colors = ['avg_duration' => '#0d6efd', 'p90_duration' => '#ffc107', 'p95_duration' => '#fd7e14', 'max_duration' => '#dc3545']; $colorLabels = ['avg_duration' => 'Avg', 'p90_duration' => 'P90', 'p95_duration' => 'P95', 'max_duration' => 'Max']; @endphp @for ($i = 0; $i <= 4; $i++) @php $y = $topPad + $barAreaH - ($barAreaH * $i / 4); $val = round($maxDuration * $i / 4); @endphp {{ $val }} @endfor @foreach ($validResults->values() as $idx => $sr) @php $gx = $leftPad + $idx * $groupW_half + $groupW_half * 0.08; @endphp @foreach (array_values(array_keys($colors)) as $ci => $metric) @php $val = $sr[$metric]; $bh = ($val / $maxDuration) * $barAreaH; $bx = $gx + $ci * ($barW_half + 1); $by = $topPad + $barAreaH - $bh; @endphp {{ round($val) }} @endforeach {{ $sr['vus'] }} VUs @endforeach @php $lx = $leftPad; @endphp @foreach ($colors as $metric => $color) {{ $colorLabels[$metric] }} @php $lx += 42; @endphp @endforeach
每秒請求數 (RPS)
@for ($i = 0; $i <= 4; $i++) @php $y = $topPad + $barAreaH - ($barAreaH * $i / 4); $val = round($maxRps * $i / 4, 1); @endphp {{ $val }} @endfor @foreach ($validResults->values() as $idx => $sr) @php $cx = $leftPad + $idx * $groupW_half + $groupW_half / 2; $bh = ($sr['http_reqs_rate'] / $maxRps) * $barAreaH; $bx = $cx - $rpsBarW_half / 2; $by = $topPad + $barAreaH - $bh; @endphp {{ $sr['http_reqs_rate'] }} {{ $sr['vus'] }} VUs @endforeach
請求失敗率 (%)
@for ($i = 0; $i <= 4; $i++) @php $y = $topPad + $barAreaH - ($barAreaH * $i / 4); $val = round($maxFail * $i / 4, 1); @endphp {{ $val }}% @endfor @foreach ($validResults->values() as $idx => $sr) @php $rate = round($sr['fail_rate'] * 100, 2); $cx = $leftPad + $idx * $groupW_half + $groupW_half / 2; $bh = ($rate / $maxFail) * $barAreaH; $bx = $cx - $rpsBarW_half / 2; $by = $topPad + $barAreaH - max($bh, 2); $fc = $rate == 0 ? '#198754' : ($rate < 5 ? '#ffc107' : '#dc3545'); @endphp {{ $rate }}% {{ $sr['vus'] }} VUs @endforeach
檢查通過率 (%)
@for ($i = 0; $i <= 4; $i++) @php $y = $topPad + $barAreaH - ($barAreaH * $i / 4); $val = round(100 * $i / 4); @endphp {{ $val }}% @endfor @foreach ($validResults->values() as $idx => $sr) @php $rate = $sr['check_pass_rate']; $cx = $leftPad + $idx * $groupW_half + $groupW_half / 2; $bh = ($rate / 100) * $barAreaH; $bx = $cx - $rpsBarW_half / 2; $by = $topPad + $barAreaH - max($bh, 2); $cc = $rate >= 90 ? '#198754' : ($rate >= 60 ? '#ffc107' : '#dc3545'); @endphp {{ $rate }}% {{ $sr['vus'] }} VUs @endforeach
{{-- ===== 曲線圖(滿版,有框) ===== --}} @if ($count >= 2) @php $lineH = 180; $lineTopPad = 22; $lineBottomPad = 28; $lineLeftPad = 48; $lineRightPad = 18; $plotW = $fullW - $lineLeftPad - $lineRightPad; $plotH = $lineH - $lineTopPad - $lineBottomPad; $vals = $validResults->values(); $xPositions = []; foreach ($vals as $i => $s) { $xPositions[] = $lineLeftPad + ($count > 1 ? ($i / ($count - 1)) * $plotW : $plotW / 2); } $lineMetrics = [ ['key' => 'avg_duration', 'label' => 'Avg', 'color' => '#0d6efd'], ['key' => 'med_duration', 'label' => 'Med', 'color' => '#6f42c1'], ['key' => 'p90_duration', 'label' => 'P90', 'color' => '#ffc107'], ['key' => 'p95_duration', 'label' => 'P95', 'color' => '#fd7e14'], ['key' => 'max_duration', 'label' => 'Max', 'color' => '#dc3545'], ]; $lineMaxY = $validResults->max('max_duration') ?: 1; @endphp
回應時間趨勢 (ms)
@for ($i = 0; $i <= 4; $i++) @php $gy = $lineTopPad + $plotH - ($plotH * $i / 4); $gv = round($lineMaxY * $i / 4); @endphp {{ $gv }} @endfor @foreach ($vals as $i => $s) {{ $s['vus'] }} VUs @endforeach @foreach ($lineMetrics as $lm) @php $points = []; foreach ($vals as $i => $s) { $px = $xPositions[$i]; $py = $lineTopPad + $plotH - ($s[$lm['key']] / $lineMaxY) * $plotH; $points[] = round($px,1).','.round($py,1); } @endphp @foreach ($vals as $i => $s) @php $px = $xPositions[$i]; $py = $lineTopPad + $plotH - ($s[$lm['key']] / $lineMaxY) * $plotH; @endphp {{ round($s[$lm['key']]) }} @endforeach @endforeach @php $llx = $lineLeftPad; @endphp @foreach ($lineMetrics as $lm) {{ $lm['label'] }} @php $llx += 55; @endphp @endforeach
@php $maxRpsLine = $validResults->max('http_reqs_rate') ?: 1; @endphp
綜合效能趨勢
@for ($i = 0; $i <= 4; $i++) @php $gy = $lineTopPad + $plotH - ($plotH * $i / 4); @endphp {{ round($maxRpsLine * $i / 4, 1) }} {{ round(100 * $i / 4) }}% @endfor req/s % @foreach ($vals as $i => $s) {{ $s['vus'] }} VUs @endforeach {{-- RPS --}} @php $rpsPoints = []; foreach ($vals as $i => $s) { $px=$xPositions[$i]; $py=$lineTopPad+$plotH-($s['http_reqs_rate']/$maxRpsLine)*$plotH; $rpsPoints[]=round($px,1).','.round($py,1); } @endphp @foreach ($vals as $i => $s) @php $px=$xPositions[$i]; $py=$lineTopPad+$plotH-($s['http_reqs_rate']/$maxRpsLine)*$plotH; @endphp {{ $s['http_reqs_rate'] }} @endforeach {{-- 失敗率 --}} @php $failPoints = []; foreach ($vals as $i => $s) { $px=$xPositions[$i]; $py=$lineTopPad+$plotH-(min($s['fail_rate']*100,100)/100)*$plotH; $failPoints[]=round($px,1).','.round($py,1); } @endphp @foreach ($vals as $i => $s) @php $px=$xPositions[$i]; $py=$lineTopPad+$plotH-(min($s['fail_rate']*100,100)/100)*$plotH; @endphp {{ round($s['fail_rate']*100, 2) }}% @endforeach {{-- 通過率 --}} @php $passPoints = []; foreach ($vals as $i => $s) { $px=$xPositions[$i]; $py=$lineTopPad+$plotH-($s['check_pass_rate']/100)*$plotH; $passPoints[]=round($px,1).','.round($py,1); } @endphp @foreach ($vals as $i => $s) @php $px=$xPositions[$i]; $py=$lineTopPad+$plotH-($s['check_pass_rate']/100)*$plotH; @endphp {{ $s['check_pass_rate'] }}% @endforeach {{-- Legend --}} @php $llx2 = $lineLeftPad; @endphp @foreach ([['RPS', '#198754', ''], ['失敗率', '#dc3545', '4,3'], ['通過率', '#0d6efd', '6,3']] as $lg) {{ $lg[0] }} @php $llx2 += 60; @endphp @endforeach
@endif @else

無法讀取測試結果 JSON 檔案。

@endif {{-- ===== 肆、各階段詳細結果 ===== --}}

肆、各階段詳細結果

@foreach ($validResults as $idx => $sr)

{{ $sr['vus'] }} VUs 階段

虛擬使用者數
{{ $sr['vus'] }} 人
HTTP 請求總數
{{ number_format($sr['http_reqs']) }} 次({{ $sr['http_reqs_rate'] }} req/s)
迭代次數
{{ number_format($sr['iterations']) }} 次
接收資料量
{{ $sr['data_received'] }} KB
發送資料量
{{ $sr['data_sent'] }} KB
{{-- 各階段回應時間分布 --}} @php $stageMetrics = [ ['label' => 'Min', 'value' => $sr['min_duration'], 'color' => '#198754'], ['label' => 'Avg', 'value' => $sr['avg_duration'], 'color' => '#0d6efd'], ['label' => 'Med', 'value' => $sr['med_duration'], 'color' => '#6f42c1'], ['label' => 'P90', 'value' => $sr['p90_duration'], 'color' => '#ffc107'], ['label' => 'P95', 'value' => $sr['p95_duration'], 'color' => '#fd7e14'], ['label' => 'Max', 'value' => $sr['max_duration'], 'color' => '#dc3545'], ]; $stageMax = $sr['max_duration'] ?: 1; $hBarH = 18; $hBarPad = 3; $hLabelW = 32; $hBarAreaW = $fullW - $hLabelW - 55; $hChartH = count($stageMetrics) * ($hBarH + $hBarPad) + 8; @endphp
{{ $sr['vus'] }} VUs — 回應時間分布 (ms)
@foreach ($stageMetrics as $mi => $m) @php $by = $mi * ($hBarH + $hBarPad) + 4; $bw = max(($m['value'] / $stageMax) * $hBarAreaW, 2); @endphp {{ $m['label'] }} {{ $m['value'] }} ms @endforeach
最小值 (ms)平均值 (ms)中位數 (ms)P90 (ms)P95 (ms)最大值 (ms)
{{ $sr['min_duration'] }}{{ $sr['avg_duration'] }}{{ $sr['med_duration'] }} {{ $sr['p90_duration'] }}{{ $sr['p95_duration'] }}{{ $sr['max_duration'] }}
@if (!empty($sr['checks']))

檢查項目:

@foreach ($sr['checks'] as $checkName => $check) @endforeach
檢查名稱 通過 失敗 結果
{{ $checkName }} {{ $check['passes'] ?? 0 }} {{ $check['fails'] ?? 0 }} @if (($check['fails'] ?? 0) == 0) PASS @else FAIL @endif
@endif @endforeach