autumn-plugin-kafka
Kafka producer and consumers for Autumn apps.
- Send records from a handler with the
KafkaProducerargument. - Process messages with async consumer handlers.
- Retry failed messages. Then send them to a dead-letter topic, or skip them.
- See Kafka status in
/actuator/healthand metrics in/actuator/prometheus. - Test without Docker with
MemoryBroker.
The example examples/orders.rs uses all parts.
CI runs its self-check against MemoryBroker and a real broker.
Install
[]
= "0.3"
The crate compiles librdkafka. You must have a C compiler, make, and zlib.
For TLS or SASL, enable the ssl feature.
| Feature | Function |
|---|---|
ssl |
TLS and SASL (PLAIN, SCRAM) with the system OpenSSL. |
ssl-vendored |
The same as ssl, with a built-in OpenSSL. |
gssapi |
SASL GSSAPI (Kerberos). |
zstd |
zstd compression. |
dynamic-linking |
Use the system librdkafka. |
cmake-build |
Build librdkafka with CMake. |
Use
use ;
use *;
async
// The error type can be `HandlerError`, `AutumnError`, or any type with `Display`.
async
async
Configure
The plugin reads [kafka] at startup. It uses the same layers as Autumn:
autumn.toml, [profile.<name>.kafka], autumn-<profile>.toml, then environment variables.
It also reads .env files and profile aliases (production, development) as Autumn does.
[]
= "localhost:9092" # default
= "autumn" # default
= "billing" # default group for consumers
= 10000 # default
[] # librdkafka properties for all clients
= "SASL_SSL"
= "PLAIN"
= "${KAFKA_USER}"
= "${KAFKA_PASSWORD}"
[]
= 5000 # default
= { = "5" }
[]
= "earliest" # default
[]
= false # default: a broker outage does not fail /ready
= 1500 # default
${NAME}gets the value of the environment variableNAME. IfNAMEis not set, the app does not start. Use$${for a literal${.- These variables override the files:
AUTUMN_KAFKA__BROKERS,__CLIENT_ID,__GROUP_ID,__SHUTDOWN_TIMEOUT_MS,__PRODUCER__SEND_TIMEOUT_MS,__HEALTH__READINESS,__HEALTH__TIMEOUT_MS. - Unknown keys are errors. Property values must be strings.
- Config errors name the key. They do not show the value.
Debughides secret properties. - To give the config in code, use
KafkaPlugin::new().config(KafkaConfig::new("broker:9092")).
Delivery rules
- A consumer processes one message at a time, in order.
- The plugin commits an offset only after the handler completes. Delivery is at-least-once.
- A failed handler gets
max_retriesretries (default 3). The delay starts atretry_backoff(default 100 ms), doubles for each retry, and stops at 30 s. - The sum of all retry delays must be less than
max.poll.interval.ms(default 300 s). If not, the app does not start. Else the broker removes the consumer from the group. - After the last retry, the plugin sends the message to
dead_letter_topic, or it logs an error and skips the message. - A dead-letter record keeps the key, the payload, and the headers. It also gets the
autumn.dlq.*headers. - The plugin removes incoming
autumn.dlq.*headers, so that a producer cannot forge them. - If the dead-letter send fails, the plugin does not commit. It tries again until shutdown.
- If the broker rejects the dead-letter record (for example, it is too large), the consumer stops with no commit. Health is then
DOWN. Fix the cause and restart. - A handler panic is a failure.
- A new group starts at the first message (
auto.offset.reset = earliest), if you do not set it. - The producer uses
enable.idempotence = true, if you do not set it. KafkaProducer::sendtimes out 500 ms afterproducer.send_timeout_ms. The broker can still get the record after a timeout.
Health and metrics
The kafka health indicator asks a broker for the metadata of all topics.
It is also DOWN if a consumer stopped before shutdown.
It is health-only by default. Set kafka.health.readiness = true so that a failed check also fails /ready.
| Metric | Labels |
|---|---|
kafka_messages_produced_total |
|
kafka_produce_errors_total |
|
kafka_messages_consumed_total |
consumer |
kafka_handler_errors_total |
consumer |
kafka_messages_dead_lettered_total |
consumer |
kafka_messages_skipped_total |
consumer |
kafka_receive_errors_total |
consumer |
kafka_consumer_running (gauge) |
consumer |
Test
Use MemoryBroker in your app tests. It does not need Docker.
TestApp does not run shutdown hooks. Call runtime.shutdown() at the end of a test.
let broker = new;
let client = new
.plugin
.routes
.build;
client.post.send.await.assert_status;
assert_eq!;
To test a handler alone, call it with Message::new(..) and AppState::detached().
To test code that takes a producer, use KafkaProducer::from_backend.
MemoryBroker::set_available(false) and MemoryBroker::reject_topic(..) simulate failures.
To run the tests of this crate against a real broker:
KAFKA_BROKERS=localhost:9092
Without KAFKA_BROKERS, the broker tests do nothing. In CI, they fail.
License
Apache-2.0