spakky-kafka¶
Kafka 통합 — 이벤트 전송/수신
메인¶
initialize(app)
¶
Initialize the spakky-kafka plugin.
Registers Kafka consumers, transports, and post-processor.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/main.py
이벤트 Transport¶
KafkaEventTransport(config)
¶
Bases: IEventTransport, IService
Synchronous Kafka event transport using confluent_kafka Producer.
One producer is created with the transport and reused for every publish. send() only queues a record for the producer's own batching, so the transport flushes when the application stops, closing the window where a queued record would die with the process.
Initialize the Kafka producer with connection config.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/transport.py
set_stop_event(stop_event)
¶
Ignore the shutdown signal: publishing is on demand, with no loop to stop.
start()
¶
stop()
¶
send(event_name, payload, headers, partition_key=None)
¶
Hand a pre-serialized event payload to the Kafka producer queue.
The payload is queued for the producer's own batching and is confirmed only by flush(), which the caller invokes at the end of its batch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event_name
|
str
|
Topic name (typically the event class name). |
required |
payload
|
bytes
|
Pre-serialized JSON bytes. |
required |
headers
|
dict[str, str]
|
Metadata headers for trace propagation. |
required |
partition_key
|
str | None
|
Key routing the message to one partition. None lets Kafka assign partitions round-robin. |
None
|
Raises:
| Type | Description |
|---|---|
EventDeliveryRejectedError
|
The producer rejected this record as too large. |
BufferError
|
The local producer queue is full. |
KafkaException
|
A different synchronous Kafka failure occurred. |
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/transport.py
flush()
¶
Block until the producer has sent every queued record.
A message-size rejection collected by the delivery callback is raised as
EventDeliveryRejectedError. Other callback errors remain transport-wide
KafkaException failures, so callers do not charge them to a record.
Raises:
| Type | Description |
|---|---|
EventDeliveryRejectedError
|
The broker refused a record as too large. |
KafkaException
|
Delivery failed for a transport-wide Kafka error. |
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/transport.py
AsyncKafkaEventTransport(config)
¶
Bases: IAsyncEventTransport, IAsyncService
Asynchronous Kafka event transport using aiokafka AIOKafkaProducer.
The producer lives as long as the application: it is opened when the application starts services and closed when the application stops them. A producer per publish would reopen the broker connection every time, defeat batching, and reset the idempotent producer sequence, which is bound to a producer instance lifetime.
send() hands a record over without waiting for its broker acknowledgement, so consecutive publishes fill one batch; flush() sends the batch out and reports whichever record the broker rejected. A publisher only ever learns the outcome of the records it handed over itself, because concurrent publishers share this Pod and one publisher's flush must not swallow another's rejected record.
aiokafka binds a producer to the event loop that created it, while the ApplicationContext runs its services on an internal event loop of its own. Publishers living on another loop (HTTP request handlers, tests) therefore have their producer calls routed back to the producer's own loop.
Initialize the async Kafka transport with connection config.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/transport.py
set_stop_event(stop_event)
¶
Ignore the shutdown signal: publishing is on demand, with no loop to stop.
start_async()
async
¶
Open the producer that serves every publish until the application stops.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/transport.py
stop_async()
async
¶
Deliver what is still buffered, then close the producer.
The transport stops accepting publishes before the producer closes, so a service still running during shutdown is told the transport stopped instead of being handed a delivery failure.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/transport.py
send(event_name, payload, headers, partition_key=None)
async
¶
Hand a pre-serialized event payload to the long-lived Kafka producer.
The record joins the producer's current batch and is confirmed only by flush(), which the caller invokes at the end of its batch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event_name
|
str
|
Topic name (typically the event class name). |
required |
payload
|
bytes
|
Pre-serialized JSON bytes. |
required |
headers
|
dict[str, str]
|
Metadata headers for trace propagation. |
required |
partition_key
|
str | None
|
Key routing the message to one partition. None lets Kafka assign partitions round-robin. |
None
|
Raises:
| Type | Description |
|---|---|
EventTransportNotRunningError
|
When the application has not started the transport's producer, or has already stopped it. |
EventDeliveryRejectedError
|
The producer rejected this record as too large. |
KafkaError
|
Any producer failure not attributable to this record's size, propagated without conversion. |
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/transport.py
flush()
async
¶
Block until the producer sent every record this publisher handed over.
Raises:
| Type | Description |
|---|---|
EventTransportNotRunningError
|
When the application has not started the transport's producer, or has already stopped it. |
EventDeliveryRejectedError
|
When the broker rejected one or more of this publisher's records as too large. Any other delivery failure keeps its original exception type. |
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/transport.py
이벤트 Consumer¶
MessageOutcome
¶
Bases: StrEnum
What running the handlers settled about one consumed message.
The consumer turns this into the offset action that gives the delivery guarantee: an offset must never move past a message whose failure is not stored anywhere else.
PROCESSED = 'processed'
class-attribute
instance-attribute
¶
The consumer is done with the message and commits its offset.
Covers handler success, every failure a retry cannot fix (a refused auth boundary, an empty message body), and a failure whose dead-letter record reached the broker.
RETRYABLE = 'retryable'
class-attribute
instance-attribute
¶
The message is still the only copy of its own failure, so it comes back.
Reached when dead-lettering itself failed. The consumer rewinds its position to the message instead of committing, and the next poll delivers it again.
KafkaEventConsumer(config)
¶
Bases: IEventConsumer, AbstractBackgroundService
Synchronous Kafka event consumer that polls messages and dispatches to handlers.
Initialize the Kafka consumer with connection config.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
producer
instance-attribute
¶
Dead-letter producer, bound by initialize for the service lifetime.
set_propagator(propagator)
¶
Set the trace propagator for extracting trace context from messages.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
propagator
|
ITracePropagator
|
An ITracePropagator instance. |
required |
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
register_auth_boundary(handler)
¶
Mark a registered post-processor endpoint as Kafka auth-aware.
register(event, handler)
¶
Register a handler for the given event type.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
initialize()
¶
Create Kafka topics, open the dead-letter producer, and subscribe.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
run()
¶
Poll Kafka for messages and route them to registered handlers.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
dispose()
¶
Flush pending dead-letter records and close the Kafka consumer.
The flush is bounded so an unreachable broker cannot block shutdown forever; records still queued at that point are reported by the delivery callback.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
AsyncKafkaEventConsumer(config)
¶
Bases: IAsyncEventConsumer, AbstractAsyncBackgroundService
Asynchronous Kafka event consumer that polls messages and dispatches to handlers.
Initialize the async Kafka consumer with connection config.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
producer
instance-attribute
¶
Dead-letter producer, bound by initialize_async for the service lifetime.
set_propagator(propagator)
¶
Set the trace propagator for extracting trace context from messages.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
propagator
|
ITracePropagator
|
An ITracePropagator instance. |
required |
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
register_auth_boundary(handler)
¶
Mark a registered post-processor endpoint as Kafka auth-aware.
register(event, handler)
¶
Register an async handler for the given event type.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
initialize_async()
async
¶
Create Kafka topics, open the dead-letter producer, and subscribe.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
run_async()
async
¶
Poll Kafka asynchronously for messages and route them to handlers.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/event/consumer.py
dispose_async()
async
¶
Flush pending dead-letter records and close the async Kafka consumer.
Auth Boundary¶
Authentication helpers for Kafka consumer boundaries.
KAFKA_AUTH_BOUNDARY = 'kafka'
module-attribute
¶
Provider-neutral boundary name used for Kafka AuthInvocation values.
KAFKA_AUTH_HEADERS_PARAMETER = '_spakky_kafka_headers'
module-attribute
¶
Internal endpoint keyword used to pass Kafka message headers.
KafkaHandlerAuthBinding(*, operation, protected)
dataclass
¶
KafkaAuthBoundary(container, application_context)
¶
Verify propagated snapshots and seed AuthContext for Kafka handlers.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/auth.py
seed_auth_context(headers, binding)
¶
Verify a Kafka snapshot header and store AuthContext before user code.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/auth.py
설정¶
Configuration for Kafka connections.
Provides configuration dataclass for Kafka connection parameters including bootstrap servers, consumer group, and security settings.
AutoOffsetResetType
¶
Bases: StrEnum
Kafka consumer auto offset reset policies.
KafkaConnectionConfig()
¶
Bases: BaseSettings
Kafka connection configuration loaded from environment variables.
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/common/config.py
group_id
instance-attribute
¶
Kafka consumer group identifier.
client_id
instance-attribute
¶
Kafka client identifier.
bootstrap_servers
instance-attribute
¶
Kafka bootstrap servers.
security_protocol = None
class-attribute
instance-attribute
¶
Security protocol for Kafka connection.
sasl_mechanism = None
class-attribute
instance-attribute
¶
SASL mechanism for Kafka authentication.
sasl_username = None
class-attribute
instance-attribute
¶
SASL username for Kafka authentication.
sasl_password = None
class-attribute
instance-attribute
¶
SASL password for Kafka authentication.
number_of_partitions = 1
class-attribute
instance-attribute
¶
Default number of partitions for created topics.
replication_factor = 1
class-attribute
instance-attribute
¶
Default replication factor for created topics.
auto_offset_reset = AutoOffsetResetType.EARLIEST
class-attribute
instance-attribute
¶
Consumer auto offset reset policy (earliest, latest, none).
poll_timeout = 1.0
class-attribute
instance-attribute
¶
Consumer poll timeout in seconds.
dead_letter_topic_suffix = '.dlt'
class-attribute
instance-attribute
¶
Suffix appended to the original topic name to build its dead-letter topic.
An empty suffix would make the dead-letter topic the original topic itself, republishing every failure back into the stream it came from, so it is rejected.
max_handler_retries = 0
class-attribute
instance-attribute
¶
How many times a failed handler dispatch is retried before dead-lettering.
Retrying re-invokes every handler registered for the event, so handlers must be idempotent when this is raised above zero. Deserialization failures never succeed on retry and are dead-lettered immediately regardless of this value.
dead_letter_delivery_timeout = 10.0
class-attribute
instance-attribute
¶
Seconds to wait for a dead-letter record to reach the broker.
Bounds how long the consumer blocks on one failed message; without it a broker outage would stall the poll loop until the client's own message timeout.
connection_configuration_dict
property
¶
librdkafka settings that only describe how to reach the cluster.
Shared by the admin client, the producer, and the consumer. Delivery
semantics live in producer_configuration_dict and
consumer_configuration_dict instead, because the safe producer default
and the safe consumer default are opposites of each other.
producer_configuration_dict
property
¶
Producer settings that keep retries from duplicating or reordering.
enable.idempotence makes the broker deduplicate retried batches and
pins max.in.flight.requests.per.connection low enough that a retry
cannot overtake an earlier batch on the same partition. acks=all waits
for every in-sync replica so an acknowledged event survives the loss of
the partition leader.
consumer_configuration_dict
property
¶
Consumer settings that tie the offset to the handler outcome.
enable.auto.commit=false is what makes delivery at-least-once: the
offset advances only when KafkaEventConsumer commits it after the
handlers finish, so a handler failure leaves the message to be
redelivered instead of silently dropping it.
async_producer_configuration_dict
property
¶
The same producer guarantees expressed as aiokafka keyword names.
aiokafka takes constructor keywords instead of a librdkafka property
dictionary, so the key spelling differs while the delivery guarantee is
identical to producer_configuration_dict.
후처리기¶
KafkaPostProcessor
¶
Bases: IPostProcessor, IContainerAware, IApplicationContextAware
Post-processor that registers event handlers with Kafka consumers.
Scans @EventHandler decorated classes for @event decorated methods and automatically registers them with the appropriate Kafka consumer (sync or async) with proper dependency injection.
set_container(container)
¶
Set the container for dependency injection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
container
|
IContainer
|
The IoC container. |
required |
set_application_context(application_context)
¶
Set the application context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
application_context
|
IApplicationContext
|
The application context instance. |
required |
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/post_processor.py
post_process(pod)
¶
Register event handlers from event handler classes.
Scans the event handler for methods decorated with @on_event and registers them with the appropriate Kafka consumer (sync or async) based on whether the method is a coroutine function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pod
|
object
|
The Pod to process. |
required |
Returns:
| Type | Description |
|---|---|
object
|
The Pod, with event handlers registered if it's an event handler. |
Source code in plugins/spakky-kafka/src/spakky/plugins/kafka/post_processor.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | |
추가 모듈¶
DeadLetterHeaderKey
¶
Bases: StrEnum
Kafka header keys describing where a dead-lettered message came from.
A reprocessing tool must be able to decide what to do from these headers alone, without parsing the original message body.