פיתוח מפיקים וצרכני Kafka מותאמים אישית

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

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

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

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

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
פיתוח מפיקים וצרכני Kafka מותאמים אישית
מורכב
~3-5 ימים

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

שאלות נפוצות

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

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

לקוחות Kafka בסביבת ייצור אינם רק "שליחת הודעה" ו"קבלת הודעה". לעיתים קרובות, תצורה שגויה פוגעת בעקביות נתונים עסקיים. לאחרונה נתקלנו בפרויקט שבו auto-commit והיעדר idempotency גרמו לאובדן של עד 10% מההזמנות במהלך הפעלות מחדש של הצרכן. לאחר יישום ניהול אופסט ידני ו-producer אידמפוטנטי, ההפסדים צומצמו לאפס. העלות של הפסדים כאלה עבור חנות מקוונת ממוצעת יכולה להגיע ל-$18k–26k בחודש. אנו מתמחים בפיתוח producers ו-consumers אמינים עבור יישומי אינטרנט בעומס גבוה ב-Java ו-Python. מאמר זה מכסה בעיות מפתח, תצורות אופייניות ומקרים מעשיים—החל מהגדרת idempotency ועד עיבוד מקבילי עם שמירת סדר.

בעיות שאנו פותרים

אובדן הודעות במהלך תקלות של Broker או Consumer

ללא תצורה נכונה של acks ו-idempotency, אתה מסתכן באובדן נתונים עסקיים קריטיים. לדוגמה, עם acks=1, הודעה יכולה ללכת לאיבוד אם ה-leader נכשל לפני שכפול. אנו משתמשים ב-acks=all עם min.insync.replicas=2 ו-enable.idempotence=true. זה מבטל אובדן גם אם broker אחד נכשל. חיסכון בתשתית על ידי הימנעות מפתרונות יקרים: עד 40%.

הפרת סדר וכפילויות

צרכנים ללא ניהול אופסט עשויים לעבד את אותה הודעה פעמיים, ועיבוד מקבילי הורס את הסדר. הפתרון: commit ידני לאחר עיבוד וניתוב לפי מפתח בתוך תור מחיצה. בפרויקט אחד, זה הפחית שגיאות אימות ב-30%.

Rebalance ארוך ועיבוד חוזר

כאשר צרכן נכשל ומתרחש rebalance, הקבוצה עשויה לקרוא מחדש כמויות גדולות של נתונים. אנו מגדירים session.timeout.ms ו-max.poll.interval.ms כך שה-rebalance לא ייקח יותר מ-5 שניות, ואנו משתמשים ב-DLQ כדי למנוע חסימה על הודעות "רעות".

כיצד אנו מבטיחים אספקה

לפי תיעוד Apache Kafka, ערבויות אספקה נקבעות על ידי השילוב של acks, idempotency ו-transactions. בואו נסתכל על דפוסים מרכזיים.

Idempotent Producer עם acks=all

עבור עסקאות פיננסיות, אירועי הזמנות וכל זרם קריטי, אנו מגדירים enable.idempotence=true ו-acks=all. זה מבטל כפילויות גם עם ניסיונות חוזרים. ההשפעה על התפוקה היא מינורית (5-10%), אך האמינות משתפרת משמעותית.

// Java — идемпотентный продюсер
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka-1:9092,kafka-2:9092,kafka-3:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, KafkaAvroSerializer.class);
props.put("schema.registry.url", "http://schema-registry:8081");
// Надёжность
props.put(ProducerConfig.ACKS_CONFIG, "all");
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
props.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, 5);
props.put(ProducerConfig.RETRIES_CONFIG, Integer.MAX_VALUE);
props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, 120_000);
// Производительность
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 65536); // 64KB батч
props.put(ProducerConfig.LINGER_MS_CONFIG, 5); // ждём до 5ms для наполнения батча
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 67_108_864); // 64MB буфер
props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "lz4");
KafkaProducer<String, OrderEvent> producer = new KafkaProducer<>(props);

שליחה עם טיפול בשגיאות:

public CompletableFuture<RecordMetadata> sendOrderEvent(OrderEvent event) {
    ProducerRecord<String, OrderEvent> record = new ProducerRecord<>(
            "order-events", event.getOrderId(), event);
    CompletableFuture<RecordMetadata> future = new CompletableFuture<>();
    producer.send(record, (metadata, exception) -> {
        if (exception != null) {
            if (exception instanceof RetriableException) {
                log.error("Retriable error, Kafka will retry: {}", exception.getMessage());
            } else {
                log.error("Fatal producer error for order {}: {}", event.getOrderId(), exception.getMessage());
                future.completeExceptionally(exception);
            }
        } else {
            log.debug("Sent to partition {} offset {}", metadata.partition(), metadata.offset());
            future.complete(metadata);
        }
    });
    return future;
}
דוגמה לתצורת צרכן עם commit ידני
Properties consumerProps = new Properties();
consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka-1:9092,kafka-2:9092,kafka-3:9092");
consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "order-processor");
consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, KafkaAvroDeserializer.class);
consumerProps.put("schema.registry.url", "http://schema-registry:8081");
consumerProps.put("specific.avro.reader", true);
// Отключаем auto-commit
consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
// Session timeout
consumerProps.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 30_000);
consumerProps.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 10_000);
consumerProps.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500);
consumerProps.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 300_000);
KafkaConsumer<String, OrderEvent> consumer = new KafkaConsumer<>(consumerProps);
consumer.subscribe(List.of("order-events"), new ConsumerRebalanceListener() {
    @Override
    public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
        commitCurrentOffsets();
    }

    @Override
    public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
        log.info("Assigned partitions: {}", partitions);
    }
});
Map<TopicPartition, OffsetAndMetadata> pendingOffsets = new HashMap<>();
try {
    while (!shutdown.get()) {
        ConsumerRecords<String, OrderEvent> records = consumer.poll(Duration.ofMillis(100));
        for (ConsumerRecord<String, OrderEvent> record : records) {
            try {
                processOrder(record.value());
                pendingOffsets.put(
                    new TopicPartition(record.topic(), record.partition()),
                    new OffsetAndMetadata(record.offset() + 1)
                );
            } catch (NonRetriableException e) {
                sendToDlq(record, e);
                pendingOffsets.put(
                    new TopicPartition(record.topic(), record.partition()),
                    new OffsetAndMetadata(record.offset() + 1)
                );
            }
        }
        if (!pendingOffsets.isEmpty()) {
            consumer.commitSync(pendingOffsets);
            pendingOffsets.clear();
        }
    }
} finally {
    consumer.close();
}

למה ניהול אופסט ידני עדיף על Auto-Commit

auto-commit יוצר אשליה של פשטות אך מסתיר סיכון: האופסט נשמר לפני העיבוד. אם הצרכן קורס בין commit לעיבוד, ההודעה אובדת ללא אפשרות שחזור. אנו תמיד משביתים את // Java — идемпотентный продюсер Properties props = new Properties(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka-1:9092,kafka-2:9092,kafka-3:9092"); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, KafkaAvroSerializer.class); props.put("schema.registry.url", "http://schema-registry:8081"); // Надёжность props.put(ProducerConfig.ACKS_CONFIG, "all"); props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true); props.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, 5); props.put(ProducerConfig.RETRIES_CONFIG, Integer.MAX_VALUE); props.put(ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG, 120_000); // Производительность props.put(ProducerConfig.BATCH_SIZE_CONFIG, 65536); // 64KB батч props.put(ProducerConfig.LINGER_MS_CONFIG, 5); // ждём до 5ms для наполнения батча props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 67_108_864); // 64MB буфер props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "lz4"); KafkaProducer<String, OrderEvent> producer = new KafkaProducer<>(props); ומבצעים commit ידני לאחר עיבוד מוצלח של הרשומה. זהו נוהג סטנדרטי עבור מערכות ייצור.

השוואת מצבי acks

מצב acks אובדן נתונים בכשל Leader ביצועים המלצה
Fire-and-forget 0 אפשרי מקסימלי רק עבור לוגים לא קריטיים
אישור Leader 1 אפשרי (לא משוכפל) גבוה שירותים פנימיים עם ניסיונות חוזרים
כל ISR all (עם min.insync.replicas) בלתי אפשרי נמוך ב-10-15% נתונים קריטיים, הזמנות, תשלומים

השוואת לקוחות Java ו-Python

קריטריון Java (kafka-clients) Python (confluent-kafka)
ביצועים גבוהים (JVM מקורי) בינוניים (דרך ספריית C)
תמיכה ב-transactions מלאה מוגבלת
ניהול אופסט ידני + אסינכרוני בעיקר commit סינכרוני
צריכת זיכרון גבוהה יותר (JVM heap) נמוכה יותר
המלצה קריטי, תפוקה גבוהה אבות טיפוס מהירים, עומס בינוני

כיצד לארגן עיבוד מקבילי מבלי לאבד סדר

שרשור poll אחד + מאגר עובדים הממופתח לפי מפתח מחיצה הוא הדפוס הקלאסי. אנו שומרים על סדר בתוך אירועי הזמנה בודדת תוך מקביליות בין הזמנות.

