Kembali ke blog

Cara Memperbaiki Timeout Error Saat Menggunakan Proxy

Kesalahan timeout melalui proxy — masalah umum saat parsing dan otomasi. Kami menganalisis penyebabnya dan memberikan solusi yang terbukti dengan contoh kode.

📅15 Desember 2025

Cara Memperbaiki Kesalahan Timeout Saat Bekerja Melalui Proxy

Permintaan macet, skrip jatuh dengan kesalahan TimeoutError, data tidak diterima. Situasi yang familiar? Kesalahan timeout melalui proxy — salah satu masalah paling umum saat parsing dan otomasi. Mari kita analisis penyebabnya dan berikan solusi konkret.

Mengapa Kesalahan Timeout Terjadi

Timeout — bukan satu masalah, tetapi gejala. Sebelum mengobati, kita perlu memahami penyebabnya:

Server proxy yang lambat. Server yang kelebihan beban atau proxy yang jauh secara geografis menambah penundaan pada setiap permintaan. Jika timeout Anda 10 detik, tetapi proxy merespons dalam 12 detik — terjadi kesalahan.

Pemblokiran di sisi situs target. Situs dapat dengan sengaja "menggantung" permintaan yang mencurigakan alih-alih menolak secara eksplisit. Ini adalah taktik melawan bot — membiarkan koneksi terbuka tanpa batas.

Masalah DNS. Proxy harus menyelesaikan domain. Jika server DNS proxy lambat atau tidak dapat diakses — permintaan macet pada tahap koneksi.

Konfigurasi timeout yang salah. Satu timeout umum untuk semuanya — kesalahan umum. Connect timeout dan read timeout — hal yang berbeda, dan harus dikonfigurasi secara terpisah.

Masalah jaringan. Kehilangan paket, koneksi proxy yang tidak stabil, masalah perutean — semuanya menyebabkan timeout.

Jenis Timeout dan Konfigurasinya

Sebagian besar perpustakaan HTTP mendukung beberapa jenis timeout. Memahami perbedaan di antara mereka — kunci untuk konfigurasi yang benar.

Connect timeout

Waktu untuk membangun koneksi TCP dengan proxy dan server target. Jika proxy tidak dapat diakses atau server tidak merespons — timeout ini akan terpicu. Nilai yang direkomendasikan: 5-10 detik.

Read timeout

Waktu menunggu data setelah koneksi dibangun. Server terhubung, tetapi diam — read timeout akan terpicu. Untuk halaman biasa: 15-30 detik. Untuk API berat: 60+ detik.

Total timeout

Waktu total untuk seluruh permintaan dari awal hingga akhir. Perlindungan dari koneksi yang macet. Biasanya: connect + read + cadangan.

Contoh konfigurasi di Python dengan perpustakaan requests:

import requests

proxies = {
    "http": "http://user:pass@proxy.example.com:8080",
    "https": "http://user:pass@proxy.example.com:8080"
}

# Tuple: (connect_timeout, read_timeout)
timeout = (10, 30)

try:
    response = requests.get(
        "https://target-site.com/api/data",
        proxies=proxies,
        timeout=timeout
    )
except requests.exceptions.ConnectTimeout:
    print("Gagal terhubung ke proxy atau server")
except requests.exceptions.ReadTimeout:
    print("Server tidak mengirim data tepat waktu")

Untuk aiohttp (Python asinkron):

import aiohttp
import asyncio

async def fetch_with_timeout():
    timeout = aiohttp.ClientTimeout(
        total=60,      # Total timeout
        connect=10,    # Untuk koneksi
        sock_read=30   # Untuk membaca data
    )
    
    async with aiohttp.ClientSession(timeout=timeout) as session:
        async with session.get(
            "https://target-site.com/api/data",
            proxy="http://user:pass@proxy.example.com:8080"
        ) as response:
            return await response.text()

Logika Retry: Pendekatan yang Benar

Timeout — tidak selalu kesalahan fatal. Sering kali permintaan berulang berhasil. Tetapi retry harus dilakukan dengan bijak.

Penundaan Eksponensial

Jangan terus-menerus mengirim permintaan berulang ke server tanpa jeda. Gunakan exponential backoff: setiap percobaan berikutnya — dengan penundaan yang meningkat.

import requests
import time
import random

