Pub/sub, singkatan dari publish/subscribe, adalah pola komunikasi yang memisahkan pengirim pesan dari penerima pesannya. Pengirim tidak mengirim pesan kepada pihak tertentu, melainkan menerbitkannya (publish) ke sebuah channel, sedangkan pihak yang berminat berlangganan (subscribe) pada channel yang sama. Topik ini membahas cara kerja pola tersebut di Redis, implementasi langganan dan penerbitan dengan redis-py, pemeriksaan perilakunya lewat redis-cli, serta jaminan pengiriman yang disediakan dan yang tidak disediakan. Seluruh contoh menggunakan channel berawalan berita: pada server Redis 8.0.2 di port 6379, dan tidak menyisakan key apa pun karena pesan tidak pernah disimpan.
Cara kerja pub/sub di Redis#
Pengirim pesan pada pola ini disebut penerbit (publisher), sedangkan penerimanya disebut pelanggan (subscriber). Perintah PUBLISH mengirimkan pesan ke satu channel, misalnya berita:kelas, dan server langsung meneruskan salinan pesan itu kepada setiap klien yang sedang berlangganan channel tersebut pada saat itu juga. Penerbit dan pelanggan sama sekali tidak saling mengenal, karena keduanya hanya berhubungan dengan nama channel. Nama channel mengikuti konvensi penamaan key yang lazim, misalnya berita:kelas untuk pengumuman sebuah kelas, sehingga pesan topik yang berbeda tidak saling bercampur.
Pesan yang diterbitkan tidak pernah disimpan di server. Redis hanya meneruskan pesan kepada pelanggan yang terhubung pada saat penerbitan, dan setelah penerusannya selesai, pesan itu langsung dilupakan. Penerusan salinan dilakukan sepenuhnya oleh server, sehingga penerbit tidak perlu mengirim ulang pesan untuk setiap penerima. Sifat inilah yang membedakan pub/sub dari penyimpanan data biasa, sehingga konsekuensinya dibahas tersendiri pada bagian akhir. Diagram berikut memperlihatkan satu penerbitan yang diterima tiga pelanggan sekaligus.
Langganan dan penerbitan dengan redis-py#
Skrip berikut memperlihatkan pola lengkapnya dengan redis-py. Tiga klien bernama Ali, Budi, dan Citra berlangganan channel berita:kelas, dan setiap langganan diawali pesan konfirmasi dari server yang dibaca dengan get_message. Membaca konfirmasi bukan formalitas, karena langganan baru berlaku setelah konfirmasinya diterima, sehingga penerbitan yang dilakukan setelahnya sudah pasti sampai. Setelah ketiga pelanggan terdaftar, satu panggilan publish dikeluarkan, dan balasannya adalah jumlah pelanggan yang menerima pesan tersebut.
import redis
r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)
CHANNEL = "berita:kelas"
# tiga pelanggan berlangganan channel yang sama
nama_pelanggan = ["Ali", "Budi", "Citra"]
pelanggan = []
for nama in nama_pelanggan:
p = r.pubsub()
p.subscribe(CHANNEL)
# langganan baru berlaku setelah konfirmasi server dibaca
konfirmasi = p.get_message(timeout=5)
print(f"{nama}: langganan aktif pada {konfirmasi['data']} channel")
pelanggan.append(p)
# satu penerbitan, dan server yang meneruskan salinannya
jumlah = r.publish(CHANNEL, "Pengumuman: ujian dimulai pukul 10")
print(f"pesan diterima {jumlah} pelanggan")
# setiap pelanggan membaca salinannya sendiri dari koneksi masing-masing
for nama, p in zip(nama_pelanggan, pelanggan):
pesan = p.get_message(timeout=5)
print(f"{nama} menerima: {pesan['data']}")
# tutup seluruh koneksi pelanggan dengan rapi
for p in pelanggan:
p.close()Ali: langganan aktif pada 1 channel
Budi: langganan aktif pada 1 channel
Citra: langganan aktif pada 1 channel
pesan diterima 3 pelanggan
Ali menerima: Pengumuman: ujian dimulai pukul 10
Budi menerima: Pengumuman: ujian dimulai pukul 10
Citra menerima: Pengumuman: ujian dimulai pukul 10Keluaran memperlihatkan sifat fan-out dari pola ini, yaitu satu pesan diperbanyak menjadi salinan untuk setiap pelanggan. Angka 1 pada tiga baris pertama berasal dari isi pesan konfirmasi, yaitu jumlah channel yang sedang dilanggankan oleh masing-masing klien. Beban pengiriman berada di sisi server, bukan di skrip yang mengirim ulang pesan tiga kali. Setiap pelanggan membaca salinannya dari koneksi masing-masing melalui get_message, sedangkan argumen timeout menentukan berapa lama klien menunggu ketika belum ada pesan yang siap. Di akhir skrip, setiap koneksi pelanggan ditutup dengan close() agar tidak ada koneksi yang tertinggal terbuka.
Pesan tanpa pelanggan#
Perilaku yang paling sering mengejutkan terjadi ketika pesan diterbitkan tanpa satu pun pelanggan. Perintah PUBLISH mengembalikan jumlah pelanggan yang menerima pesan tersebut, sehingga keadaan tanpa pelanggan langsung terbaca dari balasannya. Perintah PUBSUB NUMSUB memberi angka yang sama tanpa perlu menerbitkan pesan, sehingga keduanya cocok dipasangkan pada satu pemeriksaan. Demo berikut memeriksa jumlah pelanggan channel berita:piket terlebih dahulu, lalu menerbitkan pesan ke channel yang tidak dijaga siapa pun.
# memeriksa jumlah pelanggan channel sebelum penerbitan
redis-cli PUBSUB NUMSUB berita:piket
# menerbitkan pesan ketika belum ada satu pun pelanggan
redis-cli PUBLISH berita:piket "Jadwal piket kelas berganti ke hari Kamis"berita:piket
0
0Angka 0 pada kedua balasan berarti tidak ada satu pun salinan yang diteruskan, dan pesan itu hilang tanpa jejak. Kegagalan semacam ini tidak menimbulkan pesan error apa pun di sisi penerbit, sehingga angka 0 itulah satu-satunya tanda bahwa pesan tidak sampai. Redis tidak menyimpan pesan untuk pelanggan yang datang belakangan, tidak menyiapkan antrean pengiriman, dan tidak menyediakan pembacaan ulang atas pesan yang sudah lewat. Pesan yang diterbitkan sebelum server memproses langganan tidak pernah sampai, dan konfirmasi langganan yang dibaca klien adalah bukti bahwa pendaftaran itu sudah terjadi. Aturan ini berlaku seragam pada seluruh channel, sehingga berita:piket dan berita:kelas menerima perlakuan yang sama.
Batas jaminan pengiriman#
Pub/sub Redis bekerja dengan model fire-and-forget, yaitu server mengirimkan pesan lalu langsung melupakannya tanpa menunggu tanda terima. Tidak ada pengakuan (acknowledgment) dari pelanggan, tidak ada pengiriman ulang, dan tidak ada catatan pesan yang dapat dibaca kembali. Jaminan yang tersedia adalah at-most-once, yang berarti pesan boleh hilang, tetapi tidak pernah diterima dua kali oleh pelanggan yang sama. Apabila koneksi pelanggan terputus, seluruh pesan yang diterbitkan selama koneksi terputus hilang, dan pelanggan yang tersambung kembali memulai dari keadaan kosong.
Kecepatan pelanggan juga bagian dari kontrak ini. Pesan yang belum dibaca ditampung pada buffer koneksi setiap pelanggan, sehingga pelanggan yang lambat membaca membuat buffer itu terus tumbuh, dan server dapat memutus koneksi pelanggan yang terlambat sesuai batas client-output-buffer-limit pubsub yang dikonfigurasi. Untuk pesan yang tidak boleh hilang, Redis menyediakan stream, yaitu struktur data yang menyimpan setiap entri sampai dihapus secara eksplisit dan mendukung pembacaan ulang dari titik mana pun.
Batas-batas tersebut bukan kekurangan yang disembunyikan, melainkan konsekuensi dari desain yang menempatkan kecepatan di atas ketahanan. Pub/sub menjadi pilihan yang tepat untuk pesan yang nilai utamanya ada pada saat pengiriman, misalnya pengumuman kelas, penanda pembatalan cache, atau notifikasi jadwal piket yang hanya relevan beberapa menit. Apabila setiap pesan harus pasti sampai, pola inilah yang perlu dihindari. Keputusan untuk menggunakan atau menghindari pola ini sebaiknya diambil dari jaminan yang benar-benar disediakan, bukan dari harapan akan jaminan yang tidak ada.
Ringkasan#
PUBLISHmeneruskan salinan pesan kepada setiap pelanggan yang sedang berlangganan pada saat penerbitan, dan balasannya adalah jumlah pelanggan yang menerima.- Langganan baru berlaku setelah konfirmasi server dibaca, sehingga penerbitan yang dilakukan setelahnya sudah pasti sampai.
- Pesan tidak disimpan, sehingga pelanggan yang belum terhubung atau sedang terputus tidak menerima apa pun, dan tidak ada pengiriman ulang.
- Untuk pesan yang tidak boleh hilang, stream menyimpan setiap entri sampai dihapus, sedangkan pub/sub tepat untuk pesan yang relevan hanya pada saat dikirim.
Referensi#
- Pub/Sub — Redis — Dokumentasi resmi pola pub/sub beserta penjelasan pengiriman fire-and-forget dan batas jaminannya.
- PUBLISH — Redis — Perintah penerbitan pesan beserta balasan berupa jumlah pelanggan yang menerimanya.
- SUBSCRIBE — Redis — Perintah langganan channel beserta bentuk konfirmasi langganannya.
- PUBSUB — Redis — Perintah introspeksi untuk membaca daftar channel dan jumlah pelanggannya.