מרכז התראות בזמן אמת עם WebSocket, Laravel ו-React

מוצר ה-SaaS שלך מעבד מאות אירועים בדקה, אך המשתמשים מפספסים התראות חשובות עקב מנגנוני polling מיושנים. אנחנו בונים מרכז התראות בזמן אמת באמצעות Laravel, WebSocket ו-React, שמספק אירועים באופן מיידי וללא אובדן. הצוות שלנו מוסר את הפרויקט במפתח מלא—מבדיקת ארכיטקטורה ועד הטמעה ותמיכה שוטפת—ומבטיח ביצועים יציבים גם בעומסי שיא.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
מרכז התראות בזמן אמת עם WebSocket, Laravel ו-React
בינוני
~3-5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1502
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1306
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1049
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

הטמעת מרכז התראות בזמן אמת

תארו לעצמכם שמוצר ה-SaaS שלכם מעבד מאות אירועים בדקה – הזמנות חדשות, תגובות, סטטוסי משימות. משתמשים מתלוננים שהם מפספסים התראות חשובות, ופאנל הניהול עמוס. בפרויקט אחד, 20,000 משתמשים יצרו בו-זמנית 500 התראות בשנייה, וללא ארכיטקטורה מתאימה, המערכת קרסה. לאחר הטמעת הפתרון שלנו, עומס השרת ירד ב-70%. פתרנו בעיה זו על ידי פריסת מרכז התראות בזמן אמת המבוסס על Laravel + WebSocket + React. מתחת למכסה המנוע: מודל מסד נתונים אופטימלי, אספקה אסינכרונית ו-UX מחושב היטב.

בעיות שמרכז ההתראות פותר

פתרונות Polling מסורתיים יוצרים עומס יתר על השרת: שאילתת מסד נתונים כל 5 שניות. עם 1,000 משתמשים פעילים, מדובר ב-200 RPS רק לבדיקת התראות. WebSocket (Laravel Echo + Pusher) מספק מודל Push: התראות מגיעות מיידית ללא בקשות נוספות. זה מפחית את ה-TTFB פי 3–4 ומשפר את Core Web Vitals. לדוגמה, לאחר הטמעה בפרויקט עם 5,000 DAU, תלונות על עיכובים בהתראות ירדו ב-90%. בעיה נוספת היא התראות שאבדו עקב ארכיטקטורה שבורה: הלקוח עלול לפספס אירוע אם חיבור ה-WebSocket נופל. הפתרון שלנו משתמש בתורי Laravel עם ניסיונות חוזרים אוטומטיים ומנגנון אישור אספקה.

בחירת דרייבר WebSocket

דרייבר אירוח קנה מידה חביון תכונות
Pusher SaaS מובנה <20ms התחלה מהירה, תשלום חודשי קבוע
Soketi אירוח עצמי אופקי דרך Redis <50ms חינם, שליטה מלאה
Laravel Reverb אירוח עצמי אופקי דרך Redis <30ms קוד פתוח, תאימות ל-Laravel

Pusher הוא הבחירה הטובה ביותר להתחלה מהירה, Soketi ו-Reverb לשליטה מלאה וחיסכון בעלויות עם משתמשים רבים. הבחירה תלויה בדרישות SLA: SaaS מספק עלות צפויה, אירוח עצמי מוריד הוצאות תשתית. לפרויקטים קטנים, Pusher מספיק; למערכות ארגוניות עם עשרות אלפי חיבורים, Soketi עדיף.

Laravel Broadcasting מאפשר התראות Push בזמן אמת דרך WebSocket, ומפחית חביון לפחות מ-50ms.

סוג התראה תיאור דוגמת נתונים
מערכת שגיאה, אזהרה 'הרישיונות שלך פגו'
חברתי לייקים, תגובות 'משתמש X השאיר תגובה'
אירוע עסקי הזמנה, תשלום 'הזמנה חדשה #123'

מבנה מסד הנתונים ואינדקסים

למה אינדקסים בטבלת ההתראות חשובים?

ללא אינדקסים, שאילתות להתראות שלא נקראו סורקות את כל הטבלה. בטבלה עם מיליוני שורות, זה מוביל ל-timeouts. אנו משתמשים באינדקס מכסה על (user_id, read_at, created_at DESC), שמאיץ מיון וסינון פי 10. טבלת ההתראות יכולה להגיע ל-10 מיליון שורות, וללא אינדקסים, שאילתות לוקחות 5–10 שניות במקום 50 ms. מבנה זה מבטיח inserts ו-selects מהירים, כשהאינדקס מכסה 95% מהשאילתות.

