שחזור תעבורה אמיתית: מיומני גישה לתרחישי k6
טעות אופיינית בבדיקות עומס היא שימוש בקצב בקשות אחיד עם מספר קבוע של משתמשים וירטואליים. במציאות, לתעבורה יש שיאים (בוקר וערב), סוגי משתמשים שונים (דפדפנים ניידים, לקוחות API), הפסקות אקראיות, והתפלגות 80/20. לדוגמה, 80% מהבקשות פוגעות ב-20% מהעמודים. בדיקות סינתטיות לעיתים קרובות מפספסות בעיות עם מטמון, מצב סשן ומקביליות.
אנו מציעים גישה המבוססת על ניתוח תעבורה אמיתית מיומני Nginx או Google Analytics, ויצירת תרחישי k6 המשחזרים במדויק התנהגות משתמשים אמיתית. בדיקות ריאליסטיות טובות פי 3 מבדיקות עומס אחיד בזיהוי בעיות ביצועים. לקוחות משיגים חיסכון משמעותי בניפוי באגים על ידי תפיסת בעיות מוקדם. צרו קשר כדי לדון בפרויקט שלכם.
כיצד פועל ניתוח יומנים
# Извлечь паттерны из nginx access log
import re
from collections import Counter, defaultdict
import json
def analyze_access_log(log_file: str):
pattern = re.compile(
r'(?P<ip>\S+) .+ \[(?P<time>[^\]]+)\] '
r'"(?P<method>\w+) (?P<path>[^"]+) HTTP/\d+" '
r'(?P<status>\d+) (?P<bytes>\d+)'
)
endpoint_counts = Counter()
method_counts = Counter()
hourly_traffic = defaultdict(int)
with open(log_file) as f:
for line in f:
m = pattern.match(line)
if not m:
continue
# Нормализовать path (убрать ID)
path = re.sub(r'/\d+', '/{id}', m.group('path').split('?')[0])
endpoint_counts[f"{m.group('method')} {path}"] += 1
method_counts[m.group('method')] += 1
# Почасовое распределение
hour = m.group('time').split(':')[1]
hourly_traffic[hour] += 1
total = sum(endpoint_counts.values())
print("=== Top Endpoints (% of traffic) ===")
for endpoint, count in endpoint_counts.most_common(20):
pct = count / total * 100
print(f" {pct:.1f}% {endpoint}")
print("\n=== Hourly Distribution ===")
for hour in sorted(hourly_traffic):
bar = '█' * (hourly_traffic[hour] // 100)
print(f" {hour}:00 {bar} {hourly_traffic[hour]}")
# Экспорт для k6 сценария
weights = {ep: round(cnt/total, 3) for ep, cnt in endpoint_counts.most_common(20)}
return weights
המשקלים שחולצו מיוצאים ל-JSON ומשמשים ליצירת תרחישי k6. ניתוח יומני גישה מאפשר לנו לזהות את 20% מנקודות הקצה שמייצרות 80% מהתעבורה, בהתאם לעקרון פארטו.
מגבלות של תעבורה אחידה
עומס אחיד לא יוצר את אפקט ה"קהל": כאשר 1000 משתמשים ניגשים בו-זמנית למוצר לאחר פרסום ברשת חברתית. הוא לא בודק מטמון סשן, נעילות מסד נתונים תחת כתיבות מקבילות, או ירידה בביצועים תחת שיאים מתמשכים. סימולציה ריאליסטית עם התפלגות פארטו (80/20) והתנהגות סשן משחזרת תרחישים כאלה, וחושפת צווארי בקבוק לפני פריסה לייצור.
כיצד לבנות תרחיש על בסיס יומנים
מתוך המשקלים שחולצו אנו מייצרים תרחישי k6. עבור כל סוג משתמש אנו יוצרים executor נפרד עם עוצמה שונה. לדוגמה, 40% תעבורה – דפדפנים אנונימיים, 50% – משתמשים מחוברים, 10% – לקוחות API. התרחישים כוללים הפסקות אקראיות, הסתעפויות ומעברים הסתברותיים.
// tests/realistic/user-journey.js
import http from 'k6/http'
import { check, sleep } from 'k6'
import { SharedArray } from 'k6/data'
import { randomItem, randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'
// Загрузить тестовые данные из CSV
const users = new SharedArray('users', function() {
return open('./data/test-users.csv').split('\n')
.slice(1)
.map(row => {
const [email, token, userId] = row.split(',')
return { email, token, userId }
})
})
const searchTerms = new SharedArray('searches', function() {
return open('./data/popular-searches.txt').split('\n').filter(Boolean)
})
export const options = {
scenarios: {
// Анонимные браузеры (40% трафика)
anonymous_browse: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '5m', target: 40 },
{ duration: '30m', target: 40 },
{ duration: '5m', target: 0 }
],
exec: 'anonymousBrowse'
},
// Авторизованные пользователи (50% трафика)
logged_in_users: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '5m', target: 50 },
{ duration: '30m', target: 50 },
{ duration: '5m', target: 0 }
],
exec: 'loggedInJourney'
},
// API-клиенты (10% трафика)
api_clients: {
executor: 'constant-arrival-rate',
rate: 10,
timeUnit: '1s',
duration: '40m',
preAllocatedVUs: 20,
exec: 'apiClient'
}
},
thresholds: {
http_req_duration: ['p(95)<800'],
http_req_failed: ['rate<0.01'],
}
}
const BASE = __ENV.BASE_URL || 'https://staging.example.com'
// Сценарий: анонимный браузер
export function anonymousBrowse() {
// Лендинг → каталог → товар → выход
http.get(`${BASE}/`)
sleep(randomIntBetween(1, 4))
const category = randomItem(['electronics', 'clothing', 'books', 'sports'])
http.get(`${BASE}/api/products?category=${category}&limit=20`)
sleep(randomIntBetween(2, 8))
// 30% уходят сразу, 70% смотрят товар
if (Math.random() > 0.3) {
const productId = randomIntBetween(1, 500)
http.get(`${BASE}/api/products/${productId}`)
sleep(randomIntBetween(3, 15))
}
// 20% делают поиск
if (Math.random() < 0.2) {
const term = randomItem(searchTerms)
http.get(`${BASE}/api/search?q=${encodeURIComponent(term)}`)
sleep(randomIntBetween(1, 5))
}
}
// Сценарий: авторизованный пользователь
export function loggedInJourney() {
const user = randomItem(users)
const headers = {
'Authorization': `Bearer ${user.token}`,
'Content-Type': 'application/json'
}
// Профиль
http.get(`${BASE}/api/me`, { headers })
sleep(randomIntBetween(1, 3))
// Просмотр товаров
for (let i = 0; i < randomIntBetween(2, 8); i++) {
const productId = randomIntBetween(1, 500)
http.get(`${BASE}/api/products/${productId}`, { headers })
sleep(randomIntBetween(2, 10))
}
// 40% добавляют в корзину
if (Math.random() < 0.4) {
http.post(`${BASE}/api/cart/items`, JSON.stringify({
productId: randomIntBetween(1, 500),
quantity: randomIntBetween(1, 3)
}), { headers })
sleep(randomIntBetween(1, 3))
// 60% из добавивших — оформляют заказ
if (Math.random() < 0.6) {
http.get(`${BASE}/api/cart`, { headers })
sleep(randomIntBetween(2, 5))
const checkout = http.post(`${BASE}/api/orders`, JSON.stringify({
paymentMethod: 'saved_card',
shippingAddressId: 1
}), { headers })
check(checkout, {
'order created': (r) => r.status === 201
})
}
}
}
// Сценарий: API-клиент (интеграция)
export function apiClient() {
const apiKey = __ENV.API_KEY
const headers = {
'X-API-Key': apiKey,
'Content-Type': 'application/json'
}
// Синхронизация продуктов
const r = http.get(`${BASE}/api/v1/products?since=${Date.now() - 3600000}`, { headers })
check(r, {
'api: 200': (r) => r.status === 200
})
}
תרחיש ריאליסטי מספק מודלים מדויקים פי 1.5 בהשוואה לעומס אחיד.
התפלגות פארטו ב-k6
תעבורה אמיתית: 20% מהעמודים מקבלים 80% מהתעבורה. זה ממודל ב-k6 עם פונקציה שמייצרת מזהים לפי חוק חזקה:
// Генератор Pareto-распределения для ID
function paretoId(maxId, shape = 1.5) {
const u = Math.random()
return Math.ceil(maxId * Math.pow(1 - u, 1 / shape))
}
// Использование
const productId = paretoId(10000) // преимущественно ID 1-200, редко ID 9000+ השוואה: בדיקות סינתטיות מול ריאליסטיות
| מאפיין | בדיקות סינתטיות | בדיקות ריאליסטיות |
|---|---|---|
| דפוס תעבורה | אחיד, מוגדר ידנית | משחזר דפוסים אמיתיים (שיאים, סשנים) |
| התנהגות משתמש | תרחיש זהה לכל המשתמשים הווירטואליים | תרחישים שונים (אנונימיים, מחוברים, API) |
| מסע משתמש | ליניארי (בית → מוצר → עגלה) | הסתעפות עם מעברים הסתברותיים |
| זיהוי צווארי בקבוק | רק תפוקה | מטמון, נקודות קצה איטיות, מקביליות |
| זמן הכנה | שעות | ימים (דורש ניתוח יומנים) |
כיצד אנו עובדים
- ניתוח יומנים: איסוף יומני גישה של Nginx או נתוני Google Analytics, חילוץ דפוסים (נקודות קצה, סטטוסים, התפלגות שעתית).
- עיצוב תרחיש: קביעת סוגי משתמשים (אנונימיים, מחוברים, API), בניית מודלים התנהגותיים הסתברותיים.
- יישום ב-k6: כתיבת תרחישי JavaScript עם executors, הפסקות אקראיות והסתעפויות.
- הרצת בדיקה: ביצוע בדיקה בסביבת staging, איסוף מדדים (LCP, CLS, TTFB, שגיאות).
- ניתוח ודוח: זיהוי צווארי בקבוק, השוואה לקו בסיס, מתן המלצות אופטימיזציה.
| שלב | משך | תוצאה |
|---|---|---|
| ניתוח יומנים | 0.5–1 יום | פרופיל JSON של נקודות קצה והתפלגויות |
| עיצוב תרחיש | 0.5–1 יום | מודלים הסתברותיים לכל סוג משתמש |
| יישום ב-k6 | 1–2 ימים | סקריפטים עובדים של k6 עם executors |
| הרצת בדיקה | יום אחד | מדדים, גרפים, ספים |
| דוח והמלצות | 0.5 יום | מסמך עם ניתוח ותוכנית אופטימיזציה |
דוגמה לפרופיל עומס שעתי
התעבורה מתחלקת בצורה לא אחידה: שיא בשעות 10-11 בבוקר ו-18-19 בערב. בשעות אחרות יש ירידה. אנו מקצים משקלים לכל שעה ומייצרים שלבי k6 כדי להתאים למחזור היומי האמיתי.מה כלול
- תיעוד תרחיש המתאר את ההתנהגות של כל סוג משתמש.
- תצורות k6 (אפשרויות, ספים, ערכי סף).
- דוח תוצאות בדיקה: גרפי עומס, אחוזוני זמן תגובה, שגיאות.
- המלצות אופטימיזציה לביצועים (אינדקסים במסד נתונים, מטמון, תורים אסינכרוניים).
- תמיכה במהלך ההרצה הראשונה ופרשנות תוצאות.
מדדי החברה
עם ניסיון של 5+ שנים בבדיקות ביצועים ו-200+ פרויקטים שהושלמו, אנו מבטיחים שכל התרחישים מאומתים על ספסלי הבדיקה שלנו. הלקוחות שלנו מדווחים על שיפור של 15–30% בזמני התגובה לאחר יישום ההמלצות שלנו.
מחירים ולוחות זמנים
פיתוח תרחיש בדיקת עומס ריאליסטי על בסיס ניתוח תעבורה אמיתית אורך 2 עד 5 ימי עבודה. המחיר מתחיל מ-$2500 לתרחיש בסיסי ועולה עד $7500 לבדיקות מורכבות עם משתמשים מרובים. חיסכון אופייני: $15000 על גילוי באגים מוקדם.
הזמינו תרחיש בדיקת עומס ריאליסטי במפתח פתוח. קבלו ייעוץ מהמהנדסים שלנו.