Map<Integer, BlockingQueue<ConsumerRecord<String, OrderEvent>>> partitionQueues = new HashMap<>();
ExecutorService workers = Executors.newFixedThreadPool(12);
for (ConsumerRecord<String, OrderEvent> record : records) {
    int partitionIndex = record.partition() % NUM_WORKERS;
    workerQueues.get(partitionIndex).offer(record);
}

תהליך פיתוח מקצה לקצה

  1. אנליטיקה — הגדרת נושאים, מפתחות הודעות, פורמט (Avro/JSON/Protobuf), קבוצות צרכנים.
  2. יישום — כתיבת producers עם idempotency, consumers עם commit ידני, DLQ.
  3. בדיקות — בדיקות יחידה עם EmbeddedKafka, בדיקות אינטגרציה עם Testcontainers, בדיקות עומס.
  4. פריסה — הגדרת ניטור (פיגור צרכן, שגיאות) והתראות.

מה כלול בעבודה

  • מאגר עם קוד (Java/Python) ותיעוד תצורה.
  • תמונות Docker מוגדרות עבור staging/production.
  • הוראות פריסה וחזרה לגרסה קודמת.
  • הדרכה לצוות הלקוח (1-2 שעות).
  • אחריות על קוד: 3 חודשים של תמיכה חינם.

לוח זמנים

מ-6 עד 10 ימי עבודה בהתאם למורכבות. בקש הערכת פרויקט — נגיב תוך יום אחד.

טעויות נפוצות (רשימת בדיקה)

  • [ ] public CompletableFuture<RecordMetadata> sendOrderEvent(OrderEvent event) { ProducerRecord<String, OrderEvent> record = new ProducerRecord<>( "order-events", event.getOrderId(), event ); CompletableFuture<RecordMetadata> future = new CompletableFuture<>(); producer.send(record, (metadata, exception) -> { if (exception != null) { if (exception instanceof RetriableException) { log.error("Retriable error, Kafka will retry: {}", exception.getMessage()); } else { log.error("Fatal producer error for order {}: {}", event.getOrderId(), exception.getMessage()); future.completeExceptionally(exception); } } else { log.debug("Sent to partition {} offset {}", metadata.partition(), metadata.offset()); future.complete(metadata); } }); return future; } לא מופעל — כפילויות בניסיונות חוזרים.
  • [ ] auto-commit בשימוש — אובדן הודעות בכשל צרכן.
  • [ ] Properties consumerProps = new Properties(); consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka-1:9092,kafka-2:9092,kafka-3:9092"); consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "order-processor"); consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, KafkaAvroDeserializer.class); consumerProps.put("schema.registry.url", "http://schema-registry:8081"); consumerProps.put("specific.avro.reader", true); // Отключаем auto-commit consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); // Session timeout consumerProps.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 30_000); consumerProps.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 10_000); consumerProps.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500); consumerProps.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 300_000); KafkaConsumer<String, OrderEvent> consumer = new KafkaConsumer<>(consumerProps); consumer.subscribe(List.of("order-events"), new ConsumerRebalanceListener() { @Override public void onPartitionsRevoked(Collection<TopicPartition> partitions) { commitCurrentOffsets(); } @Override public void onPartitionsAssigned(Collection<TopicPartition> partitions) { log.info("Assigned partitions: {}", partitions); } }); Map<TopicPartition, OffsetAndMetadata> pendingOffsets = new HashMap<>(); try { while (!shutdown.get()) { ConsumerRecords<String, OrderEvent> records = consumer.poll(Duration.ofMillis(100)); for (ConsumerRecord<String, OrderEvent> record : records) { try { processOrder(record.value()); pendingOffsets.put( new TopicPartition(record.topic(), record.partition()), new OffsetAndMetadata(record.offset() + 1) ); } catch (NonRetriableException e) { sendToDlq(record, e); pendingOffsets.put( new TopicPartition(record.topic(), record.partition()), new OffsetAndMetadata(record.offset() + 1) ); } } if (!pendingOffsets.isEmpty()) { consumer.commitSync(pendingOffsets); pendingOffsets.clear(); } } } finally { consumer.close(); } גדול מדי — rebalance ארוך.
  • [ ] אין טיפול ב-enable.auto.commit — הודעה אובדת ללא DLQ.
  • [ ] גרסאות serializer לא תואמות בין producer ל-consumer — שגיאות deserialization.

אם אתה מזהה את הפרויקט שלך בבעיות אלה, צור קשר. פיתחנו producers ו-consumers עבור 15+ פרויקטים המטפלים בעד 100,000 הודעות בשנייה.

אחריות: אנו קובעים מועדים בחוזה ומספקים 3 חודשים של תמיכה לאחר מסירה.