Kembali ke blog

Load balancing melalui proxy: cara menskalakan parsing dan permintaan API hingga 10000+ RPS

Panduan lengkap tentang distribusi beban melalui proxy pool untuk pengambilan data marketplace, bekerja dengan API, dan otomatisasi: arsitektur, algoritma penyeimbangan, dan contoh praktis pengaturan.

📅7 Februari 2026
```html

Ketika Anda mem-parsing ribuan halaman marketplace, mengirimkan permintaan API massal, atau mengotomatiskan pekerjaan dengan ratusan akun, distribusi beban yang tepat melalui proxy menjadi sangat penting. Tanpa balancing yang tepat, Anda akan menghadapi pemblokiran, timeout, dan penurunan kinerja. Dalam panduan ini, kita akan membahas arsitektur distribusi beban, algoritma balancing, dan strategi praktis untuk sistem dengan beban tinggi.

Materi ini ditujukan untuk pengembang dan spesialis teknis yang bekerja dengan parsing data, otomatisasi permintaan API, atau mengelola pool proxy besar untuk tugas bisnis.

Mengapa perlu load balancing melalui proxy

Load balancing melalui proxy menyelesaikan beberapa masalah kritis dari sistem dengan beban tinggi. Masalah pertama dan terpenting adalah perlindungan terhadap pemblokiran. Ketika Anda mengirim ribuan permintaan ke satu sumber daya (marketplace, API media sosial, mesin pencari), server target melihat aktivitas yang tidak normal tinggi dari satu alamat IP dan memblokirnya. Distribusi permintaan di antara puluhan atau ratusan proxy membuat aktivitas Anda terlihat seperti tindakan pengguna biasa.

Masalah kedua adalah kinerja. Satu server proxy memiliki bandwidth terbatas (biasanya 100-1000 Mbps) dan dapat menangani jumlah koneksi simultan yang terbatas. Saat mem-parsing 10.000 halaman per menit atau mengirim permintaan API massal, satu proxy akan menjadi titik lemah dalam sistem. Load balancing memungkinkan penskalaan horizontal bandwidth dengan menambahkan proxy baru ke dalam pool.

Masalah ketiga adalah keandalan. Jika salah satu proxy mengalami kegagalan (gangguan teknis, pemblokiran, masa sewa habis), sistem secara otomatis mengalihkan lalu lintas ke proxy yang berfungsi. Tanpa mekanisme load balancing dan health checks, kegagalan satu proxy dapat menghentikan seluruh sistem.

Contoh nyata: Saat mem-parsing Wildberries untuk memantau harga pesaing, Anda perlu memproses 50.000 produk setiap jam. Itu sekitar 14 permintaan per detik. Satu proxy dapat menangani beban tersebut, tetapi Wildberries akan memblokir IP setelah 100-200 permintaan dari satu alamat. Dengan mendistribusikan permintaan di antara 20 proxy residensial, Anda mengurangi beban pada setiap IP menjadi 0,7 permintaan per detik — ini terlihat seperti aktivitas pengguna biasa.

Alasan keempat adalah distribusi geografis. Banyak layanan menunjukkan konten atau harga yang berbeda tergantung pada wilayah pengguna. Load balancing di antara proxy dari negara dan kota yang berbeda memungkinkan pengumpulan data secara bersamaan dari semua wilayah target. Misalnya, untuk memantau harga di Ozon, Anda memerlukan proxy dari Moskow, St. Petersburg, Yekaterinburg, dan kota besar lainnya.

Arsitektur sistem distribusi beban

Arsitektur klasik dari sistem load balancing melalui proxy terdiri dari beberapa komponen. Di tingkat atas terdapat load balancer — modul perangkat lunak yang menerima tugas masuk (permintaan untuk parsing, panggilan API) dan mendistribusikannya di antara proxy yang tersedia. Load balancer dapat berfungsi sebagai layanan terpisah atau terintegrasi dalam aplikasi.

Komponen kedua adalah manajer pool proxy. Ia menyimpan daftar semua proxy yang tersedia beserta karakteristiknya: alamat IP, port, protokol (HTTP/SOCKS5), lokasi geografis, jenis (residensial, seluler, pusat data), status saat ini (aktif, diblokir, dalam pemeriksaan). Manajer pool bertanggung jawab untuk menambahkan proxy baru, menghapus yang tidak berfungsi, dan memeriksa ketersediaan secara berkala.

Elemen ketiga adalah sistem pemantauan dan health checks. Sistem ini secara terus-menerus memeriksa kinerja setiap proxy: mengirimkan permintaan uji, mengukur waktu respons, memeriksa keberhasilan koneksi. Jika proxy tidak merespons atau mengembalikan kesalahan, sistem menandainya sebagai tidak tersedia dan sementara mengecualikannya dari rotasi.

Komponen Fungsi Teknologi
Load Balancer Distribusi permintaan di antara proxy HAProxy, Nginx, pustaka Python/Node.js
Proxy Pool Manager Manajemen daftar proxy Redis, PostgreSQL, penyimpanan in-memory
Health Check System Pemeriksaan ketersediaan proxy Tugas terjadwal, Celery, cron
Rate Limiter Kontrol frekuensi permintaan Token bucket, algoritma leaky bucket
Monitoring & Metrics Pengumpulan metrik kinerja Prometheus, Grafana, ELK Stack

Komponen keempat adalah rate limiter (pembatas frekuensi permintaan). Ia memastikan bahwa setiap proxy tidak melebihi frekuensi permintaan yang diizinkan ke sumber daya target. Misalnya, jika Anda mem-parsing Instagram dan tahu bahwa platform memblokir IP setelah 60 permintaan per menit, rate limiter tidak akan mengizinkan lebih dari 50 permintaan per menit melalui satu proxy, menyisakan cadangan keamanan.

Elemen kelima adalah sistem metrik dan analitik. Ia mengumpulkan data tentang kinerja setiap proxy: jumlah permintaan yang berhasil, jumlah kesalahan, waktu respons rata-rata, persentase pemblokiran. Data ini digunakan untuk mengoptimalkan algoritma balancing dan mengidentifikasi proxy yang bermasalah.

Algoritma balancing: Round Robin, Least Connections, Weighted

Algoritma Round Robin adalah metode balancing yang paling sederhana dan umum. Ia secara berurutan mengiterasi proxy dari daftar: permintaan pertama melalui proxy #1, yang kedua melalui proxy #2, yang ketiga melalui proxy #3, dan seterusnya. Ketika daftar selesai, load balancer kembali ke awal. Algoritma ini mendistribusikan beban secara merata jika semua proxy memiliki kinerja yang sama.

class RoundRobinBalancer:
    def __init__(self, proxies):
        self.proxies = proxies
        self.current_index = 0
    
    def get_next_proxy(self):
        proxy = self.proxies[self.current_index]
        self.current_index = (self.current_index + 1) % len(self.proxies)
        return proxy

# Penggunaan
proxies = [
    "http://proxy1.example.com:8080",
    "http://proxy2.example.com:8080",
    "http://proxy3.example.com:8080"
]

balancer = RoundRobinBalancer(proxies)
for i in range(10):
    proxy = balancer.get_next_proxy()
    print(f"Permintaan {i+1} → {proxy}")

Algoritma Least Connections (jumlah koneksi terkecil) memilih proxy dengan jumlah koneksi aktif minimum saat ini. Ini berguna ketika permintaan memiliki durasi eksekusi yang berbeda. Misalnya, saat mem-parsing, satu permintaan dapat diproses dalam 100 ms, sementara yang lain dalam 5 detik (jika halaman dimuat lambat). Least Connections secara otomatis mengarahkan permintaan baru ke proxy yang kurang terbebani.

class LeastConnectionsBalancer:
    def __init__(self, proxies):
        self.proxies = {proxy: 0 for proxy in proxies}
    
    def get_next_proxy(self):
        # Memilih proxy dengan jumlah koneksi minimum
        proxy = min(self.proxies.items(), key=lambda x: x[1])[0]
        self.proxies[proxy] += 1
        return proxy
    
    def release_proxy(self, proxy):
        # Mengurangi penghitung saat permintaan selesai
        self.proxies[proxy] -= 1

# Penggunaan dengan manajer konteks
balancer = LeastConnectionsBalancer(proxies)

def make_request(url):
    proxy = balancer.get_next_proxy()
    try:
        # Melakukan permintaan melalui proxy yang dipilih
        response = requests.get(url, proxies={"http": proxy})
        return response
    finally:
        balancer.release_proxy(proxy)

Weighted Round Robin (round robin berbobot) memperluas Round Robin klasik dengan memberikan bobot pada setiap proxy berdasarkan kinerja atau kualitasnya. Proxy dengan bobot lebih besar menerima lebih banyak permintaan. Ini berguna ketika Anda memiliki proxy dengan kualitas yang berbeda: misalnya, proxy residensial premium dengan kecepatan tinggi dan proxy pusat data murah dengan batasan.

class WeightedRoundRobinBalancer:
    def __init__(self, weighted_proxies):
        # weighted_proxies = [(proxy, weight), ...]
        self.proxies = []
        for proxy, weight in weighted_proxies:
            # Menambahkan proxy ke dalam daftar sebanyak weight kali
            self.proxies.extend([proxy] * weight)
        self.current_index = 0
    
    def get_next_proxy(self):
        proxy = self.proxies[self.current_index]
        self.current_index = (self.current_index + 1) % len(self.proxies)
        return proxy

# Penggunaan dengan bobot
weighted_proxies = [
    ("http://premium-proxy1.com:8080", 5),  # kualitas tinggi
    ("http://premium-proxy2.com:8080", 5),
    ("http://cheap-proxy1.com:8080", 2),    # kualitas rendah
    ("http://cheap-proxy2.com:8080", 1)     # kualitas sangat rendah
]

balancer = WeightedRoundRobinBalancer(weighted_proxies)

Algoritma Random (pemilihan acak) memilih proxy secara acak dari daftar yang tersedia. Ini lebih sederhana dalam implementasi dibandingkan Round Robin, dan bekerja dengan baik pada jumlah proxy yang besar (100+). Distribusi acak secara otomatis seimbang dengan volume permintaan yang cukup. Kekurangan — mungkin ada periode singkat dengan beban yang tidak merata.

IP Hash — algoritma yang memilih proxy berdasarkan hash dari URL target atau parameter lain. Ini menjamin bahwa permintaan ke sumber daya yang sama selalu melalui proxy yang sama. Berguna untuk situs yang menggunakan sesi atau memerlukan urutan permintaan dari satu IP (misalnya, otorisasi + pengambilan data).

Manajemen pool proxy: rotasi dan health checks

Manajemen pool proxy yang efektif memerlukan pemantauan konstan terhadap status setiap proxy dan rotasi otomatis. Health check adalah pemeriksaan berkala terhadap ketersediaan proxy dengan mengirimkan permintaan uji. Biasanya menggunakan permintaan GET sederhana ke layanan yang dapat diandalkan (misalnya, httpbin.org atau endpoint Anda sendiri), yang mengembalikan informasi tentang alamat IP.

import requests
import time
from datetime import datetime

class ProxyHealthChecker:
    def __init__(self, test_url="http://httpbin.org/ip", timeout=10):
        self.test_url = test_url
        self.timeout = timeout
    
    def check_proxy(self, proxy_url):
        """Memeriksa kinerja proxy"""
        try:
            start_time = time.time()
            response = requests.get(
                self.test_url,
                proxies={"http": proxy_url, "https": proxy_url},
                timeout=self.timeout
            )
            response_time = time.time() - start_time
            
            if response.status_code == 200:
                return {
                    "status": "healthy",
                    "response_time": response_time,
                    "timestamp": datetime.now(),
                    "ip": response.json().get("origin")
                }
            else:
                return {
                    "status": "unhealthy",
                    "error": f"HTTP {response.status_code}",
                    "timestamp": datetime.now()
                }
        except requests.exceptions.Timeout:
            return {
                "status": "unhealthy",
                "error": "timeout",
                "timestamp": datetime.now()
            }
        except Exception as e:
            return {
                "status": "unhealthy",
                "error": str(e),
                "timestamp": datetime.now()
            }

# Penggunaan
checker = ProxyHealthChecker()
proxies = ["http://proxy1.com:8080", "http://proxy2.com:8080"]

for proxy in proxies:
    result = checker.check_proxy(proxy)
    print(f"{proxy}: {result['status']} ({result.get('response_time', 'N/A')}s)")

Sistem manajemen pool harus secara otomatis menandai proxy yang tidak berfungsi sebagai tidak tersedia dan memeriksa kembali secara berkala. Strategi yang umum digunakan adalah pola circuit breaker: setelah tiga pemeriksaan gagal berturut-turut, proxy dikeluarkan dari pool selama 5-10 menit, kemudian diperiksa kembali. Jika pemeriksaan berhasil, proxy kembali ke pool aktif.

class ProxyPoolManager:
    def __init__(self, health_checker, max_failures=3, cooldown_seconds=300):
        self.health_checker = health_checker
        self.max_failures = max_failures
        self.cooldown_seconds = cooldown_seconds
        
        self.proxies = {}  # {proxy_url: ProxyInfo}
    
    def add_proxy(self, proxy_url, metadata=None):
        """Menambahkan proxy ke dalam pool"""
        self.proxies[proxy_url] = {
            "url": proxy_url,
            "status": "active",
            "failures": 0,
            "last_check": None,
            "cooldown_until": None,
            "metadata": metadata or {}
        }
    
    def get_active_proxies(self):
        """Mengembalikan daftar proxy yang aktif"""
        now = datetime.now()
        active = []
        
        for proxy_url, info in self.proxies.items():
            # Memeriksa apakah proxy dalam cooldown
            if info["cooldown_until"] and now < info["cooldown_until"]:
                continue
            
            if info["status"] == "active":
                active.append(proxy_url)
        
        return active
    
    def mark_failure(self, proxy_url):
        """Menandai kegagalan penggunaan proxy"""
        if proxy_url not in self.proxies:
            return
        
        info = self.proxies[proxy_url]
        info["failures"] += 1
        
        if info["failures"] >= self.max_failures:
            # Memindahkan ke cooldown
            info["status"] = "cooldown"
            info["cooldown_until"] = datetime.now() + timedelta(seconds=self.cooldown_seconds)
            print(f"Proxy {proxy_url} dipindahkan ke cooldown hingga {info['cooldown_until']}")
    
    def mark_success(self, proxy_url):
        """Menandai keberhasilan penggunaan proxy"""
        if proxy_url not in self.proxies:
            return
        
        info = self.proxies[proxy_url]
        info["failures"] = 0
        info["status"] = "active"
        info["cooldown_until"] = None

Rotasi proxy adalah strategi untuk secara otomatis mengganti proxy dalam interval tertentu atau setelah jumlah permintaan tertentu. Ada beberapa pendekatan: rotasi berdasarkan waktu (ganti setiap 5-10 menit), rotasi berdasarkan jumlah permintaan (ganti setelah 100-500 permintaan), rotasi berdasarkan sesi (satu proxy untuk satu sesi parsing). Pemilihan strategi tergantung pada kebutuhan situs target.

Tips: Untuk mem-parsing marketplace (Wildberries, Ozon) rotasi berdasarkan jumlah permintaan paling optimal: 50-100 permintaan per proxy, lalu ganti. Untuk bekerja dengan API media sosial (Instagram, Facebook) lebih baik menggunakan rotasi berdasarkan sesi: satu proxy untuk seluruh siklus otorisasi → tindakan → keluar.

Rate limiting dan kontrol frekuensi permintaan

Rate limiting adalah komponen yang sangat penting dari sistem load balancing. Ini mencegah melebihi frekuensi permintaan yang diizinkan ke sumber daya target, yang dapat menyebabkan pemblokiran proxy. Ada dua algoritma utama: Token Bucket (ember token) dan Leaky Bucket (ember bocor).

Token Bucket bekerja sebagai berikut: setiap proxy memiliki "ember" virtual dengan token. Setiap token memberikan hak untuk satu permintaan. Ember secara bertahap diisi dengan token pada kecepatan tertentu (misalnya, 10 token per detik). Kapasitas maksimum ember terbatas (misalnya, 50 token). Ketika permintaan datang, sistem memeriksa ketersediaan token: jika ada — mengambil satu token dan melakukan permintaan, jika tidak — menunggu munculnya token baru.

import time
from threading import Lock

class TokenBucketRateLimiter:
    def __init__(self, rate, capacity):
        """
        rate: jumlah token per detik
        capacity: jumlah maksimum token dalam ember
        """
        self.rate = rate
        self.capacity = capacity
        self.tokens = capacity
        self.last_update = time.time()
        self.lock = Lock()
    
    def _add_tokens(self):
        """Menambahkan token berdasarkan waktu yang berlalu"""
        now = time.time()
        elapsed = now - self.last_update
        new_tokens = elapsed * self.rate
        
        self.tokens = min(self.capacity, self.tokens + new_tokens)
        self.last_update = now
    
    def acquire(self, tokens=1):
        """Mencoba mendapatkan jumlah token yang ditentukan"""
        with self.lock:
            self._add_tokens()
            
            if self.tokens >= tokens:
                self.tokens -= tokens
                return True
            return False
    
    def wait_and_acquire(self, tokens=1):
        """Menunggu munculnya token dan mendapatkannya"""
        while not self.acquire(tokens):
            # Menghitung waktu tunggu
            wait_time = (tokens - self.tokens) / self.rate
            time.sleep(wait_time)

# Penggunaan untuk setiap proxy
proxy_limiters = {
    "http://proxy1.com:8080": TokenBucketRateLimiter(rate=10, capacity=50),
    "http://proxy2.com:8080": TokenBucketRateLimiter(rate=10, capacity=50)
}

def make_request_with_limit(url, proxy_url):
    limiter = proxy_limiters[proxy_url]
    limiter.wait_and_acquire()  # Menunggu ketersediaan token
    
    response = requests.get(url, proxies={"http": proxy_url})
    return response

Leaky Bucket (ember bocor) bekerja dengan cara yang berbeda: permintaan ditempatkan dalam antrean (ember), yang "mengalir" dengan kecepatan tetap. Jika ember meluap, permintaan baru ditolak atau ditunda. Algoritma ini memberikan distribusi beban yang lebih merata seiring waktu, tetapi dapat menyebabkan penundaan saat ada lonjakan aktivitas.

Untuk berbagai platform target, diperlukan batas yang berbeda. API Instagram mengizinkan sekitar 200 permintaan per jam untuk satu IP (sekitar 3,3 permintaan per menit). Wildberries memblokir setelah 100-200 permintaan per menit dari satu IP. Google Search mengizinkan 10-20 permintaan per menit. Sistem rate limiting Anda harus mempertimbangkan batasan ini dan menambahkan cadangan keamanan (biasanya 20-30%).

Platform Batas permintaan Pengaturan yang disarankan
Instagram ~200 permintaan/jam 2-3 permintaan/menit (dengan cadangan)
Wildberries 100-200 permintaan/menit 60-80 permintaan/menit
Google Search 10-20 permintaan/menit 8-12 permintaan/menit
Ozon 50-100 permintaan/menit 30-50 permintaan/menit
Facebook API 200 permintaan/jam 2-3 permintaan/menit

Pemantauan kinerja dan penskalaan otomatis

Sistem load balancing yang efektif memerlukan pemantauan konstan terhadap metrik kunci. Grup pertama metrik adalah kinerja proxy: waktu respons rata-rata, persentase permintaan yang berhasil, jumlah timeout, jumlah kesalahan 4xx dan 5xx. Data ini memungkinkan untuk mengidentifikasi proxy yang bermasalah dan mengoptimalkan algoritma balancing.

class ProxyMetrics:
    def __init__(self):
        self.metrics = {}  # {proxy_url: metrics_dict}
    
    def record_request(self, proxy_url, response_time, status_code, success):
        """Mencatat metrik permintaan"""
        if proxy_url not in self.metrics:
            self.metrics[proxy_url] = {
                "total_requests": 0,
                "successful_requests": 0,
                "failed_requests": 0,
                "total_response_time": 0,
                "timeouts": 0,
                "errors_4xx": 0,
                "errors_5xx": 0
            }
        
        m = self.metrics[proxy_url]
        m["total_requests"] += 1
        
        if success:
            m["successful_requests"] += 1
            m["total_response_time"] += response_time
        else:
            m["failed_requests"] += 1
            
            if status_code == 0:  # timeout
                m["timeouts"] += 1
            elif 400 <= status_code < 500:
                m["errors_4xx"] += 1
            elif 500 <= status_code < 600:
                m["errors_5xx"] += 1
    
    def get_success_rate(self, proxy_url):
        """Mengembalikan persentase permintaan yang berhasil"""
        m = self.metrics.get(proxy_url, {})
        total = m.get("total_requests", 0)
        if total == 0:
            return 0
        return (m.get("successful_requests", 0) / total) * 100
    
    def get_avg_response_time(self, proxy_url):
        """Mengembalikan waktu respons rata-rata"""
        m = self.metrics.get(proxy_url, {})
        successful = m.get("successful_requests", 0)
        if successful == 0:
            return 0
        return m.get("total_response_time", 0) / successful
    
    def get_report(self, proxy_url):
        """Mengembalikan laporan lengkap untuk proxy"""
        m = self.metrics.get(proxy_url, {})
        return {
            "proxy": proxy_url,
            "total_requests": m.get("total_requests", 0),
            "success_rate": self.get_success_rate(proxy_url),
            "avg_response_time": self.get_avg_response_time(proxy_url),
            "timeouts": m.get("timeouts", 0),
            "errors_4xx": m.get("errors_4xx", 0),
            "errors_5xx": m.get("errors_5xx", 0)
        }

Grup kedua metrik adalah kinerja keseluruhan sistem: total permintaan per detik (RPS — requests per second), latensi rata-rata, ukuran antrean permintaan, jumlah proxy aktif, persentase penggunaan pool proxy. Metrik ini menunjukkan apakah sistem dapat menangani beban atau memerlukan penskalaan.

Penskalaan otomatis (auto-scaling) memungkinkan penambahan atau penghapusan proxy secara dinamis tergantung pada beban. Strategi sederhana: jika rata-rata beban pool melebihi 80% selama 5 menit, sistem secara otomatis menambahkan proxy baru. Jika beban turun di bawah 30% selama 15 menit, sistem menghapus proxy yang berlebihan untuk menghemat sumber daya.

Contoh pengaturan pemantauan: Untuk mem-parsing 100.000 produk Wildberries per jam (sekitar 28 permintaan per detik), Anda memerlukan setidaknya 30-40 proxy dengan batas 60 permintaan per menit per proxy. Atur alert: jika tingkat keberhasilan turun di bawah 85% atau waktu respons rata-rata melebihi 3 detik, sistem harus secara otomatis menambahkan 10-15 proxy cadangan dari pool.

Untuk visualisasi metrik, gunakan Grafana dengan Prometheus atau ELK Stack. Buat dashboard dengan grafik: RPS dari waktu ke waktu, distribusi waktu respons, 10 proxy tercepat/paling lambat, peta kesalahan berdasarkan jenis. Ini akan memungkinkan Anda untuk dengan cepat mengidentifikasi masalah dan mengoptimalkan sistem.

Implementasi praktis di Python dan Node.js

Mari kita lihat implementasi lengkap dari sistem load balancing di Python menggunakan pustaka populer. Contoh ini menggabungkan semua komponen yang dijelaskan: load balancer, manajer pool, health checks, rate limiting, dan pemantauan.

import requests
import time
import random
from datetime import datetime, timedelta
from threading import Lock, Thread
from collections import defaultdict

class ProxyLoadBalancer:
    def __init__(self, proxies, algorithm="round_robin", rate_limit=10):
        """
        proxies: daftar proxy [{"url": "...", "weight": 1}, ...]
        algorithm: round_robin, least_connections, weighted, random
        rate_limit: jumlah maksimum permintaan per detik pada proxy
        """
        self.proxies = proxies
        self.algorithm = algorithm
        self.rate_limit = rate_limit
        
        # Status load balancer
        self.current_index = 0
        self.connections = defaultdict(int)
        self.rate_limiters = {}
        self.metrics = defaultdict(lambda: {
            "total": 0, "success": 0, "failed": 0,
            "response_times": [], "last_check": None
        })
        
        # Inisialisasi rate limiters
        for proxy in proxies:
            proxy_url = proxy["url"]
            self.rate_limiters[proxy_url] = TokenBucketRateLimiter(
                rate=rate_limit,
                capacity=rate_limit * 5
            )
        
        self.lock = Lock()
        
        # Menjalankan health check di latar belakang
        self.health_check_thread = Thread(target=self._health_check_loop, daemon=True)
        self.health_check_thread.start()
    
    def get_next_proxy(self):
        """Memilih proxy berikutnya sesuai algoritma"""
        with self.lock:
            if self.algorithm == "round_robin":
                return self._round_robin()
            elif self.algorithm == "least_connections":
                return self._least_connections()
            elif self.algorithm == "weighted":
                return self._weighted_random()
            elif self.algorithm == "random":
                return random.choice(self.proxies)["url"]
    
    def _round_robin(self):
        proxy = self.proxies[self.current_index]["url"]
        self.current_index = (self.current_index + 1) % len(self.proxies)
        return proxy
    
    def _least_connections(self):
        min_conn = min(self.connections.values()) if self.connections else 0
        candidates = [p["url"] for p in self.proxies if self.connections[p["url"]] == min_conn]
        return random.choice(candidates)
    
    def _weighted_random(self):
        weights = [p.get("weight", 1) for p in self.proxies]
        return random.choices(self.proxies, weights=weights)[0]["url"]
    
    def make_request(self, url, method="GET", **kwargs):
        """Melakukan permintaan melalui load balancer"""
        proxy_url = self.get_next_proxy()
        
        # Menunggu ketersediaan rate limit
        self.rate_limiters[proxy_url].wait_and_acquire()
        
        # Meningkatkan penghitung koneksi
        with self.lock:
            self.connections[proxy_url] += 1
        
        try:
            start_time = time.time()
            response = requests.request(
                method,
                url,
                proxies={"http": proxy_url, "https": proxy_url},
                timeout=kwargs.get("timeout", 30),
                **kwargs
            )
            response_time = time.time() - start_time
            
            # Mencatat metrik
            self._record_metrics(proxy_url, response_time, True, response.status_code)
            
            return response
        
        except Exception as e:
            self._record_metrics(proxy_url, 0, False, 0)
            raise
        
        finally:
            # Mengurangi penghitung koneksi
            with self.lock:
                self.connections[proxy_url] -= 1
    
    def _record_metrics(self, proxy_url, response_time, success, status_code):
        """Mencatat metrik permintaan"""
        with self.lock:
            m = self.metrics[proxy_url]
            m["total"] += 1
            if success:
                m["success"] += 1
                m["response_times"].append(response_time)
                # Menyimpan hanya 1000 nilai terakhir
                if len(m["response_times"]) > 1000:
                    m["response_times"].pop(0)
            else:
                m["failed"] += 1
    
    def _health_check_loop(self):
        """Pemeriksaan kesehatan proxy di latar belakang"""
        while True:
            for proxy in self.proxies:
                proxy_url = proxy["url"]
                try:
                    response = requests.get(
                        "http://httpbin.org/ip",
                        proxies={"http": proxy_url},
                        timeout=10
                    )
                    with self.lock:
                        self.metrics[proxy_url]["last_check"] = datetime.now()
                        proxy["status"] = "healthy" if response.status_code == 200 else "unhealthy"
                except:
                    with self.lock:
                        proxy["status"] = "unhealthy"
            
            time.sleep(60)  # Pemeriksaan setiap menit
    
    def get_stats(self):
        """Mengembalikan statistik untuk semua proxy"""
        stats = []
        with self.lock:
            for proxy in self.proxies:
                proxy_url = proxy["url"]
                m = self.metrics[proxy_url]
                
                avg_response_time = (
                    sum(m["response_times"]) / len(m["response_times"])
                    if m["response_times"] else 0
                )
                
                success_rate = (
                    (m["success"] / m["total"] * 100)
                    if m["total"] > 0 else 0
                )
                
                stats.append({
                    "proxy": proxy_url,
                    "status": proxy.get("status", "unknown"),
                    "total_requests": m["total"],
                    "success_rate": round(success_rate, 2),
                    "avg_response_time": round(avg_response_time, 3),
                    "active_connections": self.connections[proxy_url]
                })
        
        return stats

Menggunakan load balancer ini untuk mem-parsing marketplace:

# Pengaturan load balancer
proxies = [
    {"url": "http://proxy1.example.com:8080", "weight": 5},
    {"url": "http://proxy2.example.com:8080", "weight": 5},
    {"url": "http://proxy3.example.com:8080", "weight": 3},
    {"url": "http://proxy4.example.com:8080", "weight": 2}
]

balancer = ProxyLoadBalancer(
    proxies=proxies,
    algorithm="weighted",
    rate_limit=60  # 60 permintaan per menit pada proxy
)

# Mem-parsing daftar produk
product_urls = [f"https://www.wildberries.ru/catalog/{i}/detail.aspx" for i in range(1000)]

results = []
for url in product_urls:
    try:
        response = balancer.make_request(url)
        # Memproses respons
        results.append({"url": url, "status": "success", "data": response.text})
    except Exception as e:
        results.append({"url": url, "status": "error", "error": str(e)})
    
    # Setiap 100 permintaan, tampilkan statistik
    if len(results) % 100 == 0:
        stats = balancer.get_stats()
        for stat in stats:
            print(f"{stat['proxy']}: {stat['success_rate']}% sukses, "
                  f"{stat['avg_response_time']}s rata-rata respons")

# Statistik akhir
print("\n=== Statistik Akhir ===")
for stat in balancer.get_stats():
    print(f"{stat['proxy']}:")
    print(f"  Total permintaan: {stat['total_requests']}")
    print(f"  Tingkat keberhasilan: {stat['success_rate']}%")
    print(f"  Waktu respons rata-rata: {stat['avg_response_time']}s")
    print(f"  Status: {stat['status']}")

Untuk Node.js, Anda dapat menggunakan arsitektur serupa dengan pustaka axios untuk permintaan HTTP dan node-rate-limiter untuk kontrol frekuensi:

const axios = require('axios');
const { RateLimiter } = require('limiter');

class ProxyLoadBalancer {
  constructor(proxies, algorithm = 'round_robin', rateLimit = 10) {
    this.proxies = proxies;
    this.algorithm = algorithm;
    this.currentIndex = 0;
    this.connections = new Map();
    this.limiters = new Map();
    this.metrics = new Map();
    
    // Inisialisasi rate limiters
    proxies.forEach(proxy => {
      this.limiters.set(proxy.url, new RateLimiter({ 
        tokensPerInterval: rateLimit, 
        interval: 'second' 
      }));
      this.connections.set(proxy.url, 0);
      this.metrics.set(proxy.url, {
        total: 0,
        success: 0,
        failed: 0,
        responseTimes: []
      });
    });
  }
  
  getNextProxy() {
    if (this.algorithm === 'round_robin') {
      const proxy = this.proxies[this.currentIndex].url;
      this.currentIndex = (this.currentIndex + 1) % this.proxies.length;
      return proxy;
    } else if (this.algorithm === 'least_connections') {
      let minConn = Infinity;
      let selectedProxy = null;
      
      this.connections.forEach((count, proxy) => {
        if (count < minConn) {
          minConn = count;
          selectedProxy = proxy;
        }
      });
      
      return selectedProxy;
    }
  }
  
  async makeRequest(url, options = {}) {
    const proxyUrl = this.getNextProxy();
    const limiter = this.limiters.get(proxyUrl);
    
    // Menunggu ketersediaan rate limit
    await limiter.removeTokens(1);
    
    // Meningkatkan penghitung koneksi
    this.connections.set(proxyUrl, this.connections.get(proxyUrl) + 1);
    
    try {
      const startTime = Date.now();
      const response = await axios({
        url,
        proxy: this.parseProxyUrl(proxyUrl),
        timeout: options.timeout || 30000,
        ...options
      });
      
      const responseTime = (Date.now() - startTime) / 1000;
      this.recordMetrics(proxyUrl, responseTime, true);
      
      return response;
    } catch (error) {
      this.recordMetrics(proxyUrl, 0, false);
      throw error;
    } finally {
      this.connections.set(proxyUrl, this.connections.get(proxyUrl) - 1);
    }
  }
  
  parseProxyUrl(proxyUrl) {
    const url = new URL(proxyUrl);
    return {
      host: url.hostname,
      port: parseInt(url.port)
    };
  }
  
  recordMetrics(proxyUrl, responseTime, success) {
    const m = this.metrics.get(proxyUrl);
    m.total++;
    
    if (success) {
      m.success++;
      m.responseTimes.push(responseTime);
      if (m.responseTimes.length > 1000) {
        m.responseTimes.shift();
      }
    } else {
      m.failed++;
    }
  }
  
  getStats() {
    const stats = [];
    
    this.proxies.forEach(proxy => {
      const m = this.metrics.get(proxy.url);
      const avgResponseTime = m.responseTimes.length > 0
        ? m.responseTimes.reduce((a, b) => a + b, 0) / m.responseTimes.length
        : 0;
      
      const successRate = m.total > 0 ? (m.success / m.total * 100) : 0;
      
      stats.push({
        proxy: proxy.url,
        totalRequests: m.total,
        successRate: successRate.toFixed(2),
        avgResponseTime: avgResponseTime.toFixed(3),
        activeConnections: this.connections.get(proxy.url)
      });
    });
    
    return stats;
  }
}

// Penggunaan
const proxies = [
  { url: 'http://proxy1.example.com:8080', weight: 5 },
  { url: 'http://proxy2.example.com:8080', weight: 5 }
];

const balancer = new ProxyLoadBalancer(proxies, 'round_robin', 60);

async function parseProducts() {
  const urls = Array.from({ length: 1000 }, (_, i) => 
    `https://www.wildberries.ru/catalog/${i}/detail.aspx`
  );
  
  for (const url of urls) {
    try {
      const response = await balancer.makeRequest(url);
      console.log(`Sukses: ${url}`);
    } catch (error) {
      console.error(`Kesalahan: ${url} - ${error.message}`);
    }
  }
  
  console.log('\n=== Statistik ===');
  console.log(balancer.getStats());
}

parseProducts();

Optimasi untuk tugas tertentu: parsing, API, otomatisasi

Tugas yang berbeda memerlukan strategi load balancing yang berbeda. Untuk parsing marketplace (Wildberries, Ozon, Avito), strategi rotasi berdasarkan jumlah permintaan dan distribusi geografis paling optimal. Gunakan proxy residensial dari berbagai kota di Rusia, ganti proxy setiap 50-100 permintaan, tambahkan jeda acak antara permintaan (1-3 detik) untuk meniru perilaku manusia.

Untuk bekerja dengan API media sosial (Instagram, Facebook, VK), stabilitas alamat IP dalam satu sesi sangat penting. Gunakan algoritma IP Hash atau sesi lengket: semua permintaan dari satu akun harus melalui proxy yang sama. Ini mencegah perubahan geolokasi yang mencurigakan yang dapat menyebabkan pemblokiran akun. Disarankan untuk menggunakan proxy seluler, karena media sosial lebih mempercayai IP seluler dibandingkan dengan residensial atau pusat data.

# Contoh sesi lengket untuk Instagram
class StickySessionBalancer:
    def __init__(self, proxies):
        self.proxies = proxies
        self.session_map = {}  # {account_id: proxy_url}
        self.proxy_usage = defaultdict(int)
    
    def get_proxy_for_account(self, account_id):
        """Mengembalikan proxy tetap untuk akun"""
        if account_id in self.session_map:
            return self.session_map[account_id]
        
        # Memilih proxy yang paling sedikit terbebani
        proxy = min(self.proxies, key=lambda p: self.proxy_usage[p])
        self.session_map[account_id] = proxy
        self.proxy_usage[proxy] += 1
        
        return proxy
    
    def release_account(self, account_id):
        """Melepaskan proxy setelah selesai menggunakan akun"""
        if account_id in self.session_map:
            proxy = self.session_map[account_id]
            self.proxy_usage[proxy] -= 1
            del self.session_map[account_id]
```