Pub/Sub #

Dalam arsitektur sistem terdistribusi modern, pola komunikasi asinkron merupakan pilar utama yang menjamin keandalan, skalabilitas, dan fleksibilitas antar layanan (loosely coupled services). Ketika sebuah aplikasi monolitik dipecah menjadi puluhan microservices, komunikasi sinkron via HTTP API secara berantai (synchronous chaining) akan memicu masalah latensi kumulatif dan titik kegagalan tunggal (single point of failure). Jika salah satu layanan di hilir (downstream) mengalami gangguan, seluruh transaksi di hulu (upstream) akan gagal seketika.

Google Cloud Pub/Sub (Publish/Subscribe) hadir untuk mengatasi tantangan tersebut sebagai layanan messaging middleware berskala global yang dikelola sepenuhnya oleh Google (fully managed). Didesain dengan model serverless murni, Pub/Sub memungkinkan tim pengembang mengirim dan menerima jutaan pesan per detik tanpa perlu melakukan instalasi, klusterisasi, atau penyetelan broker pesan tradisional seperti RabbitMQ atau Apache Kafka. Pub/Sub bertindak sebagai sistem saraf pusat (central nervous system) yang menghubungkan seluruh komponen infrastruktur cloud kita secara andal, asinkron, dan otomatis.


Arsitektur Internal dan Mekanisme Pengiriman Pesan #

Memahami cara kerja internal Pub/Sub akan mempermudah kita mendesain pola pengiriman pesan yang efisien dan toleran terhadap kegagalan. Di latar belakang, Pub/Sub memisahkan peran pengirim pesan (publisher) dengan penerima pesan (subscriber) menggunakan entitas logis yang disebut Topic dan Subscription.

flowchart TD
    Publisher["Publisher App (e.g. Cloud Run, GCF)"] -->|"Publish Message"| Topic["Pub/Sub Topic"]
    Topic -->|"Push Delivery"| PushSub["Push Subscription"]
    Topic -->|"Pull Delivery"| PullSub["Pull Subscription"]
    PushSub -->|"HTTP POST (CloudEvents)"| ServiceA["Cloud Run Target Service"]
    PullSub -->|"gRPC Pull Request"| Worker["Worker Instance (Pull Consumer)"]
    ServiceA -->|"Error/Max Retry Exceeded"| DLQ["Dead Letter Queue Topic"]

    style PushSub stroke:#0288d1,stroke-width:2px
    style PullSub stroke:#0288d1,stroke-width:2px

1. Peran Topic dan Subscription #

  • Topic: Topic adalah sebuah saluran (channel) atau destinasi logis tempat publisher mengirimkan pesan. Publisher tidak pernah tahu siapa yang akan membaca pesan tersebut; tugas publisher hanyalah mengemas pesan ke dalam format data biner atau JSON dan mengirimkannya ke endpoint HTTP/gRPC milik Topic.
  • Subscription: Subscription adalah entitas yang mewakili ketertarikan pada satu Topic tertentu. Satu Topic dapat memiliki banyak Subscription (fan-out pattern). Setiap pesan yang masuk ke Topic akan disalin secara otomatis ke seluruh Subscription yang aktif di bawahnya. Melalui model ini, satu event (misalnya order_created) dapat diproses secara paralel oleh sistem gudang, sistem pembayaran, dan sistem email secara independen.

2. Mekanisme Pengiriman At-Least-Once #

Pub/Sub secara native memberikan jaminan pengiriman pesan bertipe At-Least-Once. Artinya, Google Cloud menjamin bahwa setiap pesan yang dipublikasikan akan dikirimkan setidaknya satu kali ke setiap subscriber yang berhak. Setelah mengirimkan pesan, Pub/Sub akan menunggu sinyal konfirmasi (Acknowledgement atau ACK) dari subscriber dalam batas waktu tertentu (Ack Deadline).

Jika subscriber memproses pesan dengan sukses dan mengirimkan ACK, pesan tersebut akan dihapus dari antrean Subscription. Namun, jika terjadi kendala jaringan atau aplikasi crash sebelum ACK dikirim, Pub/Sub akan mengirimkan kembali (redeliver) pesan yang sama setelah batas waktu habis. Konsekuensinya, pengembang wajib mendesain sistem subscriber agar bersifat Idempotent (aman memproses pesan yang sama berulang kali tanpa merusak state data).

3. Kemampuan Exactly-Once Delivery #

Untuk mengurangi kompleksitas penanganan pesan duplikat di sisi aplikasi, GCP menyediakan opsi Exactly-Once Delivery pada tingkat Subscription. Ketika opsi ini diaktifkan, Pub/Sub menggunakan mekanisme rekonsiliasi state internal berbasis database global Google Spanner untuk melacak status ACK pesan secara real-time. Jika sebuah pesan sedang dalam proses atau sudah berhasil di-ACK oleh satu konsumen, Pub/Sub akan memblokir pengiriman ulang pesan yang sama ke konsumen lain, bahkan dalam kondisi kegagalan jaringan regional sekalipun.


Jenis-Jenis Subscription: Pull vs. Push vs. Direct Export #

Dalam mendesain integrasi, Pub/Sub menawarkan tiga metode utama untuk menyampaikan pesan ke konsumen. Pemilihan metode ini sangat memengaruhi latensi, biaya komputasi, dan struktur jaringan sistem kita.

1. Pull Subscription (Konsumsi Reaktif) #

Dalam model Pull, aplikasi subscriber bertindak aktif dengan mengirimkan request gRPC secara berkala ke API Pub/Sub untuk meminta pesan baru (polling).

  • Cara Kerja: Subscriber mengirim panggilan Pull, menerima sekumpulan pesan, memprosesnya, lalu mengirim panggilan Acknowledge dengan membawa ID pesan terkait.
  • Skenario Terbaik: Sangat cocok untuk sistem worker pengolahan batch skala besar (heavy batch processing), aplikasi stateful yang berjalan di Compute Engine atau Google Kubernetes Engine (GKE), atau ketika konsumen ingin mengontrol penuh kecepatan konsumsi pesan (flow control) agar tidak membebani kapasitas memori internal mereka.

2. Push Subscription (Konsumsi Otomatis Serverless) #

Dalam model Push, layanan Pub/Sub bertindak proaktif sebagai webhook client yang mengirimkan pesan langsung ke endpoint HTTP/HTTPS milik subscriber.

  • Cara Kerja: Setiap kali ada pesan baru masuk ke subscription, Pub/Sub akan mengirimkan request HTTP POST dengan membawa data payload pesan ke URL target yang sudah ditentukan (misalnya endpoint Cloud Run).
  • Skenario Terbaik: Sangat ideal untuk layanan berbasis serverless seperti Cloud Run atau Cloud Functions karena kita tidak perlu menulis kode looping polling pesan. Kontainer Cloud Run hanya perlu bangkit menerima request HTTP biasa dan akan di-scale otomatis berdasarkan volume event masuk. Model ini juga menghemat biaya karena kontainer subscriber dapat diturunkan hingga nol (scale-to-zero) saat tidak ada event.

3. Direct Export Subscriptions (BigQuery & Cloud Storage) #

Pub/Sub juga mendukung pengiriman data langsung ke gudang data (data warehouse) atau penyimpanan objek tanpa perlu menulis kode aplikasi perantara (glue code).

  • BigQuery Subscription: Pesan yang masuk ke Topic akan langsung ditulis ke dalam tabel BigQuery yang dituju secara real-time. Layanan ini menangani parsing skema JSON secara otomatis dan menulis data dengan format streaming.
  • Cloud Storage Subscription: Pesan akan langsung diakumulasikan dan ditulis ke dalam file teks atau Avro di dalam bucket Google Cloud Storage berdasarkan interval waktu atau ukuran file tertentu. Opsi ini sangat berguna untuk membuat pipeline log audit atau backup data mentah berbiaya rendah.

Pengurutan Pesan (Message Ordering) dan Skema #

Membangun arsitektur event-driven skala besar membutuhkan kontrol ketat atas integritas data. Pub/Sub menyediakan dua fitur canggih untuk mengatasi masalah urutan eksekusi dan struktur payload data.

Message Ordering (Pengurutan Berbasis Key) #

Secara default, Pub/Sub mendistribusikan pesan secara paralel lintas partisi global, sehingga urutan kedatangan pesan di sisi konsumen tidak selalu sama dengan urutan saat dipublikasikan. Namun, untuk use case tertentu seperti riwayat transaksi bank atau log perubahan database (Change Data Capture), urutan eksekusi adalah hal mutlak.

  • Ordering Key: Kita dapat menyematkan string penanda yang disebut ordering key pada pesan. Pub/Sub menjamin bahwa semua pesan yang memiliki ordering key yang sama akan dikirimkan ke subscriber secara berurutan (first-in-first-out per key).
  • Trade-off: Pengaktifan fitur ini membatasi kapasitas throughput parallel pengiriman pesan, karena Pub/Sub harus menahan pesan berikutnya sampai pesan sebelumnya berhasil diproses dan dikirimkan ACK oleh subscriber.

Schema Registry (Validasi Kontrak Data) #

Ketika sistem dibangun oleh banyak tim yang berbeda, struktur payload pesan rawan mengalami perubahan tak terduga (breaking changes) yang dapat merusak aplikasi hilir. Pub/Sub mengatasi ini dengan menyediakan fitur Schema Registry.

  • Cara Kerja: Kita dapat mendaftarkan skema data formal menggunakan format Apache Avro atau Protocol Buffers (Protobuf) pada tingkat Topic. Sebelum pesan diterima oleh Topic, Pub/Sub akan melakukan validasi struktur payload. Jika pesan melanggar aturan skema (misalnya field wajib hilang atau tipe data salah), Pub/Sub akan menolak pesan tersebut dengan kode error HTTP 400.
  • Keuntungan: Menjamin kontrak data antar layanan tetap kokoh tanpa membutuhkan kode validasi manual di sisi subscriber.

Retry Policy, Dead Letter Queue (DLQ), dan Error Handling #

Kegagalan sistem hilir adalah keniscayaan dalam komputasi awan. Pub/Sub memiliki sistem penanganan error bawaan untuk memastikan tidak ada pesan penting yang hilang selama gangguan terjadi.

1. Backoff Retry Policy #

Ketika subscriber gagal memproses pesan dan mengembalikan respon non-2xx (untuk Push) atau memanggil Nack (untuk Pull), Pub/Sub akan mengantrekan kembali pesan tersebut untuk dikirim ulang.

  • Immediate Retry (Default): Pesan dikirim kembali sesegera mungkin tanpa jeda waktu.
  • Exponential Backoff: Kita dapat mengonfigurasi batas minimum dan maksimum delay (misalnya jeda 10 detik hingga maksimum 600 detik). Jeda waktu pengiriman akan berlipat ganda setiap kali kegagalan berulang, memberikan waktu bagi sistem database backend yang sedang down untuk pulih terlebih dahulu sebelum dibombardir kembali oleh request pesan.

2. Dead Letter Queue (DLQ) #

Terdapat kategori bug khusus yang disebut poison message — pesan yang memiliki format rusak atau korup sehingga akan selalu gagal diproses oleh subscriber sekreatif apa pun penanganan error kita. Tanpa penanganan khusus, pesan racun ini akan memicu perulangan tak terbatas (infinite retry loop) yang membuang-buang memori aplikasi dan biaya komputasi.

  • Mekanisme DLQ: Kita dapat mengonfigurasi properti dead-letter-topic dan batas maksimum percobaan pengiriman (max delivery attempts, misalnya 5 kali). Jika sebuah pesan gagal diproses sebanyak 5 kali berturut-turut, Pub/Sub secara otomatis akan memindahkan pesan tersebut ke Topic khusus Dead Letter Queue untuk disimpan dan dianalisis secara manual oleh tim developer. Pesan asli kemudian akan dihapus dari Subscription utama agar aliran pesan normal lainnya tidak tersendat.

Contoh Implementasi Kode: Node.js Publisher & Consumer #

Mari kita buat contoh implementasi nyata menggunakan bahasa pemrograman Node.js dan SDK @google-cloud/pubsub resmi untuk mendemonstrasikan proses publikasi pesan yang aman serta pembuatan pull consumer yang tangguh.

1. Instalasi Library SDK #

Pasang pustaka resmi Google Cloud Pub/Sub di proyek Node.js kita:

npm install @google-cloud/pubsub

2. Kode Publisher (publisher.js) #

Kode berikut menunjukkan cara mempublikasikan pesan ke Topic dengan menyertakan metadata kustom, ordering key, dan penanganan kegagalan secara asinkron.

// BENAR: Menggunakan SDK resmi @google-cloud/pubsub
import { PubSub } from '@google-cloud/pubsub';

