מה זה?

שירות “הזמנות” צריך להודיע לשירות “מלאי” ולשירות “התראות” שהזמנה חדשה נוצרה. קריאת HTTP ישירה (מיחידת ASP.NET Core) הייתה עובדת — אבל אז שירות ההזמנות תלוי בזמינות שני השירותים האחרים באותו רגע, וצריך לחכות לתשובה משניהם. Kafka פותר את זה: שירות ההזמנות “מפרסם” הודעה פעם אחת, וכל שירות שמעוניין “מאזין” לה בזמנו שלו — תקשורת אסינכרונית, בלי תלות ישירה בין הצדדים.

מילות מפתח שחשוב לזכור

• Topic — “ערוץ” בעל שם ב-Kafka שהודעות נשלחות אליו (למשל order-created)

• Producer — שירות ששולח (מפרסם) הודעות לTopic

• Consumer — שירות שקורא (צורך) הודעות מ-Topic

• Partition — Topic מחולק פנימית לכמה חלקים, מאפשר עיבוד מקבילי ומהיר יותר

• Offset — “סמן מיקום” שכל Consumer שומר — עד איזו הודעה בTopic הוא כבר קרא

• Consumer Group — קבוצת Consumers ששולחים ביחד הודעות מTopic, כל הודעה מטופלת פעם אחת בקבוצה (לא כל Consumer מקבל הכל)

  flowchart RL
    P["Order Service<br/>(Producer)"] -->|"publish message"| T["Topic:<br/>order-created"]
    T --> C1["Inventory Service<br/>(Consumer)"]
    T --> C2["Notification Service<br/>(Consumer)"]
// Producer — publish a message, doesn't wait for a response from anyone
using var producer = new ProducerBuilder<Null, string>(config).Build();

var order = new { OrderId = 123, CustomerId = 5, Total = 250 };
string json = JsonSerializer.Serialize(order);   // JSON, from the previous unit

await producer.ProduceAsync("order-created", new Message<Null, string> { Value = json });
// That's it — the Order Service moves on, without waiting for anyone to process the message
// Consumer — listens and processes messages on its own time
using var consumer = new ConsumerBuilder<Null, string>(config).Build();
consumer.Subscribe("order-created");

while (true)
{
    var result = consumer.Consume();   // waits for the next message
    var order = JsonSerializer.Deserialize<Order>(result.Message.Value);
    Console.WriteLine($"Updating inventory for order {order.OrderId}");
}

הסבר עיקרי

Producer/Consumer לא “מדברים” ישירות זה עם זה — Producer שולח ל-Topic ולא יודע (ולא צריך לדעת) מי, אם בכלל, מאזין אליו. Consumer קורא מ-Topic בזמנו שלו, גם אם ה-Producer כבר “עבר הלאה” מזמן. זה שונה מהותית מ-HTTP (מיחידת ASP.NET Core) שבו הלקוח ממתין לתשובה — כאן, “שליחה” ו"עיבוד" מנותקים לגמרי בזמן.

Partition מאפשר מקביליות — Topic עם 3 Partitions יכול “לשרת” 3 Consumers שונים בו-זמנית (כל אחד קורא Partition אחר), מעבד הודעות מהר יותר מ-Consumer בודד. Offset הוא איך כל Consumer “זוכר” איפה הוא עצר — אם הוא קורס ועולה מחדש, הוא ממשיך מה-Offset האחרון שנשמר, לא מההתחלה.

Consumer Group מונע עיבוד כפול — אם יש 3 מופעים (Instances) של Inventory Service רצים במקביל (לביצועים/זמינות), רוצים שכל הודעה תטופל פעם אחת בלבד, לא 3 פעמים. Consumer Group עושה בדיוק את זה: כל ההודעות ב-Topic מתחלקות בין חברי הקבוצה, כל הודעה מגיעה לחבר אחד בלבד בקבוצה.

יתרונות

