Backpressure für den `ExampleProducer` implementiert producer/spring-producer--backpressure producer/spring-producer--backpressure--2025-03-18--19-42 producer/spring-producer--backpressure--2025-03-signal producer/spring-producer--backpressure--2025-04-signal
authorKai Moritz <kai@juplo.de>
Fri, 1 Nov 2024 08:55:43 +0000 (09:55 +0100)
committerKai Moritz <kai@juplo.de>
Mon, 17 Mar 2025 15:58:03 +0000 (16:58 +0100)
README.sh
build.gradle
docker/docker-compose.yml
pom.xml
src/main/java/de/juplo/kafka/ApplicationConfiguration.java
src/main/java/de/juplo/kafka/ApplicationProperties.java
src/main/java/de/juplo/kafka/ExampleProducer.java
src/main/resources/application.yml

index 918e83a..19419b5 100755 (executable)
--- a/README.sh
+++ b/README.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-IMAGE=juplo/spring-producer:1.0-SNAPSHOT
+IMAGE=juplo/spring-producer:1.0-backpressure-SNAPSHOT
 
 if [ "$1" = "cleanup" ]
 then
index 1429c4d..3d22e5b 100644 (file)
@@ -8,7 +8,7 @@ plugins {
 }
 
 group = 'de.juplo.kafka'
-version = '1.0-SNAPSHOT'
+version = '1.0-backpressure-SNAPSHOT'
 
 java {
        toolchain {
index a5fd281..c3a90d6 100644 (file)
@@ -136,13 +136,14 @@ services:
       - kafka-3
 
   producer:
-    image: juplo/spring-producer:1.0-SNAPSHOT
+    image: juplo/spring-producer:1.0-backpressure-SNAPSHOT
     environment:
       juplo.bootstrap-server: kafka:9092
       juplo.client-id: producer
       juplo.producer.topic: test
       juplo.producer.delivery-timeout: 2147483647ms
-      juplo.producer.buffer-memory: 32768
+      juplo.producer.max-block: 2147483647ms
+      juplo.producer.max-queue-length: 10
 
   consumer:
     image: juplo/simple-consumer:1.0-SNAPSHOT
diff --git a/pom.xml b/pom.xml
index f64266b..0360ad3 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -15,7 +15,7 @@
   <artifactId>spring-producer</artifactId>
   <name>Spring Producer</name>
   <description>A Simple Producer, based on Spring Boot, that sends messages via Kafka</description>
-  <version>1.0-SNAPSHOT</version>
+  <version>1.0-backpressure-SNAPSHOT</version>
 
   <properties>
     <java.version>21</java.version>
index 0090cee..5fa7140 100644 (file)
@@ -29,6 +29,7 @@ public class ApplicationConfiguration
         properties.getProducerProperties().getThrottle() == null
           ? Duration.ofMillis(500)
           : properties.getProducerProperties().getThrottle(),
+        properties.getProducerProperties().getMaxQueueLength(),
         kafkaProducer,
         () -> applicationContext.close());
   }
index 4323262..f2fd951 100644 (file)
@@ -55,6 +55,8 @@ public class ApplicationProperties
     @NotNull
     private Duration linger;
     @NotNull
+    private Integer maxQueueLength;
+    @NotNull
     @NotEmpty
     private String compressionType;
     private Duration throttle;
index 25e885d..e7a7686 100644 (file)
@@ -5,6 +5,7 @@ import org.apache.kafka.clients.producer.Producer;
 import org.apache.kafka.clients.producer.ProducerRecord;
 
 import java.time.Duration;
+import java.util.concurrent.atomic.AtomicInteger;
 
 
 @Slf4j
@@ -13,11 +14,13 @@ public class ExampleProducer implements Runnable
   private final String id;
   private final String topic;
   private final Duration throttle;
+  private final int maxQueueLength;
   private final Producer<String, String> producer;
   private final Thread workerThread;
   private final Runnable closeCallback;
 
   private volatile boolean running = true;
+  private final AtomicInteger queued = new AtomicInteger(0);
   private long produced = 0;
 
 
@@ -25,12 +28,14 @@ public class ExampleProducer implements Runnable
     String id,
     String topic,
     Duration throttle,
+    int maxQueueLength,
     Producer<String, String> producer,
     Runnable closeCallback)
   {
     this.id = id;
     this.topic = topic;
     this.throttle = throttle;
+    this.maxQueueLength = maxQueueLength;
     this.producer = producer;
 
     workerThread = new Thread(this, "ExampleProducer Worker-Thread");
@@ -80,6 +85,18 @@ public class ExampleProducer implements Runnable
 
   void send(String key, String value)
   {
+    while (queued.get() >= maxQueueLength)
+    {
+      try
+      {
+        Thread.sleep(1000);
+      }
+      catch (InterruptedException e)
+      {
+        log.warn("{} - Interrupted while waiting for queue to be progressed, queued={}!", queued, e);
+      }
+    }
+
     final long time = System.currentTimeMillis();
 
     final ProducerRecord<String, String> record = new ProducerRecord<>(
@@ -88,33 +105,38 @@ public class ExampleProducer implements Runnable
       value   // Value
     );
 
+    int queuedAfterSend = queued.incrementAndGet();
+
     producer.send(record, (metadata, e) ->
     {
       long now = System.currentTimeMillis();
+      int queuedAfterReceive = queued.decrementAndGet();
       if (e == null)
       {
         // HANDLE SUCCESS
         produced++;
         log.debug(
-          "{} - Sent message {}={}, partition={}, offset={}, timestamp={}, latency={}ms",
+          "{} - Sent message {}={}, partition={}, offset={}, timestamp={}, latency={}ms, queued={}",
           id,
           key,
           value,
           metadata.partition(),
           metadata.offset(),
           metadata.timestamp(),
-          now - time
+          now - time,
+          queuedAfterReceive
         );
       }
       else
       {
         // HANDLE ERROR
         log.error(
-          "{} - ERROR for message {}={}, latency={}ms: {}",
+          "{} - ERROR for message {}={}, latency={}ms, queued={}: {}",
           id,
           key,
           value,
           now - time,
+          queuedAfterReceive,
           e.toString()
         );
       }
@@ -122,11 +144,12 @@ public class ExampleProducer implements Runnable
 
     long now = System.currentTimeMillis();
     log.trace(
-      "{} - Queued message {}={}, latency={}ms",
+      "{} - Queued message {}={}, latency={}ms, queued={}",
       id,
       key,
       value,
-      now - time
+      now - time,
+      queuedAfterSend
     );
   }
 
index 98ea128..3d632cd 100644 (file)
@@ -9,6 +9,7 @@ juplo:
     buffer-memory: 33554432
     batch-size: 16384
     linger: 0
+    max-queue-length: 100
     compression-type: gzip
     throttle: 500
 management:
@@ -36,6 +37,7 @@ info:
       buffer-memory: ${juplo.producer.buffer-memory}
       batch-size: ${juplo.producer.batch-size}
       linger: ${juplo.producer.linger}
+      max-queue-length: ${juplo.producer.max-queue-length}
       compression-type: ${juplo.producer.compression-type}
       throttle: ${juplo.producer.throttle}
 logging: