Apa Itu JMS (Java Message Service) dan Kapan Sebaiknya Dipakai

JMS (Java Message Service), yang sekarang bernama Jakarta Messaging, adalah API standar di Java untuk mengirim dan menerima pesan lewat perantara yang disebut message broker. Aplikasi pengirim tidak memanggil aplikasi penerima secara langsung; ia menitipkan pesan ke broker, lalu penerima mengambilnya saat siap. Hasilnya, dua sistem bisa bekerja dengan kecepatan berbeda, salah satunya boleh mati sebentar, dan pesan tetap tidak hilang.

Perlu dipahami sejak awal: JMS adalah spesifikasi API, bukan produk dan bukan protokol jaringan. Kode Anda ditulis terhadap antarmuka JMS, sedangkan pekerjaan sebenarnya dilakukan oleh provider, misalnya ActiveMQ Artemis, IBM MQ, atau broker lain yang menyediakan klien JMS. Karena itu kode yang sama secara prinsip bisa dipindah ke provider lain cukup dengan mengganti konfigurasi dan library klien.

Dari javax.jms ke jakarta.jms

Selama bertahun-tahun JMS menjadi bagian dari Java EE dengan package javax.jms. Setelah Java EE diserahkan ke Eclipse Foundation dan berganti nama menjadi Jakarta EE, spesifikasinya ikut berganti nama menjadi Jakarta Messaging. Mulai Jakarta Messaging 3.0, package-nya berubah menjadi jakarta.jms. Isi API-nya hampir sama; yang berubah terutama nama package. Jika Anda membaca tutorial lama dengan import javax.jms.*, konsepnya masih berlaku, tetapi untuk proyek baru gunakan jakarta.jms dan pastikan library klien broker Anda memakai varian Jakarta.

Komponen utama JMS

Ada beberapa istilah yang akan terus muncul. Pahami dulu perannya; detail kodenya bisa menyusul.

Connection factory

Connection factory adalah objek yang tahu cara terhubung ke broker: alamat server, port, kredensial, dan pengaturan lain. Di server aplikasi Jakarta EE, objek ini biasanya diambil dari JNDI (direktori nama milik server). Di aplikasi mandiri atau Spring Boot, Anda sering membuatnya langsung dari kelas milik provider. Dari connection factory inilah lahir koneksi dan sesi.

Connection, session, dan JMSContext

Di API klasik (JMS 1.1), Anda membuat Connection, lalu Session, lalu producer atau consumer. Sejak JMS 2.0 ada JMSContext yang menggabungkan koneksi dan sesi menjadi satu objek, sehingga kodenya jauh lebih pendek. Satu hal yang perlu diingat: session dan JMSContext tidak aman dipakai bersamaan oleh banyak thread. Beri setiap thread konteksnya sendiri.

Destination: queue dan topic

Destination adalah alamat tujuan pesan. JMS mengenal dua jenis:

  • Queue (antrean) untuk model point-to-point. Satu pesan hanya diproses oleh satu consumer, walaupun ada beberapa consumer yang mendengarkan queue yang sama. Cocok untuk membagi pekerjaan, misalnya memproses pesanan.
  • Topic untuk model publish-subscribe. Satu pesan disalin ke setiap subscriber yang berlangganan. Cocok untuk menyiarkan kejadian, misalnya “harga produk berubah” yang perlu diketahui beberapa layanan.

Perbedaan keduanya, termasuk durable subscription dan shared subscription, saya bahas lebih rinci di artikel memilih queue atau topic di JMS.

Producer dan consumer

Producer adalah bagian kode yang mengirim pesan ke destination. Consumer adalah bagian yang menerima. Consumer bisa menarik pesan secara sinkron dengan receive(), atau menerima secara asinkron lewat MessageListener yang dipanggil setiap kali pesan datang.

Message

Pesan JMS terdiri dari tiga bagian: header standar (misalnya JMSMessageID, JMSCorrelationID, JMSDeliveryMode, JMSExpiration, JMSPriority), property bebas yang Anda tentukan sendiri, dan body. Jenis body yang tersedia antara lain TextMessage (paling sering dipakai, biasanya berisi JSON atau XML), BytesMessage, MapMessage, StreamMessage, dan ObjectMessage. Hindari ObjectMessage untuk integrasi antarsistem karena bergantung pada serialisasi Java dan membawa risiko keamanan saat deserialisasi.

Gambaran kode singkat

Contoh berikut mengirim satu pesan teks ke queue memakai JMSContext. Kelas connection factory diambil dari klien ActiveMQ Artemis varian Jakarta, dan body JSON ditulis dengan text block Java 15 ke atas.

