Contrary to RPC-like applications, where communication and dependencies are explicitly defined between Services; data flowing between Event-Driven Applications is defined by how do they react to and emit events. A trade-off between Data-flow explicitness and Service autonomy becomes apparent between this two architectural-styles. The goal in this presentation is to demonstrate how Distributed-Tracing can help to cope with this trade-off, turning messaging exchange between decoupled, autonomous, Event-Driven Services, into explicit Data-flows. Zipkin project brings a Distributed-Tracing infrastructure that enables the collection, processing, and visualization of traces produced by RPC-based, as well as messaging-based applications.
This presentation includes demonstrations on how to enable Tracing for Kafka Streams applications, Kafka Connectors, and KSQL; evidencing how implicit Services behavior and communication through the event-log become can become explicit via Distributed-Tracing. But collecting and visualizing traces is just the first step. In order to create insights from tracing-data, models has to be built to enable an better understanding from the system, and improve our operational capabilities. Including research-based experiences from Netflix[1] and Facebook[2] on how tracing-data has been processed and polished with multiple purposes, this presentation will cover how service-dependency analysis and anomaly-detection models can be built on top of it.
12. @jeqo89 at #kafkasummit
“Complexity is anything [...]
that makes a system
hard to understand
and modify”
John Ousterhout, “A Philosophy of Software Design”
16. @jeqo89 at #kafkasummit
Talk: “Making sense of your event-driven dataflows”
40 min
Q&AWhy?
What distributed
tracing?
How to instrument Kafka
apps?
Demo
What’s next?
Demo
time
28. @jeqo89 at #kafkasummit
“The more accurately you
try to measure the position
of a particle,
the less accurately
you can measure its speed”
Heisenberg's uncertainty principle
31. @jeqo89 at #kafkasummit
/** Annotation-based approach **/
ScopedSpan span =
tracer.startScopedSpan("process");
try { // The span is in "scope"
doProcess();
} catch (RuntimeException | Error e) {
span.error(e); // mark as error
throw e;
} finally {
span.finish(); // always finish
}
32. @jeqo89 at #kafkasummit
/** Annotation-based approach **/
ScopedSpan span =
tracer.startScopedSpan("process");
try { // The span is in "scope"
doProcess();
} catch (RuntimeException | Error e) {
span.error(e); // mark as error
throw e;
} finally {
span.finish(); // always finish
}
33. @jeqo89 at #kafkasummit
/** Annotation-based approach **/
ScopedSpan span =
tracer.startScopedSpan("process");
try { // The span is in "scope"
doProcess();
} catch (RuntimeException | Error e) {
span.error(e); // mark as error
throw e;
} finally {
span.finish(); // always finish
}
34. @jeqo89 at #kafkasummit
/** Instrumentation for Kafka Clients **/
Producer<K, V> producer =
new KafkaProducer<>(settings);
Producer<K, V> tracedProducer =
kafkaTracing.producer(producer);
producer.send(
new ProducerRecord<>(
"my-topic", key, value
));
35. @jeqo89 at #kafkasummit
/** Instrumentation for Kafka Clients **/
Producer<K, V> producer =
new KafkaProducer<>(settings);
Producer<K, V> tracedProducer =
kafkaTracing.producer(producer); // wrap
tracedProducer.send(
new ProducerRecord<>(
"my-topic", key, value
));
36. @jeqo89 at #kafkasummit
/** Instrumentation for Kafka Clients **/
Consumer<K, V> consumer =
new KafkaConsumer<>(settings);
Consumer<K, V> tracedConsumer =
kafkaTracing.consumer(consumer);
while (running) {
var records = consumer.poll(1000);
records.forEach(this::process);
}
37. @jeqo89 at #kafkasummit
/** Instrumentation for Kafka Clients **/
Consumer<K, V> consumer =
new KafkaConsumer<>(settings);
Consumer<K, V> tracedConsumer =
kafkaTracing.consumer(consumer); // wrap
while (running) {
var records = tracedConsumer.poll(1000);
records.forEach(this::process);
}
39. @jeqo89 at #kafkasummit
/** Instrumentation for Kafka Streams **/
var b = new StreamsBuilder();
b.stream("input-topic")
.map(this::parseRecord))
.join(table, this::tableJoiner)
.transformValues(this::transform))
.to("output-topic");
KafkaStreams kafkaStreams =
new KafkaStreams(b.build(), config);
kafkaStreams.start();
40. @jeqo89 at #kafkasummit
/** Instrumentation for Kafka Streams **/
var b = new StreamsBuilder();
b.stream("input-topic")
.map(this::parseRecord))
.join(table, this::tableJoiner)
.transformValues(this::transform))
.to("output-topic");
KafkaStreams kafkaStreams = // wrap
ksTracing.kafkaStreams(b.build(), config);
kafkaStreams.start();
41. @jeqo89 at #kafkasummit
/** Instrumentation for Kafka Streams **/
var b = new StreamsBuilder();
b.stream("input-topic")
.transform(ksTracing.map(“parse”,
this::parseRecord))
.join(table, this::tableJoiner)
.transformValues(ksTracing.transformValues(
“transform”,
this::transform)))
.to("output-topic");
45. @jeqo89 at #kafkasummit
/** Transports for Zipkin **/
var sender =
URLConnectionSender.create(
"http://localhost:9411/api/v2/spans"
);
var reporter = AsyncReporter.create(sender);
46. @jeqo89 at #kafkasummit
/** Transports for Zipkin **/
var sender =
KafkaSender.newBuilder()
.bootstrapServers(
"localhost:9092")
.build();
var reporter = AsyncReporter.create(sender);
47. @jeqo89 at #kafkasummit
/** Transports for Zipkin **/
var sender =
BringYourOwnSender.newBuilder()
.build();
var reporter = AsyncReporter.create(sender);
56. @jeqo89 at #kafkasummit
Report Transport Storage
Dependencies
(batch)
Distributed Tracing IS A Stream Processing Problem
Data-at-rest
services
57. @jeqo89 at #kafkasummit
Distributed Tracing IS A Stream Processing Problem
Span
Consumer
Trace
Aggregation
Span Store
spans-collected
traces-completed
Dependencies
Store
github.com/jeqo/zipkin-storage-kafka
Custom
processors