Profile
Back to NewsBack
GitHub Trending 9 min
Reader Mode
Trendyol/kafka-konsumer: Easy implementation of kafka consumer with built-in exception manager (kafka-cronsumer)

Trendyol/kafka-konsumer: Easy implementation of kafka consumer with built-in exception manager (kafka-cronsumer)

14 hours ago

Kafka Konsumer

🔨Build And Test</a> 🔨IntegrationTest</a> Go Report Card</a>

Description

Kafka Konsumer provides an easy implementation of Kafka consumer with a built-in retry/exception manager (kafka-cronsumer).

Migration Guide

V2 Release Notes

  • Added ability for manipulating kafka message headers.
  • Added transactional retry feature. Set false if you want to use exception/retry strategy to only failed messages.
  • Enable manuel commit at both single and batch consuming modes.
  • Enabling consumer resume/pause functionality. Please refer to its example and
how it works documentation. - Backoff strategy support (linear, exponential options) - Added message key for retried messages - Added x-error-message to see what was the error of the message during processing
  • Reduce memory allocation.
  • Increase TP on changing internal concurrency structure.

How to migrate from v1 to v2?

You can get latest version via go get github.com/Trendyol/kafka-konsumer/v2@latest

  • You need to change import path from github.com/Trendyol/kafka-konsumer to github.com/Trendyol/kafka-konsumer/v2
  • You need to change your consume function with pointer signature.
  • We moved messageGroupDuration from batchConfiguration.messageGroupDuration to root level. Because this field is used
single (non-batch) consumer too.

Installation

go get github.com/Trendyol/kafka-konsumer/v2@latest

Examples

You can find a number of ready-to-run examples at this directory.

After running docker-compose up command, you can run any application you want.

Simple Consumer

func main() { consumerCfg := &kafka.ConsumerConfig{ Reader: kafka.ReaderConfig{ Brokers: []string{"localhost:29092"}, Topic: "standart-topic", GroupID: "standart-cg", }, ConsumeFn: consumeFn, RetryEnabled: false, } consumer, _ := kafka.NewConsumer(consumerCfg) defer consumer.Stop() consumer.Consume() } func consumeFn(message kafka.Message) error { fmt.Printf("Message From %s with value %s", message.Topic, string(message.Value)) return nil }

Simple Consumer With Retry/Exception Option