def fetch_with_retry(url, proxies, max_retries=3):
    """Permintaan dengan retry dan penundaan eksponensial"""
    
    for attempt in range(max_retries):
        try:
            response = requests.get(
                url,
                proxies=proxies,
                timeout=(10, 30)
            )
            response.raise_for_status()
            return response
            
        except (requests.exceptions.Timeout, 
                requests.exceptions.ConnectionError) as e:
            
            if attempt == max_retries - 1:
                raise  # Percobaan terakhir — lempar kesalahan
            
            # Penundaan eksponensial: 1s, 2s, 4s...
            # + jitter acak untuk menghindari gelombang permintaan
            delay = (2 ** attempt) + random.uniform(0, 1)
            print(f"Percobaan {attempt + 1} gagal: {e}")
            print(f"Coba lagi dalam {delay:.1f} detik...")
            time.sleep(delay)

Perpustakaan tenacity

Untuk kode production, lebih nyaman menggunakan solusi siap pakai:

from tenacity import retry, stop_after_attempt, wait_exponential
from tenacity import retry_if_exception_type
import requests

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=10),
    retry=retry_if_exception_type((
        requests.exceptions.Timeout,
        requests.exceptions.ConnectionError
    ))
)
def fetch_data(url, proxies):
    response = requests.get(url, proxies=proxies, timeout=(10, 30))
    response.raise_for_status()
    return response.json()

Rotasi Proxy Saat Timeout

Jika satu proxy terus memberikan timeout — masalahnya ada pada proxy tersebut. Solusi logis: beralih ke yang lain.

import requests
from collections import deque
from dataclasses import dataclass, field
from typing import Optional
import time

@dataclass
class ProxyManager:
    """Manajer proxy dengan pelacakan upaya yang gagal"""
    
    proxies: list
    max_failures: int = 3
    cooldown_seconds: int = 300
    _failures: dict = field(default_factory=dict)
    _cooldown_until: dict = field(default_factory=dict)
    
    def get_proxy(self) -> Optional[str]:
        """Dapatkan proxy yang berfungsi"""
        current_time = time.time()
        
        for proxy in self.proxies:
            # Lewati proxy dalam cooldown
            if self._cooldown_until.get(proxy, 0) > current_time:
                continue
            return proxy
        
        return None  # Semua proxy dalam cooldown
    
    def report_failure(self, proxy: str):
        """Laporkan permintaan yang gagal"""
        self._failures[proxy] = self._failures.get(proxy, 0) + 1
        
        if self._failures[proxy] >= self.max_failures:
            # Masukkan proxy ke cooldown
            self._cooldown_until[proxy] = time.time() + self.cooldown_seconds
            self._failures[proxy] = 0
            print(f"Proxy {proxy} dimasukkan ke cooldown")
    
    def report_success(self, proxy: str):
        """Reset penghitung kesalahan saat berhasil"""
        self._failures[proxy] = 0


def fetch_with_rotation(url, proxy_manager, max_attempts=5):
    """Permintaan dengan pergantian proxy otomatis saat kesalahan"""
    
    for attempt in range(max_attempts):
        proxy = proxy_manager.get_proxy()
        
        if not proxy:
            raise Exception("Tidak ada proxy yang tersedia")
        
        proxies = {"http": proxy, "https": proxy}
        
        try:
            response = requests.get(url, proxies=proxies, timeout=(10, 30))
            response.raise_for_status()
            proxy_manager.report_success(proxy)
            return response
            
        except (requests.exceptions.Timeout, 
                requests.exceptions.ConnectionError):
            proxy_manager.report_failure(proxy)
            print(f"Timeout melalui {proxy}, mencoba yang lain...")
            continue
    
    raise Exception(f"Gagal mendapatkan data setelah {max_attempts} percobaan")

Saat menggunakan proxy residensial dengan rotasi otomatis, logika ini disederhanakan — penyedia secara otomatis mengganti IP pada setiap permintaan atau sesuai interval yang ditentukan.

Permintaan Asinkron dengan Kontrol Timeout

Saat parsing massal, permintaan sinkron tidak efisien. Pendekatan asinkron memungkinkan memproses ratusan URL secara paralel, tetapi memerlukan penanganan timeout yang hati-hati.

import aiohttp
import asyncio
from typing import List, Tuple

async def fetch_one(
    session: aiohttp.ClientSession, 
    url: str,
    semaphore: asyncio.Semaphore
) -> Tuple[str, str | None, str | None]:
    """Memuat satu URL dengan penanganan timeout"""
    
    async with semaphore:  # Batasi paralelitas
        try:
            async with session.get(url) as response:
                content = await response.text()
                return (url, content, None)
                
        except asyncio.TimeoutError:
            return (url, None, "timeout")
        except aiohttp.ClientError as e:
            return (url, None, str(e))


