Dalam dunia sistem terdistribusi dan microservice, komunikasi antar layanan menjadi faktor kunci. Banyak pengembang langsung memilih HTTP/REST, gRPC, atau message broker seperti RabbitMQ. Namun, ada satu teknologi yang sering dipakai di sistem performa tinggi tetapi jarang dibahas secara populer, yaitu ZeroMQ (ØMQ).
ZeroMQ bukan server, bukan broker, melainkan library messaging yang memberi kita kontrol penuh atas bagaimana microservice saling berkomunikasi. Artikel ini akan membahas bagaimana ZeroMQ dapat digunakan untuk membangun arsitektur microservice yang cepat, skalabel, dan fleksibel.
Apa itu ZeroMQ?
Contents
- 0.1 Apa itu ZeroMQ?
- 0.2 Konsep Microservice dengan ZeroMQ
- 0.3 Messaging Pattern yang Penting
- 0.4 Contoh Microservice: Image Processing
- 0.5 Paralelisme dan Skalabilitas
- 0.6 Monitoring Queue
- 0.7 Keamanan Microservice ZeroMQ
- 0.8 ZeroMQ vs Message Broker
- 0.9 Kapan ZeroMQ Cocok untuk Microservice?
- 1 Contoh Kasus
ZeroMQ adalah asynchronous messaging library yang bekerja di atas TCP, IPC, atau in-process communication. ZeroMQ menyediakan berbagai messaging pattern yang bisa langsung dipakai tanpa harus membangun protokol sendiri dari nol.
Ciri utama ZeroMQ:
- Tidak membutuhkan server pusat
- Ringan dan sangat cepat
- Mendukung komunikasi asynchronous
- Cocok untuk sistem paralel dan terdistribusi
ZeroMQ banyak dipakai di:
- Jupyter Notebook (komunikasi kernel)
- Sistem Computer Vision & AI
- High Performance Computing (HPC)
- Trading system dan data streaming
Konsep Microservice dengan ZeroMQ
Pada microservice tradisional, setiap service berkomunikasi lewat HTTP endpoint. Dengan ZeroMQ, pendekatannya sedikit berbeda:
- Setiap service adalah process mandiri
- Komunikasi dilakukan lewat socket ZeroMQ
- Routing dan skalabilitas dikontrol oleh arsitektur
Contoh sederhana:
Client → Image Service → Result
Namun di ZeroMQ, arsitektur bisa dikembangkan menjadi:
Client → ROUTER → DEALER → Worker Service
Messaging Pattern yang Penting
1. REQ–REP (Request–Reply)
Digunakan untuk komunikasi sederhana:
- Client mengirim request
- Server membalas reply
Cocok untuk:
- API sederhana
- Prototyping
Keterbatasan:
- Tidak fleksibel untuk paralel
- Harus strict request–reply
2. ROUTER–DEALER (Direkomendasikan)
Ini adalah pola utama untuk microservice ZeroMQ.
Keunggulan:
- Mendukung banyak client
- Reply bersifat private (berdasarkan identity)
- Bisa asynchronous
- Mudah diskalakan
Pola ini memungkinkan server mengetahui client mana yang mengirim request tanpa client lain mengetahuinya.
3. PUSH–PULL (Pipeline)
Digunakan untuk job queue satu arah:
- PUSH: kirim job
- PULL: worker ambil job
Cocok untuk:
- Batch processing
- Pipeline data
Contoh Microservice: Image Processing
Arsitektur
Client → ROUTER → DEALER → Image Worker
- Client mengirim gambar
- Worker memproses gambar (grayscale, edge, OCR, dll)
- Hasil dikirim kembali ke client yang benar
Setiap worker berjalan sebagai service terpisah, sehingga benar-benar memenuhi konsep microservice.
Paralelisme dan Skalabilitas
Salah satu keunggulan ZeroMQ adalah paralelisme alami.
- Worker bisa dijalankan sebanyak core CPU
- Tidak perlu tahu jumlah client
- Beban otomatis dibagi
Jika beban meningkat:
- Jalankan worker baru
- Tidak perlu restart sistem
Hal ini sangat cocok untuk:
- AI inference server
- Image & video processing
- Sistem dengan lonjakan trafik
Monitoring Queue
ZeroMQ tidak menyediakan queue monitoring secara default. Namun ini bisa diatasi dengan:
zmq.proxy()+ capture socket- Menghitung job masuk vs job selesai
- Estimasi panjang antrian
Dengan pendekatan ini, kita bisa:
- Mendeteksi bottleneck
- Menentukan kapan menambah worker
- Membuat auto-scaling sederhana
Keamanan Microservice ZeroMQ
Secara default, ZeroMQ tidak aman. Namun ZeroMQ menyediakan mekanisme keamanan yang kuat:
CurveZMQ
- Enkripsi end-to-end
- Public–private key
- Client tidak dikenal otomatis ditolak
Best Practice Keamanan
- Gunakan CurveZMQ
- Jangan expose port ke publik
- Worker gunakan IPC (lokal)
- Batasi ukuran pesan
- Validasi payload
Dengan konfigurasi benar, ZeroMQ bisa setara aman dengan HTTPS.
ZeroMQ vs Message Broker
| Aspek | ZeroMQ | RabbitMQ |
|---|---|---|
| Tipe | Library | Broker |
| Latency | Sangat rendah | Lebih tinggi |
| Persistence | Tidak ada | Ada |
| Monitoring | Manual | Built-in |
| Fleksibilitas | Sangat tinggi | Terbatas |
ZeroMQ unggul untuk real-time microservice, sementara RabbitMQ unggul untuk reliability dan durability.
Kapan ZeroMQ Cocok untuk Microservice?
Gunakan ZeroMQ jika:
- Membutuhkan latency rendah
- Beban komputasi tinggi
- Microservice bersifat stateless
- Data besar (gambar, array, tensor)
Hindari ZeroMQ jika:
- Tidak boleh kehilangan pesan
- Butuh audit log & persistence
- Ingin solusi siap pakai tanpa banyak coding
Contoh Kasus
Kita akan membuat contoh penggunaan ZeroMQ untuk
2 client mengirim gambar → server memproses → hasil dikirim balik ke client
Model ini mirip chat, tapi isi pesannya data gambar.
Arsitektur (gampang dulu)
Pola ZeroMQ yang cocok: REQ – REP
Client A ─┐
├──> Server (olah gambar)
Client B ─┘
- Client = kirim gambar
- Server = terima → proses → balas hasil
1. Server ZeroMQ (image processor)
simpan dengan nama server.py
import zmq
import cv2
import numpy as np
import pickle
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
print("Server siap di port 5555...")
while True:
# terima data
data = socket.recv()
# deserialize
img = pickle.loads(data)
# === PROSES GAMBAR (contoh: grayscale + edge) ===
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200)
# serialize hasil
reply = pickle.dumps(edges)
socket.send(reply)
2. Client ZeroMQ (pengirim gambar)
simpan dengan nama client.py
import zmq
import cv2
import pickle
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
# baca gambar
img = cv2.imread("gambar.jpg")
# kirim ke server
socket.send(pickle.dumps(img))
# terima hasil
result = pickle.loads(socket.recv())
# tampilkan
cv2.imshow("Hasil dari server", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. Jalankan 2 client sekaligus
Buka 3 terminal:
Terminal 1 (server)
python server.py
Terminal 2 (client A)
python client.py
Terminal 3 (client B)
python client.py
Server akan melayani client satu per satu secara bergantian
Membuat Server bekerja dengan Beberapa Client
Server harus tahu “alamat/identitas” tiap client, lalu membalas langsung ke client itu saja.
Di ZeroMQ, ini tidak bisa dengan REP/REQ biasa kalau kamu mau kontrol penuh. Solusinya: ROUTER ↔ DEALER.
Setiap client punya ID unik
Server:
-
Menerima pesan + identity pengirim
-
Membalas pakai identity itu
Client lain tidak akan menerima pesan tersebut. Maka arsitektur yang benar adalah
Client A (ID=A) ──┐
├──> ROUTER Server ── balas ke A
Client B (ID=B) ──┘ ── balas ke B
Private reply, bukan broadcast
Mari kita coba
1. Server (ROUTER)
server.py
import zmq
import cv2
import numpy as np
import pickle
context = zmq.Context()
socket = context.socket(zmq.ROUTER)
socket.bind("tcp://*:5555")
print("Server ROUTER siap...")
while True:
# ROUTER menerima: [identity][empty][data]
identity, empty, data = socket.recv_multipart()
print(f"Pesan dari client: {identity}")
img = pickle.loads(data)
# proses gambar
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
reply = pickle.dumps(gray)
# balas hanya ke client ini
socket.send_multipart([
identity,
b"",
reply
])
2. Client (DEALER)
client.py
import zmq
import cv2
import pickle
import uuid
context = zmq.Context()
socket = context.socket(zmq.DEALER)
# identity unik tiap client
client_id = f"client-{uuid.uuid4()}".encode()
socket.setsockopt(zmq.IDENTITY, client_id)
socket.connect("tcp://localhost:5555")
print("Client ID:", client_id)
img = cv2.imread("gambar.jpg")
# kirim
socket.send(pickle.dumps(img))
# terima balasan (PRIVATE)
result = pickle.loads(socket.recv())
cv2.imshow("Hasil dari server", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Server multi-task (advanced)
Mari kita perluas dengan server yang multi tasking.
-
Banyak client kirim gambar
-
Server memproses secara paralel
-
Balasan hanya ke client pengirim
-
Tidak saling tahu
Desainnya seperti berikut
Client A ─┐
Client B ─┼──> [ROUTER] ──> [DEALER] ──> Worker 1
Client C ─┘ ├──> Worker 2
└──> Worker 3
Peran:
-
ROUTER (frontend) → tahu identity client
-
DEALER (backend) → load balancing ke worker
-
Worker → proses gambar
-
Reply balik lewat jalur yang sama
1. Server Utama (Broker)
server.py
import zmq
context = zmq.Context()
# frontend: client <-> server
frontend = context.socket(zmq.ROUTER)
frontend.bind("tcp://*:5555")
# backend: server <-> worker
backend = context.socket(zmq.DEALER)
backend.bind("ipc://backend.ipc")
print("Server broker jalan...")
# otomatis forward bolak-balik
zmq.proxy(frontend, backend)
Server ini tidak mengolah data, cuma mengatur lalu lintas
2. Worker (Proses Paralel)
📌 worker.py
import zmq
import cv2
import pickle
import time
context = zmq.Context()
socket = context.socket(zmq.DEALER)
socket.connect("ipc://backend.ipc")
print("Worker siap...")
while True:
# terima multipart:
# [client_id][empty][data]
client_id, empty, data = socket.recv_multipart()
img = pickle.loads(data)
# simulasi proses berat
time.sleep(2)
# proses gambar
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
reply = pickle.dumps(gray)
# kirim balik ke client yang benar
socket.send_multipart([
client_id,
b"",
reply
])
3. Client (DEALER, private)
📌 client.py
import zmq
import cv2
import pickle
import uuid
context = zmq.Context()
socket = context.socket(zmq.DEALER)
client_id = f"client-{uuid.uuid4()}".encode()
socket.setsockopt(zmq.IDENTITY, client_id)
socket.connect("tcp://localhost:5555")
print("Client:", client_id)
img = cv2.imread("gambar.jpg")
socket.send(pickle.dumps(img))
result = pickle.loads(socket.recv())
cv2.imshow("Hasil", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
4. Cara Menjalankan (INI PENTING)
Terminal 1 → Server
Terminal 2,3,4 → Worker (bebas mau berapa)
Terminal lain → Banyak client
- Worker otomatis dibagi beban
- Client tidak saling tahu
- Reply tepat sasaran