func main() { consumerCfg := &kafka.ConsumerConfig{ Reader: kafka.ReaderConfig{ Brokers: []string{"localhost:29092"}, Topic: "standart-topic", GroupID: "standart-cg", }, RetryEnabled: true, RetryConfiguration: kafka.RetryConfiguration{ Topic: "retry-topic", StartTimeCron: "/1 *", WorkDuration: 50 * time.Second, MaxRetry: 3, // Applied to retry-topic and dead-letter-topic producers. ProducerCompression: kafka.Gzip, }, ConsumeFn: consumeFn, } consumer, _ := kafka.NewConsumer(consumerCfg) defer consumer.Stop() consumer.Consume() } func consumeFn(message kafka.Message) error { fmt.Printf("Message From %s with value %s", message.Topic, string(message.Value)) return nil }

With Batch Option

func main() { consumerCfg := &kafka.ConsumerConfig{ Reader: kafka.ReaderConfig{ Brokers: []string{"localhost:29092"}, Topic: "standart-topic", GroupID: "standart-cg", }, LogLevel: kafka.LogLevelDebug, RetryEnabled: true, RetryConfiguration: kafka.RetryConfiguration{ Brokers: []string{"localhost:29092"}, Topic: "retry-topic", StartTimeCron: "/1 *", WorkDuration: 50 * time.Second, MaxRetry: 3, }, MessageGroupDuration: time.Second, BatchConfiguration: kafka.BatchConfiguration{ MessageGroupLimit: 1000, BatchConsumeFn: batchConsumeFn, }, } consumer, _ := kafka.NewConsumer(consumerCfg) defer consumer.Stop() consumer.Consume() } func batchConsumeFn(messages []kafka.Message) error { fmt.Printf("%d\n comes first %s", len(messages), messages[0].Value) return nil }

With Disabling Transactional Retry

func main() { consumerCfg := &kafka.ConsumerConfig{ Reader: kafka.ReaderConfig{ Brokers: []string{"localhost:29092"}, Topic: "standart-topic", GroupID: "standart-cg", }, LogLevel: kafka.LogLevelDebug, RetryEnabled: true, TransactionalRetry: kafka.NewBoolPtr(false), RetryConfiguration: kafka.RetryConfiguration{ Brokers: []string{"localhost:29092"}, Topic: "retry-topic", StartTimeCron: "/1 *", WorkDuration: 50 * time.Second, MaxRetry: 3, }, MessageGroupDuration: time.Second, BatchConfiguration: kafka.BatchConfiguration{ MessageGroupLimit: 1000, BatchConsumeFn: batchConsumeFn, }, } consumer, _ := kafka.NewConsumer(consumerCfg) defer consumer.Stop() consumer.Consume() } func batchConsumeFn(messages []kafka.Message) error { // you can add custom error handling here & flag messages for i := range messages { if i%2 == 0 { messages[i].IsFailed = true } } // you must return err here to retry failed messages return errors.New("err") }

With Producer Interceptor

Please refer to Producer Interceptor Example

With Distributed Tracing Support

Please refer to Tracing Example

With Pause & Resume Consumer

Please refer to Pause Resume Example

With Grafana & Prometheus

In this example, we are demonstrating how to create Grafana dashboard and how to define alerts in Prometheus. You can see the example by going to the with-grafana folder in the examples folder and running the infrastructure with docker compose up and then the application.

!grafana

With SASL-PLAINTEXT Authentication

Under the examples - with-sasl-plaintext folder, you can find an example of a consumer integration with SASL/PLAIN mechanism. To try the example, you can run the command docker compose up under the specified folder and then start the application.

With Send Direct To Dead Letter

This feature lets you send a message directly to a dead-letter topic by setting message.SendDirectToDeadLetter = true inside your ConsumeFn (or selectively for items in BatchConsumeFn). When this flag is set and your ConsumeFn returns an error, the message is produced to the configured dead-letter topic with an x-error-message header.

  • Dead letter topic resolution order: if consumer.DeadLetterTopic is set, it is used; otherwise retryConfiguration.deadLetterTopic is used.
  • You can set message.ErrDescription to override the error header value written as x-error-message.
  • The flag is also honored while consuming from the retry topic: the message is produced to the dead-letter topic once and is not retried again. If no dead-letter topic is configured, the message follows the normal retry flow.
  • Set DeadLetterProducerBatchBytes to split direct dead-letter ProduceBatch calls by approximate uncompressed message bytes. The default 0 keeps the current single-batch behavior. Configure this below the Kafka/topic max message size to leave room for Kafka protocol and request overhead. A single message larger than the limit is still sent alone and can still fail with Kafka Message Size Too Large; adjust DLQ topic/broker limits or payload handling for that case.
Please refer to Send Direct To Dead Letter Example to run both single and batch scenarios.

Configurations

| config | description | default | |--------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------| | reader | Describes all segmentio kafka reader configurations | | | consumeFn | Kafka consumer function, if retry enabled it, is also used to consume retriable messages | | | skipMessageByHeaderFn | Function to filter messages based on headers, return true if you want to skip the message | nil | | logLevel | Describes log level; valid options are debug, info, warn, and error | info | | concurrency | Number of goroutines used at listeners | 1 | | retryEnabled | Retry/Exception consumer is working or not | false | | transactionalRetry | Set false if you want to use exception/retry strategy to only failed messages | true | | deadLetterTopic | Dead-letter topic name to produce messages when message.SendDirectToDeadLetter is true. If empty, retryConfiguration.deadLetterTopic will be used. | "" | | deadLetterProducerBatchBytes | Optional byte limit for direct dead-letter producer batches, based on approximate uncompressed message size. Zero disables chunking and preserves current behavior. Set below Kafka/topic max message size to leave protocol overhead room. Works with producer compression, but does not replace proper Kafka topic/broker message-size limits. | 0 | | commitInterval | indicates the interval at which offsets are committed to the broker. | 1s | | rack | see doc | | | clientId | see doc | | | messageGroupDuration | Maximum time to wait for a batch | 1s | | metricPrefix | MetricPrefix is used for prometheus fq name prefix. If not provided, default metric prefix value is kafka_konsumer. Currently, there are two exposed prometheus metrics. processed_messages_total and unprocessed_messages_total So, if default metric prefix used, metrics names are kafka_konsumer_processed_messages_total_current and kafka_konsumer_unprocessed_messages_total_current. | kafka_konsumer | | dial.Timeout | see doc | no timeout | | dial.KeepAlive | see doc | not enabled | | transport.DialTimeout | see doc | 5s | | transport.IdleTimeout | see doc | 30s | | transport.MetadataTTL | see doc | 6s | | transport.MetadataTopics | see doc | all topics in cluster | | distributedTracingEnabled | indicates open telemetry support on/off for consume and produce operations. | false | | distributedTracingConfiguration.TracerProvider | see doc | otel.GetTracerProvider() | | distributedTracingConfiguration.Propagator | see doc | otel.GetTextMapPropagator() | | retryConfiguration.clientId | see doc | | | retryConfiguration.startTimeCron | Cron expression when retry consumer (kafka-cronsumer) starts to work at | | | retryConfiguration.metricPrefix | MetricPrefix is used for prometheus fq name prefix. If not provided, default metric prefix value is kafka_cronsumer. Currently, there are two exposed prometheus metrics. retried_messages_total_current and discarded_messages_total_current. So, if default metric prefix used, metrics names are kafka_cronsumer_retried_messages_total_current and kafka_cronsumer_discarded_messages_total_current | kafka_cronsumer | | retryConfiguration.workDuration | Work duration exception consumer actively consuming messages | | | retryConfiguration.queueCapacity | see doc | 100 | | retryConfiguration.producerBatchSize | see doc | 100 | | retryConfiguration.producerBatchBytes | Optional byte limit for retry producer batches, based on approximate uncompressed message size. Zero disables retry producer chunking and preserves current behavior. Requires kafka-cronsumer v1.6.11+. | 0 | | retryConfiguration.producerBatchTimeout | see doc | 100 | | retryConfiguration.producerRequiredAcks | see doc. Applied to the retry (kafka-cronsumer) and dead-letter producers (RequireNone / RequireOne / RequireAll). When unset, defaults to RequireNone. | RequireNone (0) | | retryConfiguration.producerCompression | see doc. Applied to both retry-topic and dead-letter-topic producers. Useful when produced retry or dead-letter messages are large and brokers enforce a maximum message size. Supported values include kafka.Gzip, kafka.Snappy, kafka.Lz4, and kafka.Zstd. | uncompressed | | retryConfiguration.topic | Retry/Exception topic names | | | retryConfiguration.startOffset | Same as ReaderConfig.StartOffset: kafka.FirstOffset, kafka.LastOffset, or 0. Zero defaults to FirstOffset. Does not follow the main reader start offset. | 0 (FirstOffset) | | retryConfiguration.deadLetterTopic | Dead-letter topic name used by the retry/exception pipeline; also used as a fallback for direct dead-lettering when top-level deadLetterTopic is empty. | | | retryConfiguration.brokers | Retry topic brokers urls | | | retryConfiguration.maxRetry | Maximum retry value for attempting to retry a message | 3 | | retryConfiguration.concurrency | Number of goroutines used at listeners | 1 | | retryConfiguration.tls.rootCAPath | see doc | "" | | retryConfiguration.tls.intermediateCAPath | Same with rootCA, if you want to specify two rootca you can use it with rootCAPath | "" | | retryConfiguration.sasl.authType | SCRAM or PLAIN | | | retryConfiguration.sasl.username | SCRAM OR PLAIN username | | | retryConfiguration.sasl.password | SCRAM OR PLAIN password | | | retryConfiguration.backOffStrategyName | Backoff strategy used by the retry consumer. Valid values (case-sensitive): fixed, linear, exponential. If empty, defaults to fixed. An invalid value causes a startup error. | fixed | | retryConfiguration.skipMessageByHeaderFn | Function to filter messages based on headers, return true if you want to skip the message | nil | | ~~retryConfiguration.verifyTopicOnStartup~~ | it checks existence of the given retry topic on the kafka cluster. This field now functions as true and will be removed in future Cronsumer versions. | true | | batchConfiguration.messageGroupLimit | Maximum number of messages in a batch | 100 | | batchConfiguration.messageGroupByteSizeLimit | Maximum number of bytes in a batch; a single message larger than this limit is processed in its own batch | | | batchConfiguration.batchConsumeFn | Kafka batch consumer function, if retry enabled it, is also used to consume retriable messages | | | batchConfiguration.preBatchFn | This function enable for transforming messages before batch consuming starts. If retry enabled, it is also called for each retried message (one message at a time, concurrently with the main flow), so it must be idempotent and safe for concurrent use | | | batchConfiguration.balancer | see doc | leastBytes | | tls.rootCAPath | see doc | "" | | tls.intermediateCAPath | Same with rootCA, if you want to specify two rootca you can use it with rootCAPath | "" | | sasl.authType | SCRAM or PLAIN | | | sasl.username | SCRAM OR PLAIN username | | | sasl.password | SCRAM OR PLAIN password | | | logger | If you want to custom logger | info | | apiEnabled | Enabled metrics | false | | apiConfiguration.port | Set API port | 8090 | | apiConfiguration.healtCheckPath | Set Health check path | healthcheck | | metricConfiguration.path | Set metric endpoint path | /metrics |

Monitoring

Kafka Konsumer offers an API that handles exposing several metrics.

Exposed Metrics

| Metric Name | Description | Value Type | |------------------------------------------------------------------|------------------------------------------------|------------| | kafka_konsumer_processed_messages_total_current | Total number of processed messages. | Counter | | kafka_konsumer_unprocessed_messages_total_current | Total number of unprocessed messages. | Counter | | kafka_konsumer_error_count_during_fetching_message_total_current | Total number of error during fetching message. | Counter |

Chat with me