Симуляция реального трафика: k6, логи, сценарии

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Симуляция реального трафика: k6, логи, сценарии
Средний
~2-3 дня
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1358
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1251
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    956
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1188
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    929
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    947

Воспроизведение реального трафика: от логов до k6-сценариев

При нагрузочном тестировании типичная ошибка — использовать равномерную подачу запросов с фиксированным числом виртуальных пользователей. В реальности трафик имеет пики (утром и вечером), разные типы пользователей (мобильные браузеры, API-клиенты), случайные паузы и распределение 80/20. Например, 80% запросов приходятся на 20% страниц. Синтетические тесты часто пропускают проблемы с кэшированием, сессионным состоянием и конкурентностью.

Мы предлагаем подход, основанный на анализе реального трафика из логов Nginx или Google Analytics, и генерации сценариев для k6, которые точно воспроизводят поведение настоящих пользователей. Реалистичное тестирование выявляет в три раза больше узких мест, чем равномерное. Заказчики экономят в среднем 40 000–80 000 рублей на отладке благодаря раннему обнаружению проблем. Свяжитесь с нами для обсуждения вашего проекта.

Как работает анализ логов?

# Извлечь паттерны из 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. Анализ access-логов позволяет выделить 20% эндпоинтов, генерирующих 80% трафика, согласно закону Парето.

Ограничения равномерного трафика

