← Kembali ke blog

Celery, RQ, atau BullMQ: Cara Membangun Antrian Tugas dengan Proxy dan Menghindari Batas Permintaan per IP

Membahas arsitektur antrean tugas dengan rotasi proxy di Celery, RQ, dan BullMQ - bagaimana mendistribusikan beban antara alamat IP dan menghindari batasan laju.

📅20 Oktober 2026

Ketika parser atau bot mengirimkan ratusan permintaan per menit dari satu IP, server dengan cepat mengaktifkan batas laju, dan selanjutnya — captcha atau larangan. Solusinya adalah antrean tugas yang mendistribusikan permintaan di antara pekerja dan proxy. Dalam artikel ini, kita akan membahas bagaimana arsitektur seperti itu bekerja di Seledri, RQ, dan BullMQ, serta bagaimana mengelola batas permintaan untuk setiap alamat IP dengan baik.

Mengapa antrean tugas saat bekerja dengan proxy

Jika tugasnya adalah mengumpulkan 50.000 kartu produk dari Wildberries atau memeriksa status 10.000 akun Instagram, menjalankan permintaan secara berurutan akan memakan waktu berjam-jam, sementara menjalankan secara paralel tanpa kontrol akan cepat terkena larangan berdasarkan IP. Antrean tugas (task queue) menyelesaikan kedua masalah ini: ia membagi volume kerja yang besar menjadi tugas-tugas kecil, mendistribusikannya di antara beberapa pekerja, dan memungkinkan untuk mengaitkan setiap pekerja dengan proxy yang berbeda.

Arsitektur biasanya terlihat seperti ini: broker pesan (Redis atau RabbitMQ) menyimpan antrean tugas, pekerja mengambil tugas dari antrean dan melaksanakannya, setiap pekerja menggunakan proxy-nya sendiri atau merotasi proxy dari kumpulan. Ini memungkinkan untuk menjaga beban pada setiap IP dalam batas yang aman, bahkan jika total volume permintaan sangat besar.

Pendekatan ini digunakan tidak hanya oleh pengembang parser, tetapi juga oleh tim yang mengotomatiskan pemeriksaan akun iklan Facebook Ads, memantau harga di Ozon, atau mengumpulkan data dari puluhan situs secara bersamaan. Tanpa antrean dan distribusi yang baik berdasarkan IP, sistem semacam itu akan terjebak dalam larangan pada hari pertama operasinya.

Masalah batas permintaan untuk satu IP

Sebagian besar situs dan API memiliki perlindungan terhadap beban berlebih. Ini dapat bervariasi: server mengembalikan HTTP 429 (Terlalu Banyak Permintaan), menampilkan captcha, sementara itu melarang IP sementara selama 10-60 menit atau menambahkannya ke daftar hitam selamanya. Batasan di berbagai platform sangat berbeda — misalnya, API Wildberries hanya mengizinkan sejumlah permintaan terbatas per detik dari satu alamat, sementara Instagram bereaksi terhadap aktivitas mencurigakan setelah 15-20 tindakan berturut-turut.

Kesalahan utama saat membangun antrean adalah menjalankan semua pekerja melalui satu IP yang sama. Bahkan jika tugas-tugas dibagi di antara 20 pekerja, tetapi semuanya menggunakan satu proxy atau tidak menggunakannya sama sekali, batas pada IP ini akan tercapai dengan cepat, sama seperti saat menjalankan secara berurutan. Oleh karena itu, jumlah pekerja harus disesuaikan dengan jumlah alamat IP yang tersedia dalam kumpulan proxy.

Untuk tugas dengan frekuensi permintaan tinggi ke satu domain, lebih baik menggunakan proxy pusat data — mereka memberikan latensi rendah dan cocok untuk pengumpulan data paralel berskala besar, di mana kecepatan lebih penting daripada menyamarkan diri sebagai pengguna biasa. Namun, jika tujuannya adalah untuk menghindari perlindungan yang lebih ketat seperti sistem anti-bot di pasar atau media sosial, diperlukan IP yang menyerupai pengguna biasa, yaitu yang bersifat residensial atau seluler.