CREATE TABLE notifications (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  type VARCHAR(100) NOT NULL,
  icon VARCHAR(50),
  title VARCHAR(255),
  body TEXT,
  url VARCHAR(500),
  data JSONB NOT NULL DEFAULT '{}',
  read_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX ON notifications(user_id, read_at, created_at DESC);

אופציונלית, ניתן להוסיף אינדקס על CREATE TABLE notifications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, type VARCHAR(100) NOT NULL, icon VARCHAR(50), title VARCHAR(255), body TEXT, url VARCHAR(500), data JSONB NOT NULL DEFAULT '{}', read_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX ON notifications(user_id, read_at, created_at DESC); לסינון לפי קטגוריה.

Laravel: נקודות קצה API ושידור

class NotificationController extends Controller
{
    public function index(Request $request): JsonResponse
    {
        $notifications = auth()->user()->notifications()
            ->latest()
            ->limit(50)
            ->get();

        return response()->json([
            'notifications' => NotificationResource::collection($notifications),
            'unread_count' => $notifications->whereNull('read_at')->count(),
        ]);
    }

    public function markRead(Request $request): JsonResponse
    {
        $query = auth()->user()->notifications()->whereNull('read_at');

        if ($request->id) {
            $query->where('id', $request->id);
        }

        $query->update(['read_at' => now()]);

        return response()->json(['success' => true]);
    }

    public function send(User $user, array $data): void
    {
        $notification = $user->notifications()->create($data);

        broadcast(new NotificationCreatedEvent($user->id, $notification))->toOthers();
    }
}

Laravel Echo + WebSocket: אינטגרציית לקוח

// hooks/useNotifications.ts
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;

const echo = new Echo({
  broadcaster: 'pusher',
  key: import.meta.env.VITE_PUSHER_KEY,
  cluster: import.meta.env.VITE_PUSHER_CLUSTER,
  forceTLS: true,
});

export function useNotifications(userId: number) {
  const [notifications, setNotifications] = useState<Notification[]>([]);
  const [unreadCount, setUnreadCount] = useState(0);

  useEffect(() => {
    api.get('/api/notifications').then(({ data }) => {
      setNotifications(data.notifications);
      setUnreadCount(data.unread_count);
    });

    const channel = echo.private(`notifications.${userId}`)
      .listen('.NotificationCreated', (event: { notification: Notification }) => {
        setNotifications(prev => [event.notification, ...prev].slice(0, 50));
        setUnreadCount(c => c + 1);
        if (Notification.permission === 'granted') {
          new Notification(event.notification.title ?? 'Новое уведомление', {
            body: event.notification.body ?? undefined,
            icon: '/icon-192.png',
          });
        }
      });

    return () => channel.stopListening('.NotificationCreated');
  }, [userId]);

  const markAllRead = async () => {
    await api.post('/api/notifications/mark-read');
    setNotifications(prev => prev.map(n => ({ ...n, read_at: new Date().toISOString() })));
    setUnreadCount(0);
  };

  return { notifications, unreadCount, markAllRead };
}

React: רכיב UI

function NotificationBell({ userId }: { userId: number }) {
  const { notifications, unreadCount, markAllRead } = useNotifications(userId);
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div className="notification-bell">
      <button
        onClick={() => setIsOpen(!isOpen)}
        aria-label={`${unreadCount} непрочитанных уведомлений`}
        aria-expanded={isOpen}
        aria-haspopup="true"
      >
        🔔
        {unreadCount > 0 && (
          <span className="badge" aria-hidden>{unreadCount > 99 ? '99+' : unreadCount}</span>
        )}
      </button>
      {isOpen && (
        <div className="notification-panel" role="dialog" aria-label="Уведомления">
          <header>
            <h2>Уведомления</h2>
            {unreadCount > 0 && (
              <button onClick={markAllRead}>Прочитать все</button>
            )}
          </header>
          <ul>
            {notifications.length === 0 && <li className="empty">Нет уведомлений</li>}
            {notifications.map(notification => (
              <li key={notification.id} className={notification.read_at ? 'read' : 'unread'}>
                {notification.url ? (
                  <a href={notification.url}>{notification.title}</a>
                ) : (
                  <span>{notification.title}</span>
                )}
                <time dateTime={notification.created_at}>{timeAgo(notification.created_at)}</time>
                {notification.body && <p>{notification.body}</p>}
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

תהליך עבודה ולוחות זמנים

איך מובטחת יציבות ההתראות בזמן אמת?

אנו מבטיחים זמינות של 99.9% למרכז ההתראות. אנו משתמשים בתורים (Laravel Queues) עם לוגיקת ניסיון חוזר על כשלים. בצד הלקוח – חיבור מחדש אוטומטי דרך Echo (Reconnect). עבור Pusher – גיבוי ל-SSE. עבור אירוח עצמי – ניטור דרך Laravel Horizon והתראות Telegram. יש לנו ניסיון של שנים עם יישומי web מורכבים ויותר מ-20 הטמעות של מרכזי התראות. תהליך שקוף מבטיח שתקבלו פתרון עובד בתוך לוחות הזמנים המוסכמים.

  1. ניתוח — אנו בוחנים דרישות, עומס ובוחרים את דרייבר ה-WebSocket.
  2. עיצוב — עיצוב סכמת DB, API ואירועים.
  3. הטמעה — כתיבת קוד, בדיקות על staging.
  4. בדיקות — בדיקות עומס (10,000+ חיבורים), אימות גיבוי.
  5. פריסה — הגדרת סביבת production, ניטור.

מה כלול

  • תיעוד: ארכיטקטורה, סכמת DB, רצף אירועים, הגדרת סביבה.
  • קוד: backend (Laravel), frontend (React), תצורת תורים ושידור.
  • גישה ל-repository ו-CI/CD.
  • הדרכת צוות: 1–2 פגישות מקוונות.
  • תמיכה: חודש אחד לאחר הפריסה.

ההשקעה במרכז התראות מחזירה את עצמה דרך הפחתת עומס והגדלת נאמנות המשתמשים. כדי לקבל פתרון מוכן, צרו קשר — אנו נבצע בדיקת פרויקט ונציע ארכיטקטורה אופטימלית. בקשו ייעוץ מהמהנדסים שלנו.

לוח זמנים להטמעה

אינטגרציה בסיסית: 2–3 ימים. WebSocket באירוח עצמי (Soketi/Laravel Reverb): +יום אחד. אם נדרשת לוגיקת קיבוץ או סינון מותאמת אישית, לוח הזמנים נדון בנפרד.