const pubSubClient = new PubSub({
    projectId: 'my-gcp-project-id'
});

async function publishOrderEvent(orderId, orderData) {
    const topicName = 'order-events';
    const topic = pubSubClient.topic(topicName, {
        // Optimasi konfigurasi batching untuk throughput tinggi
        batching: {
            maxMessages: 100,
            maxMilliseconds: 10,
        }
    });

    // Konversi payload aplikasi menjadi buffer biner
    const dataBuffer = Buffer.from(JSON.stringify(orderData));

    // ✓ BENAR: Menambahkan atribut kustom untuk keperluan routing/filtering 
    // dan menyertakan ordering key untuk menjamin urutan proses per pengguna
    const messageAttributes = {
        eventType: 'ORDER_CREATED',
        sourceSystem: 'ecommerce-checkout',
    };
    
    const orderingKey = `user-${orderData.userId}`;

    try {
        console.log(`Mengirim pesan untuk Order ID: ${orderId}...`);
        
        const messageId = await topic.publishMessage({
            data: dataBuffer,
            attributes: messageAttributes,
            orderingKey: orderingKey
        });

        console.log(`Pesan sukses terkirim. Message ID: ${messageId}`);
        return messageId;
    } catch (error) {
        // ✗ JANGAN abaikan kegagalan publikasi. Tulis ke log sentral untuk audit
        console.error(`Gagal mengirimkan pesan ke topic ${topicName}:`, error);
        throw error;
    }
}

// Simulasi eksekusi fungsi
publishOrderEvent('ORD-99882', {
    orderId: 'ORD-99882',
    userId: '10029',
    totalAmount: 1250000,
    items: ['SSD 1TB', 'RAM DDR5 32GB']
});

3. Kode Pull Subscriber (subscriber.js) #

Kode di bawah mendefinisikan sebuah subscriber bertipe Pull yang mendengarkan event baru secara persistent, mengonfigurasi batas penanganan pesan paralel, dan melakukan pengelolaan konfirmasi pesan (ACK/NACK) secara aman.

import { PubSub } from '@google-cloud/pubsub';

const pubSubClient = new PubSub({
    projectId: 'my-gcp-project-id'
});

function startSubscriptionListener() {
    const subscriptionName = 'inventory-order-sub';
    const subscription = pubSubClient.subscription(subscriptionName, {
        // Mengatur batas konkurensi konsumsi memori internal aplikasi
        flowControl: {
            maxMessages: 20, // Maksimal 20 pesan yang diproses paralel dalam satu waktu
        }
    });

    console.log(`Subscriber mendengarkan pesan dari: ${subscriptionName}...`);

    // Handler ketika ada pesan masuk
    subscription.on('message', async (message) => {
        console.log(`Menerima pesan ID: ${message.id}`);
        console.log(`Atribut Pesan:`, message.attributes);

        try {
            // Parsing payload mentah
            const orderPayload = JSON.parse(message.data.toString());
            console.log(`Memproses update inventaris untuk Order ID: ${orderPayload.orderId}`);

            // Jalankan logika bisnis utama (misalnya update stok database)
            await updateInventoryStock(orderPayload.items);

            // ✓ BENAR: Kirim sinyal ACK jika pemrosesan sukses tanpa error
            message.ack();
            console.log(`Pesan ${message.id} sukses diproses dan di-ACK.`);
        } catch (error) {
            // ✗ JANGAN biarkan error mematikan jalannya loop worker.
            console.error(`Gagal memproses pesan ${message.id}:`, error);
            
            // Kirim sinyal NACK agar Pub/Sub segera meredistribusi pesan ini ke instans worker lain
            message.nack();
        }
    });

    // Handler ketika terjadi error konektivitas internal SDK
    subscription.on('error', (error) => {
        console.error(`Terjadi error internal pada subscription listener:`, error);
    });
}

// Simulasi fungsi database asinkron
async function updateInventoryStock(items) {
    return new Promise((resolve) => setTimeout(resolve, 500));
}

// Jalankan worker listener
startSubscriptionListener();

Perbandingan: GCP Pub/Sub vs. Apache Kafka vs. AWS SQS/SNS #

Untuk membantu arsitek sistem memilih solusi perutean pesan yang paling sesuai dengan kebutuhan teknis mereka, tabel di bawah membandingkan Pub/Sub dengan solusi terkemuka lainnya di industri.