async def fetch_all(
    urls: List[str],
    proxy: str,
    max_concurrent: int = 10
) -> List[Tuple[str, str | None, str | None]]:
    """Memuat massal dengan kontrol timeout dan paralelitas"""
    
    timeout = aiohttp.ClientTimeout(total=45, connect=10, sock_read=30)
    semaphore = asyncio.Semaphore(max_concurrent)
    
    connector = aiohttp.TCPConnector(
        limit=max_concurrent,
        limit_per_host=5  # Tidak lebih dari 5 koneksi per host
    )
    
    async with aiohttp.ClientSession(
        timeout=timeout,
        connector=connector
    ) as session:
        # Atur proxy untuk semua permintaan
        tasks = [
            fetch_one(session, url, semaphore) 
            for url in urls
        ]
        results = await asyncio.gather(*tasks)
    
    # Statistik
    success = sum(1 for _, content, _ in results if content)
    timeouts = sum(1 for _, _, error in results if error == "timeout")
    print(f"Berhasil: {success}, Timeout: {timeouts}")
    
    return results


# Penggunaan
async def main():
    urls = [f"https://example.com/page/{i}" for i in range(100)]
    results = await fetch_all(
        urls, 
        proxy="http://user:pass@proxy.example.com:8080",
        max_concurrent=10
    )

asyncio.run(main())

Penting: Jangan atur paralelitas terlalu tinggi. 50-100 permintaan simultan melalui satu proxy — sudah banyak. Lebih baik 10-20 dengan beberapa proxy.

Diagnostik: Cara Menemukan Penyebabnya

Sebelum mengubah pengaturan, tentukan sumber masalahnya.

Langkah 1: Periksa proxy secara langsung

# Tes sederhana melalui curl dengan pengukuran waktu
curl -x http://user:pass@proxy:8080 \
     -w "Connect: %{time_connect}s\nTotal: %{time_total}s\n" \
     -o /dev/null -s \
     https://httpbin.org/get

Jika time_connect lebih dari 5 detik — masalah dengan proxy atau jaringan ke sana.

Langkah 2: Bandingkan dengan permintaan langsung

import requests
import time

def measure_request(url, proxies=None):
    start = time.time()
    try:
        r = requests.get(url, proxies=proxies, timeout=30)
        elapsed = time.time() - start
        return f"OK: {elapsed:.2f}s, status: {r.status_code}"
    except Exception as e:
        elapsed = time.time() - start
        return f"FAIL: {elapsed:.2f}s, error: {type(e).__name__}"

url = "https://target-site.com"
proxy = {"http": "http://proxy:8080", "https": "http://proxy:8080"}

print("Langsung:", measure_request(url))
print("Melalui proxy:", measure_request(url, proxy))

Langkah 3: Periksa jenis proxy yang berbeda

Timeout dapat bergantung pada jenis proxy:

Jenis Proxy Penundaan Khas Timeout yang Direkomendasikan
Datacenter 50-200 ms Connect: 5s, Read: 15s
Residential 200-800 ms Connect: 10s, Read: 30s
Mobile 300-1500 ms Connect: 15s, Read: 45s

Langkah 4: Catat detail

import logging
import requests
from requests.adapters import HTTPAdapter

# Aktifkan debug logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("urllib3").setLevel(logging.DEBUG)

# Sekarang Anda akan melihat semua tahap permintaan:
# - Resolusi DNS
# - Pembentukan koneksi
# - Pengiriman permintaan
# - Penerimaan respons

Daftar Periksa Solusi Kesalahan Timeout

Algoritma singkat tindakan saat timeout terjadi:

  1. Tentukan jenis timeout — connect atau read? Ini adalah masalah yang berbeda.
  2. Periksa proxy secara terpisah — apakah itu bekerja sama sekali? Berapa penundaannya?
  3. Tingkatkan timeout — mungkin nilainya terlalu agresif untuk jenis proxy Anda.
  4. Tambahkan retry dengan backoff — timeout tunggal normal, yang penting adalah ketahanan.
  5. Atur rotasi — secara otomatis beralih ke proxy lain saat masalah.
  6. Batasi paralelitas — terlalu banyak permintaan simultan membebani proxy.
  7. Periksa situs target — mungkin itu memblokir atau throttle permintaan Anda.

Kesimpulan

Kesalahan timeout melalui proxy — masalah yang dapat diselesaikan. Dalam kebanyakan kasus, cukup mengonfigurasi timeout dengan benar sesuai jenis proxy, menambahkan logika retry, dan menerapkan rotasi saat kegagalan. Untuk tugas dengan persyaratan stabilitas tinggi, gunakan proxy residensial dengan rotasi otomatis — pelajari lebih lanjut di proxycove.com.