Episode ini adalah episode paling fundamental untuk menulis pipeline: kalian membuat job Flink pertama dengan Java dan memakai transformasi dasar seperti map, flatMap, filter, keyBy, window, dan reduce. Kalian juga memahami stream partitioning, parallelism, dan operasi berbasis key.

Tiga episode pertama sudah membangun pijakan: konsep, arsitektur, dan operasi cluster. Sekarang waktunya bagian yang paling seru — menulis pipeline sungguhan dengan DataStream API. Episode 4 ini membekali kalian dengan transformasi dasar yang akan dipakai di hampir setiap job Flink: map, flatMap, filter, keyBy, window, dan reduce.
Transformasi adalah tempat logika bisnis hidup. Begitu kalian menguasai pola source → transformasi → sink dan memahami bagaimana data berpindah antar operator, kalian sudah bisa membangun mayoritas pipeline streaming yang ada di dunia nyata. Mari kita mulai.
Buat project Maven dengan dependency Flink. Dependency inti adalah flink-streaming-java dan flink-clients:
mvn archetype:generate \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DgroupId=com.example -DartifactId=flink-basic -Dversion=1.0.0Tambahkan dependency di pom.xml dengan versi yang selaras dengan Flink cluster kalian, misalnya 2.3.0. Pastikan main class ditentukan di pom.xml agar flink run bisa menemukannya.
Semua aplikasi DataStream mengikuti kerangka yang sama:
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
public class JobPertama {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.fromElements("budi", "siti", "andi")
.map(String::toUpperCase)
.print();
env.execute("job-pertama");
}
}env.fromElements membuat stream dari data statis (berguna untuk eksperimen), dan env.execute menandai akhir pipeline. Job ini siap dibangun dengan Maven dan dijalankan dengan flink run -d.
map mengubah satu elemen menjadi satu elemen lain. Ini transformasi satu-ke-satu:
env.fromElements("budi", "siti", "andi")
.map(kata -> kata.length())
.print();Kode di atas mengubah tiap nama menjadi panjang karakternya. Penerapan map berguna untuk normalisasi data, konversi format, dan ekstraksi field.
flatMap mengubah satu elemen menjadi nol, satu, atau banyak elemen. Ini adalah transformasi yang paling fleksibel:
env.fromElements("budi makan nasi", "siti minum kopi")
.flatMap((kalimat, out) -> {
for (String kata : kalimat.split(" ")) {
out.collect(kata);
}
})
.print();Perhatikan bahwa flatMap memakai Collector untuk mengeluarkan hasil. Operator flatMap sangat berguna untuk memecah baris, menormalisasi event, dan membuang elemen secara selektif.
filter mempertahankan elemen yang memenuhi kondisi:
env.fromElements(1, 2, 3, 4, 5, 6)
.filter(angka -> angka % 2 == 0)
.print();Kode ini hanya meloloskan bilangan genap. Gunakan filter untuk memangkas data di awal pipeline — memfilter lebih awal berarti lebih sedikit data yang diproses operator berikutnya.
keyBy membagi stream menjadi beberapa logical partition berdasarkan sebuah key. Semua elemen dengan key yang sama dikirim ke subtask yang sama. Ini prasyarat untuk operasi stateful per-key seperti agregasi:
env.fromElements("budi:jakarta", "siti:bandung", "andi:jakarta")
.map(baris -> baris.split(":"))
.map(parts -> new String[] { parts[0], parts[1] })
.keyBy(data -> data[1])
.print();Setelah keyBy, semua data kota yang sama dijamin dipegang subtask yang sama. Pahami keyBy sebagai cara Flink mengelompokkan data untuk komputasi paralel yang konsisten.
Selain keyBy, ada partitioning yang lebih eksplisit:
rebalance(): mendistribusikan data secara round-robin ke semua subtask.broadcast(): mengirim salinan data ke semua subtask.rescale(): redistribusi antar subtask yang berdekatan secara efisien.Partitioning ini dipakai untuk kebutuhan khusus seperti membagikan konfigurasi dinamis (broadcast) atau memecah kemiringan beban (rebalance).
Operator window mengumpulkan elemen dalam jendela waktu tertentu untuk diagregasi. Contoh tumbling window 10 detik:
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
env.fromData(Tuple2.of("jakarta", 100L), Tuple2.of("bandung", 50L), Tuple2.of("jakarta", 75L))
.keyBy(t -> t.f0)
.window(TumblingEventTimeWindows.of(Time.minutes(1)))
.reduce((a, b) -> Tuple2.of(a.f0, a.f1 + b.f1))
.print();reduce mengagregasi elemen dalam window secara inkremental — sangat efisien karena tidak menyimpan semua elemen. Detail windowing penuh (tumbling, sliding, session) akan kita bahas di episode 5.
Setiap operator bisa punya parallelism berbeda, diatur per-job atau per-operator:
env.setParallelism(4);
env.fromElements("a", "b", "c")
.map(kata -> kata.toUpperCase()).setParallelism(2)
.print();env.setParallelism(4) menetapkan default 4 untuk seluruh job, dan setParallelism(2) menimpa parallelism operator map menjadi 2. Ingat bahwa parallelism dijalankan dalam batas slot yang tersedia — jika slot habis, job menunggu.
Flink otomatis menggabungkan operator yang berdekatan tanpa state menjadi satu task untuk mengurangi overhead. Chaining ini bisa disesuaikan dengan disableChaining() atau startNewChain() — kita akan membahasnya sebagai strategi tuning di episode 15.
source → map → filter → keyBy → window → reduce → sinkGrafik di atas menggambarkan pipeline yang kalian pelajari di episode ini: elemen mengalir dari source, ditransformasi, dipartisi per key, diagregasi dalam window, lalu dikirim ke sink.
Tip
Selalu mulai eksperimen dengan env.fromElements sebelum menyambungkan source sungguhan. Ini mempercepat iterasi karena kalian tidak perlu infrastruktur eksternal hanya untuk menguji logika transformasi.
Episode 4 membekali kalian dengan transformasi inti DataStream API: map untuk transformasi satu-ke-satu, flatMap untuk satu-ke-banyak, filter untuk menyaring, keyBy untuk mempartisi stream secara logis, serta window dan reduce untuk agregasi berbasis waktu dan per-key. Kalian juga memahami parallelism dan chaining yang menentukan cara pipeline dieksekusi.
Inti yang harus dibawa pulang:
env.execute di akhir.flatMap adalah transformasi paling fleksibel karena bisa mengeluarkan nol hingga banyak elemen.keyBy mengelompokkan data per key agar komputasi stateful dan agregasi berjalan konsisten.reduce mengagregasi elemen secara inkremental tanpa menyimpan seluruh isi window.Di episode 5 selanjutnya kita akan membahas event time, watermarks, dan windowing — memahami perbedaan event time dan processing time, membangkitkan watermark serta menangani lateness, mengenal window types tumbling, sliding, dan session, serta mengatur window trigger dan allowed lateness. Ini adalah fondasi untuk analitik berbasis waktu yang akurat.