Le traitement de données en temps réel a évolué d'un défi de codage impératif complexe vers une discipline déclarative plus structurée. Pour les ingénieurs de données, la capacité à exprimer une logique de flux complexe de manière concise n'est plus un simple luxe ; c'est une nécessité pour maintenir des architectures évolutives. Deux des principaux frameworks, Apache Flink et Kafka Streams, ont adopté ce changement en offrant des interfaces déclaratives puissantes : Flink SQL et le DSL (Domain-Specific Language) de Kafka Streams.
Ce guide explore comment exploiter ces approches déclaratives pour écrire des applications de traitement de flux plus propres et plus maintenables. Nous comparerons les capacités sémantiques des deux outils et fournirons des exemples de code pratiques pour illustrer leurs forces distinctes.
La puissance de la logique déclarative
Le traitement de flux impératif oblige souvent les développeurs à gérer manuellement l'état, les sémantiques temporelles et le fenêtrage. Ce contrôle de bas niveau est puissant, mais au détriment de la lisibilité et de la maintenabilité. La logique déclarative, en revanche, se concentre sur ce que les données devraient être plutôt que sur comment les transformer étape par étape.
En abstrayant la complexité de la gestion de l'état et du traitement du temps d'événement, les interfaces déclaratives permettent aux équipes de se concentrer sur la logique métier. C'est particulièrement bénéfique dans les grandes organisations où les pipelines de données doivent être auditable et facilement compris par les analystes de données qui peuvent ne pas avoir d'expertise approfondie en Java ou Scala.
Apache Flink SQL : La couche de requête unifiée
Apache Flink a positionné SQL comme un citoyen de premier ordre, traitant le streaming et le batch de manière uniforme. Flink SQL vous permet de définir des tables représentant des sujets Kafka, des fichiers HDFS ou des bases de données JDBC, puis de les interroger comme s'il s'agissait de tables relationnelles statiques. Le moteur gère automatiquement les sémantiques de streaming en arrière-plan.
Considérons un scénario où vous devez calculer la latence moyenne des requêtes API sur une fenêtre glissante de 5 minutes. Dans Flink SQL, c'est simple :
CREATE TABLE api_logs (
request_id STRING,
user_id STRING,
latency_ms INT,
timestamp TIMESTAMP(3),
WATERMARK FOR `timestamp` AS `timestamp` - INTERVAL '5' SECOND
) WITH (
'connector' = 'kafka',
'topic' = 'api-logs',
'properties.bootstrap.servers' = 'kafka-broker:9092',
'format' = 'json'
);
CREATE VIEW avg_latency_view AS
SELECT
user_id,
TUMBLE_END(`timestamp`, INTERVAL '5' MINUTE) AS window_end,
AVG(latency_ms) AS avg_latency
FROM api_logs
GROUP BY
user_id,
TUMBLE(`timestamp`, INTERVAL '5' MINUTE);
INSERT INTO sink_table SELECT * FROM avg_latency_view;
Notez l'utilisation de la définition WATERMARK. Flink SQL gère automatiquement la progression du temps d'événement, permettant des agrégations précises et tolérantes aux données tardives sans code explicite pour les TTL d'état ou la gestion des dépassements de délai.
DSL Kafka Streams : Flexibilité centrée sur Java
Bien que Flink SQL soit excellent pour les jointures complexes et les transformations multi-tables, le DSL Kafka Streams offre une proposition de valeur différente : une intégration étroite avec l'écosystème Java et un contrôle fin sur la topologie du flux. Le DSL est basé sur la chaînage de méthodes, permettant une interface fluide et lisible qui reste fortement typée.
Voici comment vous pourriez exprimer une logique de filtrage et d'agrégation similaire dans le DSL Kafka Streams :
KStream logs = builder.stream("api-logs",
Consumed.with(Serdes.String(), Serdes.Serializer.of(ApiLog.class)));
KTable aggregated = logs
.filter((key, value) -> value.getLatencyMs() > 0)
.groupByKey(Grouped.with(Serdes.String(), Serdes.Serializer.of(ApiLog.class)))
.windowedBy(TimeWindows.ofSizeWithNoGrace(Duration.ofMinutes(5)))
.aggregate(
Aggregation.with(
() -> new AggregatedLatency(0, 0),
(key, value, agg) -> agg.add(value),
Serdes.Serializer.of(AggregatedLatency.class)
)
)
.toStream();
aggregated.to("aggregated-latency", Produced.with(Serdes.String(), Serdes.Serializer.of(AggregatedLatency.class)));
Le DSL Kafka Streams brille lorsque vous avez besoin de magasins d'état personnalisés, de requêtes interactives ou de sorties secondaires complexes qui ne sont pas facilement exprimables en SQL. L'API fluide facilite la construction de pipelines multi-étapes tout en maintenant la sécurité des types.
Choisir le bon outil
Le choix entre Flink SQL et le DSL Kafka Streams dépend souvent des exigences spécifiques de votre pipeline de données :
- Utilisez Flink SQL lorsque :
- Vous avez besoin de jointures complexes multi-tables.
- Votre équipe inclut des analystes de données qui préfèrent SQL.
- Vous souhaitez unifier la logique de traitement batch et stream.
- L'évolution du schéma et la gestion du catalogue sont critiques.
- Utilisez le DSL Kafka Streams lorsque :
- Vous avez besoin d'une intégration étroite avec des services Java existants.
- Vous avez besoin d'implémentations personnalisées de magasins d'état.
- Les requêtes interactives ou les recherches clé-valeur depuis des systèmes externes sont fréquentes.
- Vous avez besoin d'un contrôle fin sur les sémantiques exactly-once au niveau de l'enregistrement.
Conclusion
Le traitement de flux déclaratif ne consiste pas à supprimer le contrôle ; il s'agit d'élever le niveau d'abstraction pour correspondre à la complexité des architectures de données modernes. À la fois Flink SQL et le DSL Kafka Streams offrent des outils robustes et prêts pour la production pour construire des applications en temps réel. En comprenant les forces de chacun, les ingénieurs de données peuvent construire des pipelines qui ne sont pas seulement performants, mais aussi maintenables et évolutifs. Alors que l'industrie continue de se diriger vers les « data fabrics » et la gestion unifiée des données, la capacité à basculer sans effort entre les paradigmes déclaratif et impératif sera une compétence clé pour tout professionnel des données.