Равномерная нагрузка не создаёт эффекта "толпы": когда 1000 пользователей одновременно переходят на один товар после публикации в соцсетях. Она не проверяет сессионное кэширование, блокировки БД при конкурентной записи или деградацию под постоянными пиками. Реалистичная симуляция с распределением Pareto (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 раза более точное моделирование по сравнению с равномерным.

Распределение Pareto в k6

Реальный трафик: 20% страниц получают 80% трафика. В k6 это моделируется функцией, генерирующей ID по степенному закону:

// Генератор 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+

Сравнение синтетического и реалистичного тестирования

Характеристика Синтетическое тестирование Реалистичное тестирование
Тип трафика Равномерный, заданный вручную Воспроизводит реальные паттерны (пики, сессии)
Поведение пользователей Одинаковые сценарии для всех VU Разные сценарии (анонимы, авторизованные, API)
Путь пользователя Линейный (главная → товар → корзина) Ветвистый с вероятностными переходами
Выявление узких мест Только пропускная способность Кэширование, медленные эндпоинты, конкурентность
Время подготовки Часы Дни (требуется анализ логов)

Процесс работы

  1. Анализ логов: сбор access-логов Nginx или данных Google Analytics, извлечение паттернов (эндпоинты, статусы, почасовое распределение).
  2. Проектирование сценариев: определение типов пользователей (анонимные, авторизованные, API), построение вероятностных моделей поведения.
  3. Реализация на k6: написание JavaScript-сценариев с executors, случайными паузами и ветвлениями.
  4. Тестовый прогон: запуск теста на staging-окружении, сбор метрик (LCP, CLS, TTFB, ошибки).
  5. Анализ и отчёт: выявление узких мест, сравнение с baseline, рекомендации по оптимизации.
Этап Длительность Результат
Анализ логов 0.5–1 день JSON-профиль эндпоинтов и распределений
Проектирование сценариев 0.5–1 день Вероятностные модели для каждого типа пользователя
Реализация на k6 1–2 дня Рабочие k6-скрипты с executors
Тестовый прогон 1 день Метрики, графики, пороговые значения
Отчёт и рекомендации 0.5 дня Документ с анализом и планом оптимизации
Пример профиля нагрузки по часамТрафик распределён неравномерно: пик в 10-11 утра и 18-19 вечера. В остальное время — спад. Мы задаём веса для каждого часа и генерируем stages для k6, чтобы нагрузка соответствовала реальному дневному циклу.

Результаты и сроки

  • Документация сценария с описанием поведения каждого типа пользователей.
  • Конфигурации k6 (options, thresholds, пороговые значения).
  • Отчёт с результатами тестирования: графики нагрузки, процентили времени ответа, ошибки.
  • Рекомендации по оптимизации производительности (индексы БД, кэширование, асинхронные очереди).
  • Поддержка при первом запуске и интерпретации результатов. Мы гарантируем, что все сценарии проверены на наших тестовых стендах.

Средняя экономия на отладке — 40 000–80 000 руб. за счёт раннего обнаружения узких мест. Стоимость разработки сценария — от 30 000 до 50 000 руб.

Разработка реалистичного сценария нагрузочного теста на основе анализа реального трафика занимает от 2 до 5 рабочих дней. Стоимость рассчитывается индивидуально после ознакомления с вашими данными.

Закажите реалистичный сценарий нагрузочного тестирования под ключ. Получите консультацию инженеров.

Почему юнит-тесты важны, но не панацея?

Баг, найденный юнит-тестом, стоит минуты исправления. Тот же баг в продакшене — часы инцидента, компенсации и потеря доверия. На проекте интернет-магазина ошибка в расчёте скидки прошла ручное тестирование, попала в прод и за 4 часа обработала 37 заказов по нулевой цене. Автотест на граничные случаи расчёта поймал бы её при первом же push. Оцените свой проект — мы проведём аудит текущего покрытия и дадим рекомендации.

Jest — стандарт для JavaScript/TypeScript, но юнит-тесты оправданы только там, где есть изолированная логика: функции трансформации, валидаторы, бизнес-правила, утилиты. Тестировать React-компоненты через Jest + Testing Library правильно для поведенческих тестов: «кнопка появляется после загрузки», «форма показывает ошибку при пустом email». Снепшот-тесты (toMatchSnapshot) — ловушка: они ломаются при любом изменении вёрстки и становятся шумом, который разработчики обновляют не глядя. Покрытие кода (code coverage) — плохая метрика качества: 80% coverage можно получить тестами, которые ничего не проверяют. Coverage показывает, что код выполнился, а не то, что он работает правильно.

Критерий Jest Vitest
Скорость для больших проектов Средняя (Babel-трансформация) В 10–20 раз быстрее (ES modules)
Интеграция с Vite Через плагин Нативная
Монорепозитории Требует конфигурации Из коробки

Vitest как альтернатива Jest для Vite-проектов: в 10–20 раз быстрее за счёт нативных ES modules без трансформации через Babel. Для монорепозиториев с тысячами тестов разница в скорости ощутима. Подробнее о юнит-тестировании.

Как настроить E2E тесты, которые не будут flaky?

Playwright обошёл Cypress по ключевым параметрам: нативная поддержка multi-tab, multi-origin, iframe; параллельное выполнение на уровне тестов; WebKit, Firefox, Chromium из коробки; нет iframe для приложения — тесты работают в реальном браузере.

Playwright codegen записывает действия и генерирует тест — хорошая точка старта, но сгенерированный код нужно рефакторить. Локаторы по text content хрупки: getByRole('button', { name: 'Оформить заказ' }) — устойчивее, чем locator('.btn-primary').

Page Object Model — стандарт организации E2E тестов. Каждая страница — отдельный класс с методами вместо прямых локаторов. Когда кнопка переехала из хедера в сайдбар — меняем в одном месте, не ищем по всем тестам.

Как избежать flaky тестов? Типичная проблема — flaky tests. Причины: race condition между запросом и рендером, анимации без ожидания, зависимость от внешних API. Решение: `page.waitForResponse()` вместо `page.waitForTimeout()`, мокирование внешних API через `page.route()`.
// Плохо
await page.click('#submit');
await page.waitForTimeout(2000);
await expect(page.locator('.success')).toBeVisible();

// Хорошо
await page.click('#submit');
await page.waitForResponse(resp =>
  resp.url().includes('/api/orders') && resp.status() === 201
);
await expect(page.getByRole('alert', { name: /заказ создан/i })).toBeVisible();

Наши инженеры гарантируют стабильность тестов в CI. Документация Playwright — основной инструмент на проектах с миллионами пользователей.

Нагрузочное тестирование с k6

k6 — инструмент для нагрузочного тестирования с JavaScript API. Сценарии пишутся как код, версионируются в git, запускаются в CI. Три основных сценария:

  • Spike test — резкий рост нагрузки: 0 → 1000 пользователей за 30 секунд. Имитирует запуск рекламной кампании. Показывает способность системы реагировать на пики.
  • Soak test — стабильная нагрузка на 2–4 часа. Выявляет memory leaks, connection pool exhaustion, деградацию производительности.
  • Stress test — нагрузка выше расчётной (150–200% от ожидаемого пика). Показывает точку отказа и graceful degradation.

Пороговые значения:

thresholds: {
  http_req_duration: ['p95<500', 'p99<1000'],
  http_req_failed: ['rate<0.01'],
}

p95 < 500ms означает: 95% запросов отвечают быстрее полусекунды. Если порог не выполняется — k6 завершается с кодом ошибки, CI-пайплайн падает.

На одном проекте интернет-магазина мы выявили деградацию API на 4-й час теста: p95 вырос с 200ms до 2s из-за утечки соединений. После оптимизации клиент сэкономил около $15,000 в год на инцидентах и лишних ресурсах. Получите аналогичный аудит вашего проекта — закажите нагрузочное тестирование.

Как Core Web Vitals влияют на ранжирование?

Google использует Core Web Vitals в ранжировании. Lighthouse CLI в CI-пайплайне: при каждом деплое проверяем, что LCP < 2.5s, CLS < 0.1, INP < 200ms. Подробнее о веб-производительности. Реальные проблемы, которые Lighthouse находит:

  • Hero image без width/height атрибутов: CLS 0.35 при загрузке.
  • JavaScript-бандл 2.1MB синхронно блокирует парсинг: INP 450ms.
  • Шрифты без font-display: swap: невидимый текст до загрузки шрифта (FOIT).
  • Неоптимизированный hero image 4MB: LCP 8.2s.

Lighthouse CI (lhci) сохраняет историю метрик и отправляет комментарий к PR с деградацией. По данным Google, 53% пользователей покидают сайт при загрузке дольше 3 секунд — наши тесты предотвращают такие потери.

Пирамида тестирования в проекте

Уровень Инструмент Количество Скорость
Юнит Vitest/Jest Много (тысячи) <5 мин
Интеграция Vitest + supertest Среднее 5–15 мин
E2E Playwright Немного (happy path) 10–30 мин
Нагрузка k6 По расписанию 30–60 мин
Performance Lighthouse CI При каждом деплое 5 мин

Что входит в работу?

  • Аудит текущего покрытия и определение критических user flows.
  • Написание unit-тестов для ключевой бизнес-логики, интеграционных тестов для API, E2E для сценариев пользователя.
  • Настройка параллельного выполнения в CI (sharded workers для Playwright).
  • Нагрузочное тестирование с отчётом и рекомендациями.
  • Документация по тест-кейсам, обучение вашей команды работе с тестами.
  • Гарантийная поддержка 1 месяц после внедрения.

Процесс работы

  1. Аналитика — аудит текущего тестирования, выявление слабых мест, определение приоритетов.
  2. Проектирование — выбор инструментов, написание тест-плана, согласование.
  3. Реализация — написание тестов, интеграция в CI.
  4. Тестирование — прогон всех уровней, анализ результатов, исправление ошибок.
  5. Деплой — запуск в прод, мониторинг метрик, обучение команды.

Сроки

Настройка полного тест-пайплайна (Jest + Playwright + k6 + Lighthouse CI) с нуля: 2–4 недели. Покрытие E2E-тестами существующего проекта (20–30 сценариев): 3–6 недель. Нагрузочное тестирование с отчётом и рекомендациями: 1–2 недели. Стоимость рассчитывается индивидуально после аудита.

Готовы обсудить ваш проект? Оставьте заявку — мы проведём аудит текущего тестирования бесплатно и предложим план с экономией до 60% времени на инциденты. Получите консультацию по тестированию веб-приложений — напишите нам.