This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Load Testing ASP.NET Core Applications with k6: Practical Implementation
Tuesday, 02 December 2025
Nu när du förstår grunderna till k6 och prestanstestning , är det ' dags att sätta in kunskapen i praktiken.. Denna artikel följer dig genom att skriva riktiga tester, ,, integrera dem i CI, M SK5, CD-rör, ,, och använda profileringsverktyg för att identifiera och fixa prestansproblem.
Det här är Del 2 av en tvåpartserie på lasttestning med k6:
- Del 1: Inleiding: Introduction till kM SK1 installering , testtyper, och varför kSSK4
- Del 2 (den här artikelnM SK2: skrivande tester , CI
Om du inte läst del 1, börjar där för att förstå kM SK2 grunder , installering , och tester innan du dyker in i implementeringen
Att sätta upp din testmiljö
Innan man startar k6 tester , släpper ' upp MinimalBlog för att testa
1. kör MinimalBlog lokalt
Att använda demoprojektet:
cd Mostlylucid.MinimalBlog.Demo
dotnet run
Eller med Docker:
docker run -d -p 5000:8080 \
-v $(pwd)/Markdown:/app/Markdown \
--name minimalblog-test \
scottgal/minimalblog:latest
Varför Release Mode spelar roll för prestationens tester
För noggranna resultattestning, alltid bygga i Release mode .:
dotnet run --configuration Release
Debug vs Release-modi skillnader:
| Aspect | Debug Modus | Release Modus | ||
|---|---|---|---|---|
| Optimeringar | Deaktiverad | Hela JIT-optimeringar aktiverade | ||
| Inlening | Minimalt ♫ ♫ | ♫ Aggressiv metod som visar ♫ | ||
| Den döda koden | Bevarad | Avlägsnat | ||
| Debug-symboler | Hela PDB , extra metadata | Minimal eller ingen | ||
| Ägningar | Debug.Assert() aktiv |
Kompilerad ut | ||
| Begränsningar | Extra säkerhetskontroller | |||
| Typiska överhead | ♫ 2-10 ♫ x långsammare ♫ |
Varför avbugsmodiet ger missledande resultat:
- Inga JIT-optimeringar: Kompilern bevarar kostrukturen för att lösa problem snarare än att optimera hastigheten
- Extra instrumentation: Debug-byggnader innefattar kod för brytpunkter , variable inspection , och stapelspår
- Inget inlining: metoderna är inte inlinjerade, ' inte ilinerad, ♫ ♫ , ♫ som lägger till anslutande överskott som kommer, ♪ ♫ inte existerar i produktionen,
- Ägningar körs:
Debug.Assert()uttalande utföra, och lägga till kontroller på att produktionen inte har - Skillnade minnesmönster: Debug allocations inkluderar padding och spårning som påverkar GC-beteendet
När använda avbugsmodiet för att testa:
- Misslyckanden i att lösa problem: När tester misslyckas och du behöver detaljerade stapelspår
- Utforskning av specifika begäran: Attack en debugger för att spåra en enda problematisk fråga
- Verifiering av loginering: Se till att din login fångar rätt information under last
- Initiell utveckling: När man skriver nya test skript och kontrollerar att de når rätt slutpunkter
Recommended workflow:
# 1. Develop and debug with Debug mode
dotnet run # Debug mode (default)
# 2. Validate functionality works
k6 run --vus 1 --duration 10s smoke-test.js
# 3. Switch to Release for actual performance testing
dotnet run -c Release
# 4. Run full load tests
k6 run load-test.js
tumregler: Om du
2. Förbereda testerdata
Att skapa flera markerdown-poster för att testa med:
# Create test posts directory
mkdir -p TestMarkdown
# Create sample posts
for i in {1..10}; do
cat > TestMarkdown/test-post-$i.md <<EOF
# Test Post $i
This is test post number $i with some **bold** text and *italic* text.
## Sample Content
- Item 1
- Item 2
- Item 3
\`\`\`csharp
public class Test {
public int Value { get; set; }
}
\`\`\`
EOF
done
3. Create Test Scripts Directory
mkdir k6-tests
cd k6-tests
Nu är vi - ' - redo att skriva våra tester.
Test 1: Smogtest
Låt oss börja med en enkel röktest för att kontrollera grundläggande funktionalitet.
File: smoke-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 1,
duration: '30s',
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests should be below 500ms
http_req_failed: ['rate<0.01'], // Less than 1% errors
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5000';
export default function() {
// Test homepage
let response = http.get(`${BASE_URL}/`);
check(response, {
'homepage status is 200': (r) => r.status === 200,
'homepage has content': (r) => r.body.length > 0,
'homepage response time OK': (r) => r.timings.duration < 500,
});
sleep(1);
// Test a single post
response = http.get(`${BASE_URL}/post/test-post-1`);
check(response, {
'post status is 200': (r) => r.status === 200,
'post has content': (r) => r.body.length > 0,
'post contains title': (r) => r.body.includes('Test Post 1'),
});
sleep(1);
// Test categories page
response = http.get(`${BASE_URL}/categories`);
check(response, {
'categories status is 200': (r) => r.status === 200,
'categories has content': (r) => r.body.length > 0,
});
sleep(1);
// Test category filter
response = http.get(`${BASE_URL}/category/Testing`);
check(response, {
'category filter status is 200': (r) => r.status === 200,
'category has posts': (r) => r.body.includes('Test Post'),
});
}
kör testet:
k6 run smoke-test.js
Oväntad utgång:
✓ homepage status is 200
✓ homepage has content
✓ homepage response time OK
✓ post status is 200
✓ post has content
✓ post contains title
✓ categories status is 200
✓ categories has content
✓ category filter status is 200
✓ category has posts
checks.........................: 100.00% ✓ 300 ✗ 0
data_received..................: 1.2 MB 40 kB/s
data_sent......................: 15 kB 500 B/s
http_req_duration..............: avg=45ms min=12ms med=38ms max=156ms p(95)=98ms
http_reqs......................: 120 4/s
Test 2: Cache-Valideringstest
MinimalBlog's prestationer beror mycket på att kassera
File: cache-test.js
import http from 'k6/http';
import { check, group } from 'k6';
export const options = {
vus: 1,
iterations: 10,
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5000';
export default function() {
group('Memory Cache Test - First Request', () => {
const start = Date.now();
const response = http.get(`${BASE_URL}/post/test-post-1`);
const duration = Date.now() - start;
check(response, {
'first request successful': (r) => r.status === 200,
});
console.log(`First request: ${duration}ms`);
});
group('Memory Cache Test - Cached Request', () => {
const start = Date.now();
const response = http.get(`${BASE_URL}/post/test-post-1`);
const duration = Date.now() - start;
check(response, {
'cached request successful': (r) => r.status === 200,
'cached request is faster': (r) => r.timings.duration < 100,
});
console.log(`Cached request: ${duration}ms`);
});
group('Output Cache Headers', () => {
const response = http.get(`${BASE_URL}/post/test-post-1`);
check(response, {
'has cache headers': (r) => r.headers['Cache-Control'] !== undefined,
'output cache working': (r) => {
const age = r.headers['Age'];
return age !== undefined && parseInt(age) >= 0;
},
});
console.log(`Cache-Control: ${response.headers['Cache-Control']}`);
console.log(`Age: ${response.headers['Age']}`);
});
}
kör testet:
k6 run cache-test.js
Den här testen visar dig
- Första uppmaningens tid ( när kaset är kallt)
- Följande inmatningstid (cache warmM SK1
- Bevakning av kas-kolor
Test 3: Ladetest
Låt oss nu testa
File: load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';
// Custom metrics
const errorRate = new Rate('errors');
export const options = {
stages: [
{ duration: '2m', target: 10 }, // Ramp up to 10 users
{ duration: '5m', target: 10 }, // Stay at 10 users
{ duration: '2m', target: 0 }, // Ramp down to 0
],
thresholds: {
http_req_duration: ['p(95)<300'], // 95% under 300ms
http_req_failed: ['rate<0.01'], // Less than 1% errors
errors: ['rate<0.1'], // Less than 10% errors
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5000';
const scenarios = [
{ weight: 50, path: '/' }, // 50% homepage
{ weight: 30, path: '/post/test-post-1' }, // 30% specific post
{ weight: 10, path: '/categories' }, // 10% categories
{ weight: 10, path: '/category/Testing' }, // 10% category filter
];
function weightedChoice(scenarios) {
const total = scenarios.reduce((sum, s) => sum + s.weight, 0);
let random = Math.random() * total;
for (const scenario of scenarios) {
random -= scenario.weight;
if (random <= 0) return scenario;
}
return scenarios[0];
}
export default function() {
const scenario = weightedChoice(scenarios);
const response = http.get(`${BASE_URL}${scenario.path}`);
const success = check(response, {
'status is 200': (r) => r.status === 200,
'response time OK': (r) => r.timings.duration < 500,
'has content': (r) => r.body.length > 0,
});
errorRate.add(!success);
sleep(Math.random() * 2 + 1); // Random sleep 1-3 seconds
}
kör testet:
k6 run load-test.js
Interpreterande resultat:
scenarios: (100.00%) 1 scenario, 10 max VUs, 9m30s max duration
default: 2m00s ramp-up, 5m00s plateau, 2m00s ramp-down
✓ status is 200
✓ response time OK
✓ has content
checks.........................: 100.00% ✓ 3450 ✗ 0
data_received..................: 12 MB 22 kB/s
data_sent......................: 132 kB 244 B/s
errors.........................: 0.00% ✓ 0 ✗ 1150
http_req_duration..............: avg=42ms min=8ms med=35ms max=245ms p(95)=86ms p(99)=156ms
http_reqs......................: 1150 2.12/s
iteration_duration.............: avg=2.1s min=1.0s med=2.0s max=3.4s
vus............................: 10 min=0 max=10
Nyckelmått att titta på:
- http_req: 95 ste percentilresponstid | ( | skulle vara under tröskel |)
- http_req_failde: Błędfrekvens ( skulle vara nära 0%)
- http_reqs: Hela genomgången
Test 4: Stresstest
Låt oss hitta MinimalBlog, ' och brytpunkten.
File: stress-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 10 }, // Normal load
{ duration: '2m', target: 50 }, // Increase to 50
{ duration: '2m', target: 100 }, // Stress at 100
{ duration: '2m', target: 200 }, // High stress at 200
{ duration: '3m', target: 0 }, // Recovery
],
thresholds: {
http_req_duration: ['p(99)<3000'], // 99% under 3s even under stress
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5000';
export default function() {
const responses = http.batch([
['GET', `${BASE_URL}/`],
['GET', `${BASE_URL}/post/test-post-1`],
['GET', `${BASE_URL}/post/test-post-2`],
['GET', `${BASE_URL}/categories`],
]);
responses.forEach((response, index) => {
check(response, {
[`request ${index} status is 200`]: (r) => r.status === 200,
});
});
sleep(0.5);
}
export function handleSummary(data) {
return {
'stress-test-summary.json': JSON.stringify(data),
stdout: textSummary(data, { indent: ' ', enableColors: true }),
};
}
kör testet:
k6 run stress-test.js
Titta på:
- När börjar reageringstid att försämras?
- När VU räknas dyker upp fel
- Kommer systemet att återhämta sig efter att lasten minskar?
Test 5: Spiketest
Testa plötsliga trafikstockningar ( som i Reddit eller Hacker News
File: spike-test.js
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '1m', target: 10 }, // Normal traffic
{ duration: '30s', target: 200 }, // Sudden spike!
{ duration: '3m', target: 200 }, // Sustained spike
{ duration: '1m', target: 10 }, // Back to normal
{ duration: '1m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<1000'], // Allow higher latency during spike
http_req_failed: ['rate<0.05'], // Allow 5% errors during spike
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5000';
export default function() {
const response = http.get(`${BASE_URL}/`);
check(response, {
'status is 200 or 503': (r) => r.status === 200 || r.status === 503,
});
}
kör testet:
k6 run spike-test.js
Den här testet bekräftar:
- Handlar systemet med plötslig belastning?
- Finns det lämpliga hastighetsbegränsningsmekanismer?
- Kommer den att återhämta sig gracefully?
Test 6: Soaktest
lång test för att upptäcka minnesmängder eller resurser uttömning
File: soak-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 20 }, // Ramp up
{ duration: '3h', target: 20 }, // Stay at 20 for 3 hours
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5000';
export default function() {
const response = http.get(`${BASE_URL}/post/test-post-${Math.floor(Math.random() * 10) + 1}`);
check(response, {
'status is 200': (r) => r.status === 200,
'response time stable': (r) => r.timings.duration < 500,
});
sleep(2);
}
kör testet:
k6 run soak-test.js
Nyckel: övervaka systemresurser under vätsketester
# In another terminal
watch -n 5 'dotnet-counters ps | grep -i minimal'
# Or use top/htop
htop -p $(pgrep -f MinimalBlog)
Titta på:
- Graduell minnesökning (memory leak
- CPU-användningen kryper upp
- Reaktionszeit nedbrytning över tid
Test 7: Realistisk User Journey
simulera det faktiska användarbeteendet:
File: user-journey-test.js
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { htmlReport } from 'https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js';
export const options = {
vus: 10,
duration: '5m',
thresholds: {
'group_duration{group:::01_homepage}': ['avg<500'],
'group_duration{group:::02_browse_posts}': ['avg<500'],
'group_duration{group:::03_categories}': ['avg<500'],
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5000';
export default function() {
// Simulate a real user journey
group('01_homepage', () => {
const response = http.get(`${BASE_URL}/`);
check(response, {
'homepage loaded': (r) => r.status === 200,
'homepage has posts': (r) => r.body.includes('Test Post'),
});
sleep(Math.random() * 3 + 2); // Read homepage 2-5 seconds
});
group('02_browse_posts', () => {
// User clicks on a post
let response = http.get(`${BASE_URL}/post/test-post-1`);
check(response, {
'post loaded': (r) => r.status === 200,
'post has content': (r) => r.body.length > 500,
});
sleep(Math.random() * 20 + 10); // Read post 10-30 seconds
// User clicks another post
response = http.get(`${BASE_URL}/post/test-post-2`);
check(response, {
'second post loaded': (r) => r.status === 200,
});
sleep(Math.random() * 15 + 5); // Read second post 5-20 seconds
});
group('03_categories', () => {
// User explores categories
const response = http.get(`${BASE_URL}/categories`);
check(response, {
'categories loaded': (r) => r.status === 200,
});
sleep(2);
// User clicks a category
const categoryResponse = http.get(`${BASE_URL}/category/Testing`);
check(categoryResponse, {
'category posts loaded': (r) => r.status === 200,
'category has posts': (r) => r.body.includes('Test Post'),
});
sleep(Math.random() * 10 + 5); // Browse category 5-15 seconds
});
}
export function handleSummary(data) {
return {
'user-journey-report.html': htmlReport(data),
};
}
kör testet:
k6 run user-journey-test.js
Det här skapar en HTML-rapport user-journey-report.html
Advanced: Testing MetaWeblog API
Om du har MetaWeblog aktiverat
File: metaweblog-test.js
import http from 'k6/http';
import { check } from 'k6';
import encoding from 'k6/encoding';
export const options = {
vus: 1,
iterations: 10,
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5000';
const USERNAME = __ENV.USERNAME || 'admin';
const PASSWORD = __ENV.PASSWORD || 'changeme';
function createXmlRpcRequest(methodName, params) {
return `<?xml version="1.0"?>
<methodCall>
<methodName>${methodName}</methodName>
<params>
${params}
</params>
</methodCall>`;
}
export default function() {
// Test getRecentPosts
const recentPostsXml = createXmlRpcRequest('blogger.getRecentPosts', `
<param><value><string>0</string></value></param>
<param><value><string>${USERNAME}</string></value></param>
<param><value><string>${PASSWORD}</string></value></param>
<param><value><int>10</int></value></param>
`);
const response = http.post(`${BASE_URL}/metaweblog`, recentPostsXml, {
headers: { 'Content-Type': 'text/xml' },
});
check(response, {
'MetaWeblog API responds': (r) => r.status === 200,
'MetaWeblog returns XML': (r) => r.body.includes('<?xml'),
'MetaWeblog has methodResponse': (r) => r.body.includes('methodResponse'),
});
}
kör testet:
k6 run -e USERNAME=admin -e PASSWORD=yourpassword metaweblog-test.js
Att köra tester i CI/CD
Att använda k6 som ett kvalitetgrind
Den kraftfullaste tillämpningen av k6 i CI/CD är kvalitet-grind - failar automatiskt att bygga om prestationen minskar . Här' är hur man implementerar komplexa k-testning i GitHub-aktioner
GitHub actions: Grundversion
Låt's börja med en grundläggande arbetsström som kör på varje dragning och blockerar sammankoppling om tester misslyckas
Att skapa .github/workflows/k6-pr-check.yml:
name: k6 Performance Check
on:
pull_request:
branches: [ main ]
jobs:
performance-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Setup k6
run: |
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \
--keyserver hkp://keyserver.ubuntu.com:80 \
--recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | \
sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
- name: Create test data
run: |
mkdir -p TestMarkdown
for i in {1..10}; do
cat > TestMarkdown/test-post-$i.md <<EOF
# Test Post $i
This is test post $i with **bold** and *italic* text.
\`\`\`csharp
public class Test { public int Value { get; set; } }
\`\`\`
EOF
done
- name: Build MinimalBlog
run: |
cd Mostlylucid.MinimalBlog.Demo
dotnet build --configuration Release
- name: Start MinimalBlog
run: |
cd Mostlylucid.MinimalBlog.Demo
dotnet run --configuration Release &
echo $! > app.pid
# Wait for app to be ready
timeout 60 bash -c 'until curl -sf http://localhost:5000 > /dev/null; do
echo "Waiting for app..."
sleep 2
done'
echo "- App is ready!"
- name: Run Smoke Test
id: smoke-test
run: |
k6 run --out json=smoke-results.json k6-tests/smoke-test.js
echo "smoke-test-passed=true" >> $GITHUB_OUTPUT
- name: Run Load Test
id: load-test
run: |
k6 run --out json=load-results.json k6-tests/load-test.js
echo "load-test-passed=true" >> $GITHUB_OUTPUT
- name: Run Cache Test
id: cache-test
run: |
k6 run --out json=cache-results.json k6-tests/cache-test.js
echo "cache-test-passed=true" >> $GITHUB_OUTPUT
- name: Stop MinimalBlog
if: always()
run: |
if [ -f Mostlylucid.MinimalBlog.Demo/app.pid ]; then
kill $(cat Mostlylucid.MinimalBlog.Demo/app.pid) || true
fi
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: k6-test-results
path: |
*-results.json
*.html
retention-days: 30
- name: Check Test Results
if: always()
run: |
echo "## k6 Performance Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.smoke-test.outputs.smoke-test-passed }}" == "true" ]; then
echo "- Smoke Test: PASSED" >> $GITHUB_STEP_SUMMARY
else
echo "- Smoke Test: FAILED" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ steps.load-test.outputs.load-test-passed }}" == "true" ]; then
echo "- Load Test: PASSED" >> $GITHUB_STEP_SUMMARY
else
echo "- Load Test: FAILED" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ steps.cache-test.outputs.cache-test-passed }}" == "true" ]; then
echo "- Cache Test: PASSED" >> $GITHUB_STEP_SUMMARY
else
echo "- Cache Test: FAILED" >> $GITHUB_STEP_SUMMARY
fi
Nyckliga egenskaper:
- körs på varje PR
- Skeplar provdata automatiskt
- Vänter till att app är redo innan den testar
- kör flera testtyper
- Det går inte att bygga om tröskeln inte är uppfyllda
- Laddar upp resultat som artefakter
- Ser sammanfattningen i GitHubs gränssnitt
sequenceDiagram
participant PR as Pull Request
participant GHA as GitHub Actions
participant App as MinimalBlog
participant k6 as k6 Tests
PR->>GHA: Trigger workflow
GHA->>GHA: Setup .NET & k6
GHA->>GHA: Create test data
GHA->>App: Build & Start
App-->>GHA: App ready
GHA->>k6: Run smoke test
k6-->>GHA: Results
GHA->>k6: Run load test
k6-->>GHA: Results
GHA->>k6: Run cache test
k6-->>GHA: Results
GHA->>App: Stop
alt Tests Pass
GHA->>PR: Mark as success
else Tests Fail
GHA->>PR: Block merge
end
GitHub-aktioner: Utveckling med PR-kommentar
Att skapa .github/workflows/k6-pr-comment.yml att lägga upp resultaten som PR kommentarer:
name: k6 Performance with PR Comment
on:
pull_request:
branches: [ main ]
permissions:
pull-requests: write
contents: read
jobs:
performance-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Setup k6
uses: grafana/setup-k6-action@v1
- name: Create test data
run: |
mkdir -p TestMarkdown
for i in {1..10}; do
cat > TestMarkdown/test-post-$i.md <<EOF
# Test Post $i
Test content for post $i.
EOF
done
- name: Build and Start App
run: |
cd Mostlylucid.MinimalBlog.Demo
dotnet build -c Release
dotnet run -c Release > app.log 2>&1 &
APP_PID=$!
echo $APP_PID > app.pid
timeout 60 bash -c 'until curl -sf http://localhost:5000; do sleep 2; done'
- name: Run k6 Tests
id: k6-test
run: |
# Run tests and capture output
k6 run --out json=results.json k6-tests/load-test.js > k6-output.txt 2>&1 || true
# Parse results
if grep -q "✓" k6-output.txt; then
echo "tests-passed=true" >> $GITHUB_OUTPUT
else
echo "tests-passed=false" >> $GITHUB_OUTPUT
fi
# Extract key metrics
P95=$(grep "http_req_duration" k6-output.txt | grep -oP 'p\(95\)=\K[0-9.]+' || echo "N/A")
RPS=$(grep "http_reqs" k6-output.txt | grep -oP '\d+\.\d+/s' || echo "N/A")
ERRORS=$(grep "http_req_failed" k6-output.txt | grep -oP '\d+\.\d+%' || echo "0%")
echo "p95=${P95}" >> $GITHUB_OUTPUT
echo "rps=${RPS}" >> $GITHUB_OUTPUT
echo "errors=${ERRORS}" >> $GITHUB_OUTPUT
- name: Comment PR
uses: actions/github-script@v7
if: always()
with:
script: |
const fs = require('fs');
const output = fs.readFileSync('k6-output.txt', 'utf8');
const testsPassed = '${{ steps.k6-test.outputs.tests-passed }}' === 'true';
const icon = testsPassed ? 'PASS' : 'FAIL';
const status = testsPassed ? 'PASSED' : 'FAILED';
const comment = `## ${icon} k6 Performance Test Results
**Status:** ${status}
### Key Metrics
| Metric | Value |
|--------|-------|
| P95 Response Time | ${{ steps.k6-test.outputs.p95 }}ms |
| Requests/sec | ${{ steps.k6-test.outputs.rps }} |
| Error Rate | ${{ steps.k6-test.outputs.errors }} |
### Thresholds
- P95 < 300ms
- Error rate < 1%
<details>
<summary>Full k6 Output</summary>
\`\`\`
${output}
\`\`\`
</details>
[View full test results](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
- name: Stop App
if: always()
run: |
[ -f Mostlylucid.MinimalBlog.Demo/app.pid ] && \
kill $(cat Mostlylucid.MinimalBlog.Demo/app.pid) || true
- name: Upload Artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: k6-results
path: |
results.json
k6-output.txt
Mostlylucid.MinimalBlog.Demo/app.log
Den här arbetsgången:
- Beiträger leder direkt till PR
- Ser nyckelmått i en tabell
- Tillhandahåller full utgång i expanderande sektioner
- länkar till den fulla testrun
- Visuella statusindikatorer
GitHub actions: Performance Regression Detection
Att skapa .github/workflows/k6-baseline.yml att upptäcka prestansregressioner:
name: Performance Regression Check
on:
pull_request:
branches: [ main ]
jobs:
regression-check:
runs-on: ubuntu-latest
steps:
- name: Checkout PR code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Setup k6
uses: grafana/setup-k6-action@v1
- name: Create test data
run: |
mkdir -p TestMarkdown
for i in {1..20}; do
echo "# Test Post $i" > TestMarkdown/test-$i.md
echo "" >> TestMarkdown/test-$i.md
echo '' >> TestMarkdown/test-$i.md
echo "Test content $i" >> TestMarkdown/test-$i.md
done
- name: Test PR Branch
run: |
cd Mostlylucid.MinimalBlog.Demo
dotnet build -c Release
dotnet run -c Release &
APP_PID=$!
timeout 60 bash -c 'until curl -sf http://localhost:5000; do sleep 2; done'
# Run test and save results
k6 run --summary-export=pr-results.json k6-tests/load-test.js
kill $APP_PID
sleep 5
- name: Checkout main branch
uses: actions/checkout@v4
with:
ref: main
path: baseline
- name: Test Baseline (main branch)
run: |
cd baseline/Mostlylucid.MinimalBlog.Demo
dotnet build -c Release
dotnet run -c Release &
APP_PID=$!
timeout 60 bash -c 'until curl -sf http://localhost:5000; do sleep 2; done'
# Run test and save results
k6 run --summary-export=baseline-results.json ../k6-tests/load-test.js
kill $APP_PID
- name: Compare Results
run: |
# Extract P95 from both runs
PR_P95=$(jq '.metrics.http_req_duration.values["p(95)"]' pr-results.json)
BASE_P95=$(jq '.metrics.http_req_duration.values["p(95)"]' baseline/baseline-results.json)
# Calculate percentage change
CHANGE=$(echo "scale=2; (($PR_P95 - $BASE_P95) / $BASE_P95) * 100" | bc)
echo "## Performance Comparison" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Branch | P95 Response Time |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------------------|" >> $GITHUB_STEP_SUMMARY
echo "| main (baseline) | ${BASE_P95}ms |" >> $GITHUB_STEP_SUMMARY
echo "| PR | ${PR_P95}ms |" >> $GITHUB_STEP_SUMMARY
echo "| Change | ${CHANGE}% |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Fail if performance degrades by more than 20%
if (( $(echo "$CHANGE > 20" | bc -l) )); then
echo "- Performance degraded by ${CHANGE}%" >> $GITHUB_STEP_SUMMARY
echo "::error::Performance regression detected: ${CHANGE}% slower than baseline"
exit 1
else
echo "- Performance acceptable" >> $GITHUB_STEP_SUMMARY
fi
Den här arbetsgången:
- Testar både PR och huvudavdelningen
- Vergleichar prestationsmått
- Om prestationen minskar >20% misslyckas
- Försämrar att kombinera långsamma kod
graph TD
A[PR Submitted] --> B[Checkout PR Code]
B --> C[Test PR Branch]
C --> D[Checkout main Branch]
D --> E[Test Baseline]
E --> F{Calculate<br/>Difference}
F -->|>20% Slower| G[FAIL Build]
F -->|<20% Change| H[PASS Build]
G --> I[Block Merge]
H --> J[Allow Merge]
style G stroke:#ff6b6b
style H stroke:#51cf66
bästa metoder för CI/CD k6 Testing
Starta snabbt, Tor tidigt
- kör röktester först (30sM SK1
- Bara köra fulla tester om rökning passerar
- Det misslyckades snabbt att spara CI minuter
Använd lämpliga tröskel
// Too strict for CI thresholds: { http_req_duration: ['p(95)<100'] } // Good for CI thresholds: { http_req_duration: ['p(95)<300', 'p(99)<1000'], http_req_failed: ['rate<0.01'] }Cache beroenden
- name: Cache .NET packages uses: actions/cache@v4 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}Samma protokoll för misslyckade tester
- name: Upload App Logs if: failure() uses: actions/upload-artifact@v4 with: name: app-logs path: Mostlylucid.MinimalBlog.Demo/app.logAnvänd miljön- specifika tröskel
const isPR = __ENV.GITHUB_EVENT_NAME === 'pull_request'; export const options = { thresholds: { http_req_duration: isPR ? ['p(95)<500'] // Relaxed for PR : ['p(95)<300'], // Strict for main }, };
Att övervaka k6 tester med Grafana
För avancerad övervakning kan man skicka k6 mätningar till Grafana
1. installera Prometheus och Grafana
docker-kompose.yml för övervakning:
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
depends_on:
- prometheus
2. kör k6 med Prometheus utgång
k6 run --out experimental-prometheus-rw load-test.js
3. Import kM SK1 Tavlansbord i Grafana
- Gå till Grafana på:
http://localhost:3000 - Lägg till Prometheus datakälla
- Importera k6 brädan (IDM SK2 18030)
Interpreterar k6 Metriker
Nyckelmått för att förstå
Response Time Metrics
- http_req: Totala inmatningstid
avg: Medelvärde av responsstidmin/max: snabbaste / lågaste responsmed: Median (50 fjärde procentil )p(90): 90ste procentilp(95): 95ste percentil (bra SLA-måttriktp(99): 99ste procentil (fångstviktiga
Bra värden för MinimalBlog:
- p(95) M SK1 | 300 ms för kaserad innehåll
- p(95) < M SK2ms för första sändningstid
Utputtmetrik
- http_reqs: Totala begäran
- http_req_rate: begärningar per sekund
- iteration_djuur: tid för en komplett repetition
Frågmetrik
- http_req_failde: Misslyckade begäran | (4 |xx |, |
- kontroller: Bekräftelsepass
Målet : < | 1%
Dataöverföring
- data_bekommen: laddade ner data
- data_sent: laddade upp data
De bästa metoderna för att testa MinimalBlog
1. Test i isolering
Testa MinimalBlog separat från andra tjänster:
# Stop other services
docker-compose down
# Run only MinimalBlog
dotnet run --project Mostlylucid.MinimalBlog.Demo
2. Använd realistiska data
Don' inte testa med tomma markerdown-filer . Använd realistisk innehåll
- varierade utgångslängder (kurt , medel, långtM SK3
- Skillnade markerdown-fenskaper (kodexblockar, bilder , listor
- Flera kategorier
- Distribution av post i världen
3. Testa kasbehavior tydligt
MinimalBlog's prestationer är beroende av att kassera
// Test cold cache
export function setup() {
// Clear cache by restarting app or waiting for expiration
}
// Test warm cache
export default function() {
// First request (cache miss)
http.get(`${BASE_URL}/post/test-1`);
// Second request (cache hit)
const cached = http.get(`${BASE_URL}/post/test-1`);
check(cached, {
'cache hit is faster': (r) => r.timings.duration < 50,
});
}
4. Monitorsystemresurser
kör dessa tillsammans med k6 tester
# CPU and memory
htop
# .NET metrics
dotnet-counters monitor -p $(pgrep -f MinimalBlog)
# GC collections
dotnet-counters monitor --counters System.Runtime[gc-heap-size,gen-0-gc-count,gen-1-gc-count,gen-2-gc-count]
5. Se realistiska tröskel
inte sätta trösklar för strikta eller för lurade
export const options = {
thresholds: {
// Too strict (will fail unnecessarily)
// http_req_duration: ['p(95)<50'],
// Too loose (won't catch problems)
// http_req_duration: ['p(95)<5000'],
// Just right for MinimalBlog
http_req_duration: ['p(95)<300', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
checks: ['rate>0.95'],
},
};
6. Testa olika scenarior
// Homepage (list view)
http.get(`${BASE_URL}/`);
// Individual posts (detail view)
http.get(`${BASE_URL}/post/getting-started`);
// Categories (filtering)
http.get(`${BASE_URL}/category/Tutorial`);
// Non-existent posts (error handling)
http.get(`${BASE_URL}/post/does-not-exist`);
7. Version dina tester
Håll tester i versionkontroll tillsammans med kod:
Mostlylucid.MinimalBlog/
├── k6-tests/
│ ├── smoke-test.js
│ ├── load-test.js
│ ├── stress-test.js
│ ├── spike-test.js
│ ├── soak-test.js
│ ├── cache-test.js
│ └── user-journey-test.js
├── .github/
│ └── workflows/
│ └── k6-tests.yml
└── README-TESTING.md
Att lösa vanliga problem
Utmaning 1: Verbinding avfärdad
Problemet:
WARN[0000] Request Failed error="Get \"http://localhost:5000/\": dial tcp [::1]:5000: connect: connection refused"
Lösningen:
# Verify MinimalBlog is running
curl http://localhost:5000
# Check correct port
netstat -an | grep 5000
# Use correct URL
k6 run -e BASE_URL=http://localhost:5173 smoke-test.js
Problemet 2: hög felmängd
Problemet:
✗ http_req_failed: 45.23% (threshold is < 1%)
Lösningen:
- Prüfa applikationslogs för fel
- Verifiera att provdata existerar (markdown-filer)
- Reduera VU-tal (kan vara överbelad )
- Förhöja rampen, -, uppgången
Nummer 3: långsamma responsstid
Problemet:
http_req_duration..............: avg=2.5s p(95)=5.2s
Lösningen:
- Build in Release mode:
dotnet run -c Release - Bevestig att kaskning fungerar
- Kontrollera disken I/O (svärre lêer läserM SK2
- Profil med dotnet-trace:
dotnet-trace collect -p $(pgrep -f MinimalBlog) --format speedscope
Utmaning 4: minnesmängder i dämningstest
Problemet: minnesanvändningen växer kontinuerligt
Lösningen:
- Monitor med:
dotnet-counters monitor --counters System.Runtime[gc-heap-size,gc-committed,gc-allocated]
- Samma ett minnesdump:
dotnet-dump collect -p $(pgrep -f MinimalBlog)
dotnet-dump analyze memory.dump
- Prüfa för:
- Kaset håller inte på att expira
- File handleder inte håller på att stänga
- Eventbehandlare håller inte på att avkolla
Verklig-World Example: Testing Before Release
Här är "'", en komplett testprocess innan MinimalBlogs Veröffentlichung .
#!/bin/bash
# test-release.sh
set -e
echo "Building: Building MinimalBlog in Release mode..."
dotnet build -c Release Mostlylucid.MinimalBlog/Mostlylucid.MinimalBlog.csproj
echo "Starting: Starting MinimalBlog Demo..."
dotnet run -c Release --project Mostlylucid.MinimalBlog.Demo &
APP_PID=$!
# Wait for app to start
echo "Waiting: Waiting for app to start..."
timeout 30 bash -c 'until curl -s http://localhost:5000 > /dev/null; do sleep 1; done'
echo "- App started successfully"
# Run test suite
echo "Running: Running smoke tests..."
k6 run k6-tests/smoke-test.js
echo "Running: Running cache validation..."
k6 run k6-tests/cache-test.js
echo "Running: Running load tests..."
k6 run k6-tests/load-test.js
echo "Running: Running stress tests..."
k6 run k6-tests/stress-test.js
echo "Running: Running spike tests..."
k6 run k6-tests/spike-test.js
echo "Generating: Generating combined report..."
# Process and combine results
echo "Stopping: Stopping app..."
kill $APP_PID
echo "- All tests completed successfully!"
kör den:
chmod +x test-release.sh
./test-release.sh
Profilering med dotTrace och dotMemory
k6 säger till dig vad är långsamt, men profiler säger varför. kombinerar kM SK1 belastningstest med JetBrains profiler (dotTrace for performance, dotMemory for memoryMSC4 låter dig identifiera exakta bottlenecks i MinimalBlog-koden
Varför är profilen under laddad?
Profilering utan belastning missar ofta verkliga problem
- Cold start vs warm cache: olika kodexvägar
- Konkurent tillgång: Lock-kontentionM SK1 tävlingsvillkor
- minnestryck: GC-beteende under last
- Förbrukning av resurser: Anslutningspooler , file handleder
k6 skapar en realistisk belastning medan profilern fångar vad som händer inuti din app
Setup: Att installera JetBrains verktyg
Option 1: JetBrains dotTrace/dotMemory (Commercial
ladda ner från JetBrains:
# Linux/Mac
wget https://download.jetbrains.com/resharper/dotUltimate.2024.3/JetBrains.dotTrace.GlobalTools.2024.3.nupkg
# Install as global tool
dotnet tool install --global JetBrains.dotTrace.GlobalTools
dotnet tool install --global JetBrains.dotMemory.Console
Option 2: dotnet - spåra ( fria, byggt, - i )
dotnet tool install --global dotnet-trace
dotnet tool install --global dotnet-counters
dotnet tool install --global dotnet-dump
För den här guiden, ,, visar vi båda angreppssätten.
Profilering av arbetsflödet
flowchart LR
A[Start App] --> B[Baseline<br/>Snapshot]
B --> C[Start Profiler]
C --> D[Run k6 Load Test]
D --> E[Capture Metrics]
E --> F[Stop Profiler]
F --> G[Final Snapshot]
G --> H{Analyze Results}
H --> I[Identify<br/>Hotspots]
I --> J[Optimize Code]
J --> K[Re-test with k6]
K --> L{Better?}
L -->|Yes| M[Document & Deploy]
L -->|No| I
style M stroke:#51cf66
style I stroke:#FFD700
steg 1: baseline utan last steg 2: Profil under k6 belastning steg 3: Vergleicha och identifiera bottlenecks steg 4: Fixera och återställ -test
Performanceprofilering med dotTrace
Scenario: Hitta långsamt Markdown-Parsing
1. Starta MinimalBlog med profiling
cd Mostlylucid.MinimalBlog.Demo
# Using dotTrace
dottrace attach <PID> --save-to=profile-baseline.dtt --timeout=60s
# Or build and run with profiling
dotnet build -c Release
dotnet run -c Release &
APP_PID=$!
# Attach profiler
dottrace attach $APP_PID --save-to=profile-load.dtt --timeout=120s
2. I ett annat terminalM SK1 köra k6 lasttestMSC3
# This generates load while profiler captures data
k6 run --duration 60s --vus 20 k6-tests/load-test.js
3. Analisera profilen :
# Convert to speedscope format for web viewing
dottrace report profile-load.dtt --output=report.html
Vad ska man leta efter i rapportet:
- Heißa vägar: Metoder som tar mest CPU-tid
- Appellträd: Var tid spenderas i telefonhierarkin
- Självtid: tid i metoden utom samtalen till andra metoder
Ett exempel på resultaten i MinimalBlog:
Method Total Time Self Time Calls
-----------------------------------------------------------------------
MarkdownBlogService.GetAllPosts() 450ms 5ms 100
├─ LoadAllPosts() 420ms 10ms 10
│ ├─ ParseFile() 400ms 20ms 100
│ │ ├─ Markdown.Parse() 250ms 250ms 100 HOTSPOT
│ │ └─ Regex.Match() (categories) 80ms 80ms 100 HOTSPOT
│ └─ File.ReadAllText() 10ms 10ms 100
└─ cache.GetOrCreate() 25ms 25ms 90
Det här visar
Markdown.Parse()är bottleneck #1- Regex matchning är #2 (80ms totaltM SK2
- Den största delen av första 10 samtalen ( kastar kaset
Att använda dotnet
# Start tracing
dotnet-trace collect -p $APP_PID --format speedscope -o trace.json &
TRACE_PID=$!
# Run k6 test
k6 run --duration 30s --vus 10 k6-tests/load-test.js
# Stop tracing
kill -SIGINT $TRACE_PID
# View in browser
# Upload trace.json to https://www.speedscope.app/
Speedscope visualisering:
- Flame-grafen visar uppmanade högar
- Tidsorterad syn visar uitvoering över tid
- Den vänstra stora bilden lyfter fram långsamma operationer
Profilering av minnen med dotMemory
Scenario: Detektor av minneslösch i kas
1. Starta MinimalBlog och anslut minnes profilern
cd Mostlylucid.MinimalBlog.Demo
dotnet run -c Release &
APP_PID=$!
# Take initial snapshot
dotmemory get-snapshot $APP_PID --save-to=snapshot-start.dmw
2. köra kM SK1 lasttest :
# Run for 5 minutes with constant load
k6 run --duration 5m --vus 20 k6-tests/load-test.js
3. Ta foton under och efter testet
# During load (after 2 minutes)
sleep 120
dotmemory get-snapshot $APP_PID --save-to=snapshot-load.dmw
# After load (cool down)
# Wait for k6 to finish, then wait 2 more minutes
sleep 120
dotmemory get-snapshot $APP_PID --save-to=snapshot-after.dmw
4. Analisera minnesväxten
# Compare snapshots
dotmemory compare snapshot-start.dmw snapshot-after.dmw --save-to=comparison.html
Vad att leta efter:
- Kollektioner som växer: Lysar , Woordekat som aldrig minskar
- Eventbehandlare: Otympliga händelser med referensramar
- Cache-blöd: Cache växer bortom förväntade storlek
- Stringaloktioner: Excessive string concatenation
exempel på resultaten:
Object Type Start Load After Growth
-------------------------------------------------------------------
System.String 5 MB 45 MB 15 MB +10 MB
BlogPost[] 2 MB 2 MB 2 MB 0 MB PASS
MarkdownDocument 0 MB 8 MB 0 MB 0 MB - (GC'd)
Dictionary<string, BlogPost> 1 MB 1 MB 1 MB 0 MB PASS
Det här visar
- Stringar växte med 10 MB ( potentiell strängläcka eller cacheproblem
- BlogPosts kas är stabil (bra)
- MarkdownDokument rensat av GC (goodM SK1
Att använda dotnet
# Capture memory dump during load
dotnet-dump collect -p $APP_PID -o dump-load.dmp
# After test
dotnet-dump collect -p $APP_PID -o dump-after.dmp
# Analyze dump
dotnet-dump analyze dump-load.dmp
> dumpheap -stat
> dumpheap -mt <MethodTable> -min 1000
> gcroot <address>
Kombinerad k6 + Profileringsskript
Här är ett komplettt skript som sätter igång k6 medan man profilar
File: profile-under-load.sh
#!/bin/bash
set -e
echo "Profiling: Profiling MinimalBlog under k6 load"
# Configuration
DURATION="120s"
VUS="20"
OUTPUT_DIR="profiling-results"
mkdir -p $OUTPUT_DIR
# Build and start app
echo "Building: Building MinimalBlog..."
cd Mostlylucid.MinimalBlog.Demo
dotnet build -c Release > /dev/null
echo "Starting: Starting MinimalBlog..."
dotnet run -c Release > $OUTPUT_DIR/app.log 2>&1 &
APP_PID=$!
echo $APP_PID > $OUTPUT_DIR/app.pid
# Wait for startup
timeout 60 bash -c "until curl -sf http://localhost:5000 > /dev/null; do sleep 2; done"
echo "- App started (PID: $APP_PID)"
# Baseline metrics
echo "Generating: Collecting baseline metrics..."
dotnet-counters collect -p $APP_PID --format json -o $OUTPUT_DIR/baseline-metrics.json &
COUNTER_PID=$!
sleep 10
kill -SIGINT $COUNTER_PID
# Take initial memory snapshot
echo "Snapshot: Initial memory snapshot..."
if command -v dotmemory &> /dev/null; then
dotmemory get-snapshot $APP_PID --save-to=$OUTPUT_DIR/snapshot-start.dmw
else
dotnet-dump collect -p $APP_PID -o $OUTPUT_DIR/dump-start.dmp
fi
# Start performance profiling
echo "Starting: Starting performance profiler..."
if command -v dottrace &> /dev/null; then
dottrace attach $APP_PID --save-to=$OUTPUT_DIR/performance-profile.dtt --timeout=150s &
PROFILER_PID=$!
else
dotnet-trace collect -p $APP_PID --format speedscope -o $OUTPUT_DIR/trace.json &
PROFILER_PID=$!
fi
sleep 5
# Run k6 load test
echo "Running: Running k6 load test ($DURATION, $VUS VUs)..."
cd ..
k6 run --duration $DURATION --vus $VUS \
--out json=$OUTPUT_DIR/k6-results.json \
k6-tests/load-test.js | tee $OUTPUT_DIR/k6-output.txt
echo "- k6 test completed"
# Wait for profiler to finish
wait $PROFILER_PID
echo "- Performance profile captured"
# Take final memory snapshot
echo "Snapshot: Final memory snapshot..."
if command -v dotmemory &> /dev/null; then
dotmemory get-snapshot $APP_PID --save-to=$OUTPUT_DIR/snapshot-end.dmw
echo "Comparing: Comparing memory snapshots..."
dotmemory compare $OUTPUT_DIR/snapshot-start.dmw $OUTPUT_DIR/snapshot-end.dmw \
--save-to=$OUTPUT_DIR/memory-comparison.html
else
dotnet-dump collect -p $APP_PID -o $OUTPUT_DIR/dump-end.dmp
fi
# Stop app
echo "Stopping: Stopping MinimalBlog..."
kill $APP_PID
# Generate reports
echo "Generating: Generating reports..."
if command -v dottrace &> /dev/null; then
dottrace report $OUTPUT_DIR/performance-profile.dtt \
--output=$OUTPUT_DIR/performance-report.html
fi
# Summary
echo ""
echo "========================================="
echo "- Profiling Complete!"
echo "========================================="
echo ""
echo "Results in: $OUTPUT_DIR/"
echo ""
echo "Files generated:"
ls -lh $OUTPUT_DIR/
echo ""
echo "Generating: View results:"
echo " - k6 output: cat $OUTPUT_DIR/k6-output.txt"
echo " - Performance profile: open $OUTPUT_DIR/performance-report.html"
echo " - Memory comparison: open $OUTPUT_DIR/memory-comparison.html"
echo " - Speedscope trace: https://www.speedscope.app/ (upload trace.json)"
echo ""
kör den:
chmod +x profile-under-load.sh
./profile-under-load.sh
bästa metoder: k6 M SK2 Profilering
Profilar alltid under realistisk belastning
- Använd k6 för att simulera produktionstrafikmönster
- Profile-idle-apps säger inget om verkliga prestationer
Ta flera korta bilder
- Innan lading (grundlinjen)
- Under last (aktiv
- Efter lading (kontrollera renning)
Fokusera på varma vägar först
- Rätta de största knorrarna först
- 80/20 regel
Messa före och efter
- Always re-run k6 test efter optimering
- Verifiera förbättring med profiler
Titta på regressioner
- Håll baseline-profiler i repo
- Vergleicha profiler i CI/CD
Profilutsläpp byggs
- Debug-byggnader har olika egenskaper
- Använd alltid
-c Releaseför profilering
Tools Summary
| verktyg | syfte | kostnad | МSK3 | bäst för | |||||
|---|---|---|---|---|---|---|---|---|---|
| k6 | Ladetestning | Gratis | Hitta vad som är långsamt | ||||||
| dotTrace | Leistungsprofilering | ||||||||
| dotMemory | minnesprofilering | betalad | minneskredningarM SK3 GC-problem M | ||||||
| dotnet-trace | Performance-profilering | Gratis | Grund CPU-profiling | ||||||
| dotnet-mätare | Verklig -tids mätningar | Gratis | Livlig övervakning | ||||||
| dotnet-dump | minnesdumpar | gratis | efteråt -mortemanalys | ||||||
| PerfView | avancerad profilering | gratis | djupa Windowsprofilering |
slutsats : Build Confidence Through Testing
Ladetestning med k6 ger dig självförtroendet att MinimalBlog kan hantera riktiga traffic.
- Installera k6 på vilken plattform som helst.
- Att skriva och köra olika typer av prestationerstest
- Bevestig att kaskande beteende fungerar som väntat.
- Hitta drivkraftsproblem innan de når produktionen
- Integrera lasttest i CI/CD-rör
- Att övervaka och tolka k6 mätningar
Ta med era nyckeln
- Börja med röktester - Verifiera grundläggande funktionalitet före djupare tester
- Testa kasching eksplicitt - MinimalBlog 's prestation beror på det
- Använd realistiska data - tomma filer inte representerar verkligt användande
- Monitora systemresurser - CPU
- Se meningsfulla trösklar - Baserat på faktiska krav
- Automatisera i CI/CD - Ta tag i regressioner tidigt
- Testa olika scenarior - Homepage , träffar, kategorierM SK3 fel
Vad vi lärde oss om MinimalBlog
Genom att testa k6 , kan vi kontrollera MinimalBlogs antaganden
- Vinnig: p
- Effektiv: låg minnesmängd , minimalt GC-tryck
- Skalbar: Handler 100+ samtida användare på blygsam hårdvara
- Tillförlitlig: < ≥0.1% felmängd under normal belastning
- Cache-effektivt: 10x snabbare reaktioner med varm kass
Nästa steg
Nu vet du hur man testar ett ASP.NET med k6:
- Att skapa ett basnivå-prestentialprofil för din konfiguration
- Att sätta upp automatiserade tester i ditt CI/CD-rör
- Runna dämpa tester innan större releases
- Monitora produktionsmått och jämför med testresultat
- Itera : test | -> | optimera |-> | test igen
Kom ihåg prestanstestning är en engångsmässig aktivitet. När du lägger in innehåll , modifierar koden , eller ändrar hosting-providerna M SK3 omarbetar MSC4 gör de här testerna för att säkerställa att din app är snabb och tillförlitlig
resurser
k6 Dokumentation
- De officiella k6 dokumenten - Komplett k6 dokumentation
- k6 JavaScript API - Alla tillgängliga k6 APIs
- k6 exempel - Example testscripts
- k6-extensioner - utöka k6 funktionalitet
- k6 mätningar - Understanding metrics
- k6 trösklar - Inställningspass
- k6 scenarier - avancerade testscenarier
GitHub-aktioner
- GitHub Actions Docs - Dokumentation för färdiga handlingar
- Workflow syntax - YAML syntaxreferens
- Att använda artefakter - Store-testresultat
- Sätt upp handlingar - k6 inställningsaktion
Profileringsverktyg
- dotnet-trace - Leistungsprofilering
- dotnet-mätare - Verklig
- dotnet-dump - Memory dumps
- JetBrains dotTrace - kommersiell profiler
- JetBrains dotMemory - minnes profiler
- accelerometer - Flame graph viewer
Monitoring & Visualisering
- k6 moln - Cloud -based kM SK2 testing
- k6 Reporter - HTML rapporter
- Grafana k6 Tappbord - kM SK1 mättavla
- Prometheus - Kollektion av mätinstrument
- Grafana - Metriksvisualisering
MinimalBlog
- MinimalBlogs introduktion - Varför finns MinimalBlog?
- källkod - Hela källan
- NuGet-paket - Installera via NuGet
- Demoprojekt - Ett fungerande exempel
ASP.NET Performance
- ASP.NET Core Performance - De bästa metoderna
- Caching i ASP.NET Core - Reaktionskasering
- Memory management - .NET GC
- Diagnostiska verktyg - .NET diagnostisering
lycklig testning!