import jakarta.jms.ConnectionFactory;
import jakarta.jms.JMSContext;
import jakarta.jms.Queue;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;

public class KirimPesanan {
    public static void main(String[] args) {
        ConnectionFactory cf = new ActiveMQConnectionFactory(
                "tcp://localhost:61616",
                System.getenv("BROKER_USER"),
                System.getenv("BROKER_PASSWORD"));
        String payload = """
                {"orderId": "INV-1001", "total": 150000}
                """;
        try (JMSContext context = cf.createContext()) {
            Queue queue = context.createQueue("pesanan.baru");
            context.createProducer()
                   .setProperty("sumber", "checkout")
                   .send(queue, payload);
        }
    }
}

Bagian consumer, acknowledge mode, dan cara menutup resource dengan benar dibahas langkah demi langkah di panduan mengirim dan menerima pesan JMS dengan Java.

Jaminan yang diberikan JMS

Pesan persistent (DeliveryMode.PERSISTENT, nilai bawaan) disimpan broker ke media penyimpanan sebelum pengiriman dianggap berhasil, sehingga tetap ada walau broker restart. Pesan non-persistent lebih cepat tetapi bisa hilang. Selain itu ada acknowledgement: consumer memberi tahu broker bahwa pesan sudah diterima, dan sampai saat itu broker belum menghapus pesannya. Jika consumer mati sebelum mengirim acknowledgement, pesan akan dikirim ulang.

Konsekuensinya, dalam praktik JMS memberi jaminan at-least-once: pesan bisa sampai lebih dari sekali dalam kondisi gagal tertentu. Logika consumer sebaiknya tahan terhadap duplikat.

Kapan JMS layak dipakai

JMS bukan jawaban untuk semua komunikasi antarsistem. Panggilan REST biasa tetap lebih sederhana bila Anda butuh jawaban saat itu juga. JMS cocok ketika:

  • Pekerjaan tidak harus selesai saat itu juga. Mengirim email konfirmasi, membuat label pengiriman, atau sinkronisasi stok bisa diantrekan tanpa membuat pengguna menunggu.
  • Sistem penerima kadang lambat atau mati. Pesan menumpuk di queue dan diproses ketika penerima pulih.
  • Beban datang tidak rata. Saat lonjakan, queue menjadi penyangga dan consumer memproses sesuai kemampuannya.
  • Anda sudah berada di ekosistem Java atau Jakarta EE. Server aplikasi seperti WildFly, Payara, atau Open Liberty mendukung JMS dan message-driven bean.
  • Butuh transaksi atau acknowledgement yang jelas. JMS mendukung sesi transaksi dan, di lingkungan Jakarta EE, transaksi XA bersama database.

Kapan sebaiknya tidak

Jika pengirim harus langsung tahu hasilnya, misalnya validasi kartu atau cek stok di halaman produk, pola request-reply lewat broker hanya menambah kerumitan. Jika Anda butuh menyimpan aliran event dalam jumlah sangat besar dan memutar ulang data lama, platform log terdistribusi seperti Kafka lebih pas, walaupun Kafka tidak memakai API JMS. Dan jika sistem Anda ditulis dalam banyak bahasa, pertimbangkan protokol netral seperti AMQP; banyak broker mendukung JMS dan AMQP sekaligus.

Kesalahan umum pemula

  1. Membuat koneksi baru untuk setiap pesan. Membuka koneksi itu mahal. Pakai ulang koneksi atau gunakan pool.
  2. Lupa menutup resource. Koneksi yang tidak ditutup menumpuk di broker. Gunakan try-with-resources karena JMSContext mengimplementasikan AutoCloseable.
  3. Mengira pesan pasti sampai tepat sekali. Siapkan consumer untuk menerima duplikat.
  4. Memakai topic tanpa durable subscription untuk data penting. Subscriber non-durable yang sedang offline tidak akan menerima pesan yang terbit selama ia mati.
  5. Tidak memasang batas pengiriman ulang. Tanpa dead letter queue, satu pesan rusak bisa terus gagal dan menyibukkan consumer.

Langkah belajar berikutnya

Cara tercepat memahami JMS adalah menjalankan broker lokal, misalnya ActiveMQ Artemis lewat Docker, lalu mengirim beberapa pesan dan melihatnya di konsol web broker. Amati apa yang terjadi saat consumer dimatikan, saat pesan gagal diproses, dan saat broker di-restart. Percobaan kecil ini membuat istilah seperti acknowledgement, redelivery, dan persistent terasa konkret, bukan sekadar definisi di buku.

Hendra Kurniawan

Oleh Hendra Kurniawan

Pengembang backend Java yang lama menangani integrasi sistem dan message broker. Menulis catatan praktis tentang JMS, API, dan arsitektur aplikasi.