Seledri: arsitektur dan rotasi proxy dalam kode

Seledri adalah antrean tugas yang paling matang dan populer dalam ekosistem Python. Ia bekerja di atas broker (biasanya Redis atau RabbitMQ) dan mendukung pengaturan kompetisi, percobaan ulang, dan prioritas tugas yang fleksibel. Untuk rotasi proxy, nyaman untuk menyimpan kumpulan IP di Redis dan menetapkan proxy untuk setiap tugas saat dimasukkan ke antrean.

from celery import Celery
import requests
import redis
import random

app = Celery('tasks', broker='redis://localhost:6379/0')
redis_client = redis.Redis(host='localhost', port=6379, db=1)

PROXY_POOL = [
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
]

def get_proxy():
    # Round-robin melalui Redis, untuk tidak membebani satu IP
    idx = redis_client.incr("proxy_cursor") % len(PROXY_POOL)
    return PROXY_POOL[idx]

@app.task(bind=True, max_retries=3, default_retry_delay=30)
def fetch_url(self, url):
    proxy = get_proxy()
    try:
        response = requests.get(
            url,
            proxies={"http": proxy, "https": proxy},
            timeout=15
        )
        if response.status_code == 429:
            raise self.retry(exc=Exception("Batas laju tercapai"))
        return response.status_code
    except requests.exceptions.RequestException as exc:
        raise self.retry(exc=exc)

Poin kunci adalah fungsi get_proxy(), yang melalui penghitung Redis memberikan proxy berikutnya dari kumpulan secara bergiliran. Ini menjamin bahwa beban didistribusikan secara merata di antara semua IP, dan tidak terfokus pada satu. Parameter max_retries dan penanganan kode 429 memungkinkan tugas untuk secara otomatis dikirim ulang dengan proxy yang berbeda jika IP saat ini terkena batasan.

Untuk mengontrol kompetisi di tingkat pekerja, Seledri memungkinkan untuk membatasi jumlah tugas yang berjalan bersamaan melalui parameter --concurrency saat menjalankan pekerja, serta melalui antrean dengan prioritas berbeda (misalnya, antrean terpisah untuk tugas lambat dengan batas laju yang ketat).

RQ (Redis Queue): alternatif sederhana

RQ adalah pustaka yang lebih ringan dibandingkan Seledri, tanpa broker RabbitMQ dan dengan konfigurasi minimal. Ini cocok jika proyek kecil dan tidak memerlukan pengaturan rute tugas yang rumit, tetapi hanya membutuhkan antrean dengan pekerja dan kemampuan untuk mengaitkan proxy dengan tugas.

from redis import Redis
from rq import Queue
import requests
import itertools

redis_conn = Redis()
q = Queue(connection=redis_conn)

PROXY_POOL = [
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
]
proxy_cycle = itertools.cycle(PROXY_POOL)

def fetch_with_proxy(url):
    proxy = next(proxy_cycle)
    response = requests.get(
        url,
        proxies={"http": proxy, "https": proxy},
        timeout=15
    )
    if response.status_code == 429:
        # Penempatan ulang dalam antrean dengan penundaan
        q.enqueue_in(60, fetch_with_proxy, url)
        return None
    return response.status_code

# Penempatan tugas dalam antrean
urls = ["https://example.com/page1", "https://example.com/page2"]
for url in urls:
    q.enqueue(fetch_with_proxy, url)

Di RQ, rotasi melalui itertools.cycle bekerja di dalam proses pekerja — ini lebih sederhana daripada penghitung Redis, tetapi kurang skalabel dengan banyak pekerja paralel, karena setiap pekerja memulai siklus dari awal. Untuk proyek kecil dengan 1-5 pekerja, ini sudah cukup, tetapi untuk yang lebih besar, lebih baik kembali ke penghitung terpusat di Redis, seperti dalam contoh Seledri.

RQ sangat cocok untuk pengembang yang sudah menggunakan Redis dalam proyek dan tidak ingin mengangkat broker pesan terpisah. Kekurangannya adalah lebih sedikit mekanisme bawaan untuk prioritas dan pemantauan dibandingkan dengan Seledri, sehingga untuk sistem produksi dengan ratusan ribu tugas per hari, biasanya lebih memilih Seledri.

BullMQ: antrean di Node.js dengan batas IP

BullMQ adalah standar untuk antrean tugas dalam ekosistem Node.js, dibangun di atas Redis dan memiliki pembatas laju bawaan, yang sangat nyaman untuk mengontrol batas permintaan pada IP. Alih-alih menghitung permintaan secara manual, Anda dapat menetapkan pembatas langsung di tingkat antrean.

const { Queue, Worker } = require('bullmq');
const axios = require('axios');

const connection = { host: 'localhost', port: 6379 };

const proxyPool = [
  'http://user:[email protected]:8000',
  'http://user:[email protected]:8000',
  'http://user:[email protected]:8000',
];

const queue = new Queue('fetchQueue', { connection });

// Pembatas: tidak lebih dari 5 tugas per detik pada antrean
const worker = new Worker('fetchQueue', async job => {
  const proxy = proxyPool[job.id % proxyPool.length];
  try {
    const response = await axios.get(job.data.url, {
      proxy: false,
      httpsAgent: new (require('https-proxy-agent'))(proxy),
      timeout: 15000,
    });
    return response.status;
  } catch (err) {
    if (err.response && err.response.status === 429) {
      throw new Error('Batas laju tercapai, mencoba lagi dengan proxy lain');
    }
    throw err;
  }
}, {
  connection,
  limiter: { max: 5, duration: 1000 },
  concurrency: proxyPool.length,
});

for (let i = 0; i < 100; i++) {
  queue.add('fetch', { url: `https://example.com/item/${i}` }, {
    attempts: 3,
    backoff: { type: 'exponential', delay: 5000 },
  });
}

Parameter limiter di BullMQ adalah mekanisme bawaan yang membatasi jumlah total tugas yang diproses dalam jangka waktu tertentu, yang nyaman untuk mematuhi batas permintaan di tingkat sistem secara keseluruhan. Dan parameter concurrency, yang sama dengan ukuran kumpulan proxy, menjamin bahwa jumlah pekerja yang berjalan bersamaan tepat sebanyak IP yang tersedia.

Pendekatan ini sering digunakan oleh tim yang menulis bot mereka sendiri untuk mengotomatiskan tindakan di TikTok atau Telegram — BullMQ terintegrasi dengan baik dengan tumpukan Node.js lainnya dan tidak memerlukan peralihan ke Python hanya untuk satu tugas.

Perbandingan Seledri, RQ, dan BullMQ

Parameter Seledri RQ BullMQ
Bahasa Python Python Node.js
Broker Redis / RabbitMQ Hanya Redis Hanya Redis
Pembatas laju bawaan Tidak ada (ditulis secara manual) Tidak ada Ya, bawaan
Ambang masuk Sedang Rendah Sedang
Cocok untuk Sistem produksi besar Proyek kecil, prototipe Tumpukan Node.js, bot, otomatisasi

Strategi distribusi beban berdasarkan IP

Round-robin, yang digunakan dalam contoh di atas, adalah strategi paling sederhana, tetapi tidak selalu optimal. Berikut beberapa opsi yang perlu dipertimbangkan tergantung pada tugas:

  • Round-robin — setiap permintaan berikutnya melalui proxy berikutnya secara bergiliran. Cocok ketika semua IP setara dan batasan pada mereka sama.
  • Sticky session — satu proxy terikat pada akun atau sesi tertentu selama bekerja dengannya. Kritis untuk media sosial dan pasar, di mana perubahan IP di tengah sesi terlihat mencurigakan.
  • Distribusi berbobot — sebagian permintaan diarahkan ke IP yang lebih mahal dan lebih andal (misalnya, seluler), sebagian ke yang murah dari pusat data, tergantung pada domain yang diproses.
  • Throttling adaptif — kecepatan pengiriman tugas secara otomatis dikurangi jika persentase kesalahan 429 atau captcha meningkat, dan dipulihkan ketika kesalahan hilang.

Untuk tugas di mana penyamaran sebagai pengguna nyata penting — misalnya, pemanasan dan pemeriksaan akun di Instagram atau TikTok melalui browser anti-detect seperti Dolphin Anty atau AdsPower — lebih baik menggunakan sticky session dengan proxy seluler: mereka lebih jarang dicurigai oleh sistem anti-bot dan berperilaku seperti pengguna biasa di internet seluler.

Untuk pengumpulan data massal dari situs web, di mana kecepatan dan volume lebih penting daripada kepercayaan algoritma, round-robin dengan proxy residensial memberikan keseimbangan antara stabilitas dan harga: IP terlihat seperti pengguna nyata, tetapi dapat sering diganti tanpa risiko untuk satu sesi tertentu.

Pemantauan kesalahan 429 dan larangan

Antrean tugas tanpa pemantauan adalah kotak hitam: sulit untuk memahami mengapa sebagian tugas gagal dan IP mana yang sudah dicurigai. Setidaknya, metrik yang perlu dipantau adalah:

  • Persentase respons dengan kode 429 dan 403 untuk setiap proxy secara terpisah
  • Waktu respons rata-rata — peningkatan latensi sering kali mendahului larangan
  • Jumlah percobaan ulang (retries) pada tugas
  • Panjang antrean tugas secara real-time — jika meningkat, pekerja tidak dapat menangani

Di Seledri, mudah untuk menggunakan Flower — antarmuka web untuk memantau tugas dan pekerja secara real-time. Di BullMQ, ada Bull Board dengan fungsi serupa. Untuk RQ, ada RQ Dashboard. Ketiga alat ini menunjukkan status tugas, jumlah percobaan yang gagal, dan memungkinkan untuk dengan cepat memahami proxy mana dari kumpulan yang harus sementara dikecualikan dari rotasi.

Praktik yang baik adalah secara otomatis menandai proxy sebagai "dalam karantina" selama 15-30 menit setelah beberapa kesalahan 429 berturut-turut dari IP ini, dan mengembalikannya ke kumpulan hanya setelah permintaan pengujian yang berhasil. Ini mencegah situasi di mana IP yang diblokir terus menerima tugas dan membuang waktu pekerja.

Kesimpulan

Antrean tugas menyelesaikan masalah penskalaan permintaan, tetapi tidak melindungi dari batasan pada IP — untuk itu diperlukan rotasi proxy yang dipikirkan dengan baik di dalam Seledri, RQ, atau BullMQ. Pemilihan alat tergantung pada tumpukan: proyek Python lebih sering menggunakan Seledri untuk produksi dan RQ untuk tugas sederhana, sementara pengembang Node.js memilih BullMQ dengan pembatas laju bawaannya.

Terlepas dari antrean yang dipilih, hasil sangat tergantung pada kualitas kumpulan proxy itu sendiri. Jika tugasnya adalah pengumpulan data massal yang cepat, perhatikan proxy pusat data untuk kecepatan, dan untuk bekerja dengan media sosial dan pasar, di mana loyalitas terhadap sistem anti-bot penting, gunakan IP residensial atau seluler — mereka mengurangi persentase kesalahan 429 dan larangan, yang secara langsung mengurangi jumlah percobaan ulang dan mempercepat penyelesaian seluruh antrean tugas.