Parameter Evaluasi Google Cloud Pub/Sub Apache Kafka AWS SQS & SNS
Abstraksi Utama Unified (Satu API Topic/Sub) Log Partisi Terdistribusi SQS (Antrean) & SNS (Topik) Terpisah
Model Scaling Otomatis Penuh (Serverless) Manual (Menambah Node/Partition) Otomatis Penuh
Penyimpanan Pesan Sementara (Hingga 7 hari) Permanen / Durasi Panjang Sementara (Hingga 14 hari)
Sistem Skema Built-in (Avro & Protobuf) Eksternal (Confluent Schema Registry) Tidak Didukung secara native
Throughput Global Jutaan pesan/detik tanpa setup Sangat Tinggi (Perlu konfigurasi kluster) Tinggi (SQS FIFO terbatas tanpa batching)
Exactly-Once Delivery Didukung secara native di Subscription Didukung di level produsen/konsumen khusus Didukung di antrean bertipe FIFO saja

Best Practice untuk Skalabilitas dan Keamanan #

Membangun arsitektur Pub/Sub yang aman dan efisien memerlukan penerapan prinsip-prinsip desain berikut:

1. Tangani Idempotency Konsumen Secara Konsisten #

Karena adanya jaminan pengiriman At-Least-Once, duplikasi pengiriman pesan akibat kegagalan koneksi di akhir transaksi adalah hal yang lumrah. Konsumen kita harus dirancang untuk menolak request duplikat.

  • BENAR: Gunakan ID transaksi bisnis unik yang disematkan dalam payload (misalnya orderId atau UUID transaksi) sebagai kunci unik (unique key) di database target. Jika database mendeteksi kunci yang sama sudah pernah ditulis, segera abaikan pesan tersebut tanpa memicu error.

2. Gunakan Pola Payload Reference untuk Data Besar (> 10MB) #

Batas ukuran pesan maksimal di Google Cloud Pub/Sub adalah 10 MB per pesan. Mengirim payload dengan ukuran mendekati batas ini adalah anti-pattern besar karena akan meningkatkan latensi transmisi jaringan dan memakan biaya bandwidth yang mahal.

  • BENAR: Gunakan Claim Check Pattern. Unggah file data besar (seperti video atau gambar) ke Google Cloud Storage terlebih dahulu. Kemudian, kirimkan pesan Pub/Sub kecil yang hanya berisi metadata dan URL referensi file di Cloud Storage tersebut. Subscriber kemudian akan mengunduh file secara langsung dari bucket GCS saat pemrosesan dimulai.

3. Pantau Metrik Backlog Secara Proaktif #

Kesehatan sistem event-driven kita tercermin dari kemampuan konsumen memproses antrean pesan secara tepat waktu.

  • BENAR: Buat dashboard pemantauan di Cloud Monitoring menggunakan metrik pubsub.googleapis.com/subscription/num_undelivered_messages (jumlah backlog pesan) dan pubsub.googleapis.com/subscription/oldest_unacked_message_age (usia pesan terlama yang belum di-ACK). Setel alert otomatis ke Slack atau pager jika usia pesan tidak terkirim melebihi batas 15 menit, yang mengindikasikan adanya bug atau macetnya aplikasi worker kita.

Ringkasan #

  • Google Cloud Pub/Sub adalah broker pesan serverless berskala global yang memisahkan publisher dan subscriber secara asinkron.
  • Jaminan At-Least-Once mengharuskan pengembang mendesain konsumen yang bersifat idempotent guna menangani potensi duplikasi pesan.
  • Push Subscription sangat cocok untuk Cloud Run karena memicu auto-scaling instans subscriber secara native berdasarkan traffic event.
  • Schema Registry mendukung Avro dan Protobuf untuk menjamin konsistensi kontrak payload pesan tanpa penulisan kode validasi manual.
  • Gunakan Dead Letter Queue (DLQ) untuk menangani poison message secara otomatis dan mencegah siklus retry yang tidak terbatas.
  • Gunakan claim check pattern dengan memindahkan payload data berukuran besar (> 10MB) ke Cloud Storage dan mengirimkan referensi URL-nya saja.

← Sebelumnya: Cloud Run   Berikutnya: Workflows →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact