Розробка Real-Time голосувань та опитувань на сайті

Уявіть: конференція на 10 000 учасників, організатори запускають голосування, а результати оновлюються раз на 30 секунд через звичайний AJAX-polling. Користувачі скаржаться на затримки, сервер падає від 10 000 запитів на секунду. Знайома ситуація? Обираючи real-time голосування, ви вирішуєте два зав

Розробка та обслуговування будь-яких видів сайтів:

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Розробка Real-Time голосувань та опитувань на сайті
Середній
~3-5 днів

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

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    984
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    1000

Уявіть: конференція на 10 000 учасників, організатори запускають голосування, а результати оновлюються раз на 30 секунд через звичайний AJAX-polling. Користувачі скаржаться на затримки, сервер падає від 10 000 запитів на секунду. Знайома ситуація? Обираючи real-time голосування, ви вирішуєте два завдання: миттєва доставка результатів та зниження навантаження на сервер. Наша команда має понад 5 років досвіду у розробці високонавантажених веб-систем і реалізувала такі рішення для 30+ проєктів — від корпоративних опитувань до масштабних прямих ефірів. Ми пропонуємо розробку систем голосування під ключ: від проектування до розгортання.

Чому SSE, а не polling?

Polling — це N запитів на секунду, де N — кількість користувачів. При 500 користувачах — 500 req/s даремно. SSE або WebSocket дають одне з'єднання на користувача, через яке сервер надсилає дані лише при зміні. Для голосувань підходять два механізми: SSE (Server-Sent Events) та WebSocket. Третій варіант — polling — не розглядаємо: 1 запит на секунду на 500 одночасних користувачів — це 500 req/s навантаження лише заради перевірки «нічого не змінилося». SSE — однонаправлений потік від сервера до клієнта, описаний у специфікації EventSource. Для опитувань в реальному часі цього достатньо: голос надсилається звичайним POST, результат приходить через SSE. WebSocket виправданий, якщо потрібен негайний зворотний зв'язок (анімація «ваш голос прийнято») або додаткові інтерактивні елементи. Зазначимо, що SSE у 3 рази простіший у реалізації, ніж WebSocket, при цьому покриває той самий базовий функціонал.

Параметр SSE WebSocket
Напрямок Однонаправлений (сервер → клієнт) Двонаправлений
Складність реалізації Низька (нативний EventSource) Середня (потрібен WebSocket-сервер)
Інфраструктура Не потребує sticky sessions Потребує sticky session або окремий сервер
Продуктивність До 10 000 з'єднань на один PHP-воркер До 100 000 з'єднань на Node.js
Підтримка браузерів Всі сучасні, крім IE Всі сучасні

Як запобігти дублюванню голосів?

Для авторизованих користувачів — unique constraint (option_id, user_id) та перевірка в контролері. Для анонімних голосувань — захист через IP + fingerprint. Fingerprint генерується на фронті (бібліотека fingerprintjs) і передається в заголовку. Це не абсолютний захист, але достатній для більшості випадків.

$fingerprint = $request->header('X-Client-Fingerprint'); $alreadyVoted = PollVote::where('poll_id', $poll->id) ->where(function ($q) use ($request, $fingerprint) { $q->where('ip', $request->ip()) ->orWhere('fingerprint', $fingerprint); })->exists(); 

Додатково можна використовувати Redis-блокування: Cache::lock('vote:'.$poll->id.':'.$userId, 10)->get() — це запобіжить одночасному надсиланню з одного акаунту.

Як масштабувати голосування на тисячі учасників?

PHP-застосунок з SSE тримає з'єднання відкритим. 1 000 одночасних користувачів = 1 000 PHP-воркерів. Це дорого. Рішення: винести broadcast через Pusher або Laravel Echo Server (socket.io). Тоді SSE-контролер більше не потрібен — клієнт підписується на канал, сервер публікує подію poll.updated в Redis, Laravel Echo транслює всім підписникам. Наприклад, для проєкту з 1000 учасників використання Pusher замість прямих SSE-воркерів може зменшити витрати на сервери з $500 до $150 на місяць — економія понад 70%.

// Після запису голосу broadcast(new PollUpdated($poll->id, $counts))->toOthers(); 
Echo.channel(`poll.${pollId}`) .listen('PollUpdated', ({ counts }) => updateBars(counts)); 

Така архітектура тримає сотні тисяч підключень на одному процесі Node.js. Для моніторингу використовуйте Laravel Horizon: він показує кількість активних SSE-воркерів та час відгуку. Pusher для голосування витримує навантаження у 2-3 рази більше, ніж власний SSE-воркер, при тих же витратах.

Схема даних та оптимізація

Схема бази даних
CREATE TABLE polls ( id BIGSERIAL PRIMARY KEY, title VARCHAR(500) NOT NULL, is_multiple BOOLEAN NOT NULL DEFAULT false, is_active BOOLEAN NOT NULL DEFAULT true, ends_at TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT NOW() ); CREATE TABLE poll_options ( id BIGSERIAL PRIMARY KEY, poll_id BIGINT NOT NULL REFERENCES polls(id) ON DELETE CASCADE, label VARCHAR(255) NOT NULL, position SMALLINT NOT NULL DEFAULT 0 ); CREATE TABLE poll_votes ( id BIGSERIAL PRIMARY KEY, option_id BIGINT NOT NULL REFERENCES poll_options(id), user_id BIGINT REFERENCES users(id), ip INET, voted_at TIMESTAMP NOT NULL DEFAULT NOW(), UNIQUE(option_id, user_id) ); 

Агрегація рахується через materialized view або прямим COUNT. При піковому навантаженні (прямий ефір, 5 000+ учасників) краще зберігати лічильники окремо та інкрементувати через Redis: HINCRBY poll:42:counts 1 1.

Клієнтська частина та надсилання голосу

const pollId = 42; const source = new EventSource(`/api/polls/${pollId}/stream`); source.onmessage = (event) => { const { counts } = JSON.parse(event.data); updateBars(counts); }; source.onerror = () => { console.warn('SSE reconnecting...'); }; function updateBars(counts) { const total = Object.values(counts).reduce((a, b) => a + Number(b), 0); document.querySelectorAll('[data-option-id]').forEach(el => { const id = el.dataset.optionId; const pct = total > 0 ? Math.round((counts[id] || 0) / total * 100) : 0; el.querySelector('.bar').style.width = pct + '%'; el.querySelector('.label').textContent = pct + '%'; }); } async function vote(optionId) { const resp = await fetch(`/api/polls/${pollId}/vote`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ option_id: optionId }), }); if (resp.status === 409) { showMessage('Ви вже голосували'); } } 

Як реалізувати SSE-ендпоінт на Laravel

Кроки реалізації:

  1. Створіть маршрут, що повертає streaming response.
  2. У контролері в циклі отримуйте поточну статистику через Redis або COUNT.
  3. Відправляйте дані у форматі SSE: echo "data: {$data}\n\n";.
  4. Додайте обов'язковий заголовок X-Accel-Buffering: no для Nginx.
  5. Перевіряйте connection_aborted() для коректного завершення.
Route::get('/api/polls/{poll}/stream', function (Poll $poll) { return response()->stream(function () use ($poll) { while (true) { if (connection_aborted()) break; $counts = PollVote::selectRaw('option_id, COUNT(*) as votes') ->whereIn('option_id', $poll->options->pluck('id')) ->groupBy('option_id') ->pluck('votes', 'option_id'); $data = json_encode(['counts' => $counts, 'ts' => now()->timestamp]); echo "data: {$data}\n\n"; ob_flush(); flush(); sleep(2); } }, 200, [ 'Content-Type' => 'text/event-stream', 'Cache-Control' => 'no-cache', 'X-Accel-Buffering' => 'no', ]); }); 

Як тестувати real-time голосування?

Використовуйте інструменти: Postman для надсилання голосів, k6 для навантажувального тестування, вбудовану консоль браузера для перевірки SSE-з'єднань. Основні сценарії: 1) перевірка отримання оновлень після голосування; 2) симуляція одночасних голосів від тисячі користувачів; 3) перевірка стійкості при обриві з'єднання (SSE автоматично перепідключається).

Типові помилки та їх вирішення

  • N+1 запити: при отриманні списку голосів з підвантаженням опцій через lazy loading. Використовуйте with('options') в Eloquent.
  • Відсутність X-Accel-Buffering: без цього заголовка Nginx буферизує SSE-потік, і користувачі бачать дані пачками.
  • Ігнорування connection_aborted(): без цього PHP-воркери продовжують висіти, споживаючи пам'ять.
  • Невживання Redis для лічильників: прямий COUNT з БД при кожному оновленні створює навантаження. Redis інкременти в рази швидші.

Що входить у роботу

  • Технічне завдання та узгодження архітектури
  • Розробка системи голосування під ключ
  • Документація з інтеграції для вашої команди
  • Навчання адміністраторів та розробників
  • Налаштування моніторингу (Laravel Horizon або аналог)
  • 30-денна гарантія на відсутність багів та підтримка після запуску

Строки розробки

Етап Час
Базове голосування (SSE, авторизовані) 2–3 дні
Анонімне голосування + anti-duplicate +1 день
Багатоваріантні опитування + історія +1 день
Масштабування через Pusher/Echo +2 дні
Адміністративний інтерфейс 2–3 дні

Ми пропонуємо реалізацію голосування на сайті з урахуванням усіх сучасних вимог. Наша команда має 5+ років досвіду та 30+ успішних проєктів. Пишіть нам, і ми оцінимо ваш проект за 1 день — отримайте детальний план і точні строки. Замовте консультацію, щоб переконатися, що ваше голосування працюватиме без збоїв при будь-якому навантаженні.