Kafka מנתק שירותים זה מזה בזמן (Decoupling) — Producer לא ממתין ל-Consumer; Partitions מאפשרות עיבוד מקבילי ומהיר; Consumer Groups מבטיחות שכל הודעה מטופלת פעם אחת גם עם כמה מופעי שירות רצים; Offset מאפשר להמשיך מאיפה שהפסיקו אחרי קריסה.

חסרונות

תקשורת אסינכרונית קשה יותר לדבג מ-HTTP סינכרוני (אין “תשובה מיידית” לבדוק); Kafka דורש תשתית וניהול נוספים (Broker, ZooKeeper/KRaft); עיצוב נכון של Topics/Partitions דורש תכנון מראש.

נקודות חשובות

• Producer שולח ל-Topic; Consumer קורא ממנו — לא מדברים ישירות זה עם זה

• Partition מאפשרת מקביליות; Offset הוא מיקום הקריאה של Consumer בתוך Partition

• Consumer Group מבטיח שכל הודעה מטופלת פעם אחת בקבוצה, גם עם כמה מופעי שירות

• Kafka מתאים לתקשורת אסינכרונית בין שירותים — לא תחליף ל-REST API סינכרוני בכל מקום

טעויות נפוצות

• לצפות שProducer “יידע” אם Consumer עיבד את ההודעה בהצלחה — הם מנותקים לגמרי בזמן

• לשים כמה מופעי שירות לא באותו Consumer Group, ולגלות שכל הודעה מטופלת כמה פעמים בטעות

• לבחור Kafka לתקשורת שדורשת תשובה מיידית (כמו “האם הכרטיס אושר?”) — שם REST/HTTP סינכרוני מתאים יותר

סיכום

Kafka מנתק שירותים בזמן — Producer מפרסם הודעה ל-Topic בלי להמתין, Consumer קורא ומעבד אותה בזמנו שלו. Partitions מאפשרות עיבוד מקבילי; Offset שומר מיקום קריאה שמאפשר המשכיות אחרי קריסה; Consumer Group מבטיח שכל הודעה מטופלת פעם אחת, גם עם כמה מופעי שירות. זהו הבסיס לתקשורת אסינכרונית בארכיטקטורת מיקרוסרביסים, שיחזור ביחידות הבאות של הקורס.

דוקומנטציה רשמית

Apache Kafka — Documentation

Confluent — .NET Client


תרגילים

תרגיל 1 — Producer/Consumer בסיסי

המשימה: צרו Topic בשם test-topic, כתבו Producer ששולח 5 הודעות טקסט, וConsumer שקורא ומדפיס אותן.

בדיקה: כל 5 ההודעות מודפסות על ידי ה-Consumer, בסדר שנשלחו.

תרגיל 2 — Consumer Group

המשימה: הריצו שני מופעי Consumer באותה Consumer Group על אותו Topic, ושלחו 10 הודעות. עקבו אילו הודעות כל מופע קיבל.

בדיקה: ביחד, שני המופעים מקבלים את כל 10 ההודעות בדיוק פעם אחת כל אחת — לא כפילות, לא החמצה.


פרויקט מסכם

המשימה: הוסיפו פרסום אירוע Kafka ל-TasksController (מהפרויקטים המסכמים) בכל יצירת משימה.

דרישות:

  1. Topic task-created — כל POST מוצלח ל-TasksController מפרסם הודעת JSON עם פרטי המשימה
  2. Consumer נפרד (קונסולה עצמאית, לא חלק מה-API) שמאזין ל-Topic ומדפיס “משימה חדשה נוצרה: [כותרת]”
  3. הרצת ה-API והConsumer בו-זמנית — יצירת משימה דרך ה-API מופיעה בקונסולת ה-Consumer

בדיקה: יצירת משימה דרך endpoint ה-API גורמת מיד להדפסה בConsumer הנפרד, בלי שה-API ממתין לתגובה ממנו.

מה בפרק הבא

בפרק הבא — Kafka: Producer/Consumer מרובה-שפות: שילוב C# ו-Python על אותו Topic — הוכחה שKafka לא תלוי בשפת התכנות, בדיוק כמו JSON.