Wprowadzenie: dlaczego Java HTTP proxy ma znaczenie
Każdy programista Java, który buduje aplikacje wymagające zewnętrznych żądań HTTP — od web scrapingu po integracje API — wcześniej czy później napotyka potrzebę użycia proxy. Java HTTP proxy to nie tylko kwestia ukrywania adresu IP. To fundament dla: geolokalizacji żądań, omijania limitów rate-limiting, testowania aplikacji z różnych lokalizacji i budowania odpornych systemów scrapowania.
Ekosystem Java oferuje kilka podejść do obsługi proxy: od natywnego HttpClient (Java 11+), przez popularny OkHttp, po biblioteki parsingowe jak Jsoup. Każda z nich ma swoją specyfikę konfiguracji — i każda wymaga innego podejścia do autoryzacji, pulowania połączeń czy obsługi TLS.
W tym przewodniku pokażę konkretne, uruchamialne przykłady kodu dla wszystkich głównych bibliotek HTTP w Java 17+. Skupię się na wzorcach produkcyjnych: retry logic, connection pooling, timeouty i równoległe scrapowanie przez pulę residential proxy.
Java 11+ HttpClient z ProxySelector
HttpClient wprowadzony w Java 11 to nowoczesny, asynchroniczny klient HTTP wbudowany w JDK. Obsługa proxy odbywa się przez ProxySelector — mechanizm, który pozwala dynamicznie wybierać proxy dla każdego żądania.
Podstawowa konfiguracja proxy
Najprostszy sposób to użycie systemowego ProxySelector lub utworzenie własnego:
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class HttpClientProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
public static void main(String[] args) throws Exception {
// Tworzymy ProxySelector dla konkretnego proxy
ProxySelector proxySelector = ProxySelector.of(
new InetSocketAddress(PROXY_HOST, PROXY_PORT)
);
HttpClient client = HttpClient.newBuilder()
.proxy(proxySelector)
.connectTimeout(Duration.ofSeconds(30))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/ip"))
.timeout(Duration.ofSeconds(60))
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status: " + response.statusCode());
System.out.println("Body: " + response.body());
}
}Ten przykład działa dla proxy bez autoryzacji. Dla proxy z autoryzacją musimy dodać Authenticator:
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class HttpClientAuthenticatedProxy {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String USERNAME = "user-country-US"; // Geo-targeting w nazwie użytkownika
private static final String PASSWORD = "your_password";
public static void main(String[] args) throws Exception {
// ProxySelector
ProxySelector proxySelector = ProxySelector.of(
new InetSocketAddress(PROXY_HOST, PROXY_PORT)
);
// Authenticator dla proxy
Authenticator proxyAuthenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(USERNAME, PASSWORD.toCharArray());
}
};
HttpClient client = HttpClient.newBuilder()
.proxy(proxySelector)
.authenticator(proxyAuthenticator)
.connectTimeout(Duration.ofSeconds(30))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/ip"))
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Response: " + response.body());
}
}Dynamiczny wybór proxy z własnym ProxySelector
Dla zaawansowanych scenariuszy — np. rotacja IP czy geo-targeting — warto zaimplementować własny ProxySelector:
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class RotatingProxySelector extends ProxySelector {
private final List<Proxy> proxies;
private final AtomicInteger counter = new AtomicInteger(0);
private final ConcurrentHashMap<URI, List<Proxy>> cache = new ConcurrentHashMap<>();
public RotatingProxySelector(List<String> proxyAddresses) {
this.proxies = proxyAddresses.stream()
.map(addr -> {
String[] parts = addr.split(":");
return new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress(parts[0], Integer.parseInt(parts[1]))
);
})
.toList();
}
@Override
public List<Proxy> select(URI uri) {
// Round-robin rotation
int index = counter.getAndIncrement() % proxies.size();
Proxy selected = proxies.get(index);
System.out.println("Selected proxy: " + selected.address());
return List.of(selected);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
System.err.println("Proxy connection failed: " + sa + " - " + ioe.getMessage());
// Logika retry lub usunięcie uszkodzonego proxy z puli
}
}OkHttp z Proxy i Authenticator
OkHttp to najpopularniejszy klient HTTP w ekosystemie Java/Android. Jego API jest bardziej intuicyjne niż HttpClient, a obsługa proxy wymaga ustawienia Proxy i Authenticator na OkHttpClient.
Podstawowa konfiguracja OkHttp proxy
import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
public class OkHttpProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String USERNAME = "user-country-DE";
private static final String PASSWORD = "your_password";
public static void main(String[] args) throws IOException {
// Definicja proxy
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress(PROXY_HOST, PROXY_PORT)
);
// Authenticator dla proxy
Authenticator proxyAuthenticator = new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
if (response.request().header("Proxy-Authorization") != null) {
return null; // Już próbowaliśmy autoryzacji - nie powtarzaj
}
String credential = Credentials.basic(USERNAME, PASSWORD);
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
}
};
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator(proxyAuthenticator)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
Request request = new Request.Builder()
.url("https://httpbin.org/ip")
.header("User-Agent", "Mozilla/5.0")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println("Status: " + response.code());
System.out.println("Body: " + response.body().string());
}
}
}Connection pooling i optymalizacja w OkHttp
OkHttp automatycznie zarządza pulą połączeń, ale warto dostosować parametry do przypadku użycia:
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import java.util.concurrent.TimeUnit;
public class OkHttpOptimizedClient {
public static OkHttpClient createOptimizedClient() {
// Connection pool: max 20 idle connections, 5 minut keep-alive
ConnectionPool connectionPool = new ConnectionPool(20, 5, TimeUnit.MINUTES);
return new OkHttpClient.Builder()
.connectionPool(connectionPool)
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
// Retry interceptor z exponential backoff
.addInterceptor(new RetryInterceptor(3))
// Connection pool monitoring
.eventListener(new ConnectionPoolEventListener())
.build();
}
// Custom retry interceptor
static class RetryInterceptor implements Interceptor {
private final int maxRetries;
RetryInterceptor(int maxRetries) {
this.maxRetries = maxRetries;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
IOException lastException = null;
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
return chain.proceed(request);
} catch (IOException e) {
lastException = e;
if (attempt == maxRetries) break;
// Exponential backoff
long delayMs = (long) Math.pow(2, attempt) * 1000;
try {
Thread.sleep(delayMs);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted during retry", ie);
}
}
}
throw new IOException("Max retries exceeded", lastException);
}
}
}Jsoup z proxy dla parsowania HTML
Jsoup to biblioteka do parsowania HTML, która pozwala na pobieranie dokumentów bezpośrednio z URL. Obsługa proxy wymaga użycia Connection.proxy() lub systemowych właściwości.
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
public class JsoupProxyExample {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String USERNAME = "user-country-US-session-abc123";
private static final String PASSWORD = "your_password";
public static void main(String[] args) {
try {
// Proxy HTTP
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress(PROXY_HOST, PROXY_PORT)
);
// Pobieranie dokumentu przez proxy z autoryzacją
Document doc = Jsoup.connect("https://example.com")
.proxy(proxy)
.header("Proxy-Authorization", java.util.Base64.getEncoder()
.encodeToString((USERNAME + ":" + PASSWORD).getBytes()))
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.timeout(30000)
.followRedirects(true)
.get();
// Parsowanie tytułu
String title = doc.title();
System.out.println("Title: " + title);
// Wyciąganie linków
for (Element link : doc.select("a[href]")) {
System.out.println("Link: " + link.attr("href") + " - " + link.text());
}
} catch (IOException e) {
System.err.println("Error fetching document: " + e.getMessage());
}
}
}Integracja Jsoup z OkHttp dla lepszej kontroli
Dla bardziej zaawansowanych scenariuszy warto użyć OkHttp do pobrania HTML, a Jsoup tylko do parsowania:
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class JsoupWithOkHttpExample {
private final OkHttpClient httpClient;
public JsoupWithOkHttpExample(OkHttpClient httpClient) {
this.httpClient = httpClient;
}
public Document fetchAndParse(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.header("Accept", "text/html,application/xhtml+xml")
.header("Accept-Language", "en-US,en;q=0.9")
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected response code: " + response.code());
}
String html = response.body().string();
return Jsoup.parse(html, url); // URL jako base URI dla relative links
}
}
}Apache HttpClient (krótkie omówienie)
Starsze systemy często używają Apache HttpClient 4.x. Konfiguracja proxy jest bardziej rozwlekła:
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
public class ApacheHttpClientProxy {
public static void main(String[] args) throws Exception {
// Proxy host
HttpHost proxy = new HttpHost("gate.proxyhat.com", 8080);
// Credentials provider dla autoryzacji proxy
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("gate.proxyhat.com", 8080),
new UsernamePasswordCredentials("user-country-US", "your_password")
);
// Request config z proxy
RequestConfig config = RequestConfig.custom()
.setProxy(proxy)
.setConnectTimeout(30000)
.setSocketTimeout(60000)
.build();
try (CloseableHttpClient client = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultRequestConfig(config)
.build()) {
HttpGet request = new HttpGet("https://httpbin.org/ip");
try (CloseableHttpResponse response = client.execute(request)) {
String body = EntityUtils.toString(response.getEntity());
System.out.println(body);
}
}
}
}Równoległe scrapowanie z ExecutorService i pulą residential proxy
Produkcyjne scrapowanie wymaga równoległości. Oto kompletny przykład z pulą residential proxy, rate limiting i obsługą błędów:
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ParallelScraper {
private static final String PROXY_HOST = "gate.proxyhat.com";
private static final int PROXY_PORT = 8080;
private static final String BASE_USER = "user-country-US";
private static final String PASSWORD = "your_password";
private final HttpClient baseClient;
private final ExecutorService executor;
private final Semaphore rateLimiter;
private final AtomicInteger successCount = new AtomicInteger(0);
private final AtomicInteger failureCount = new AtomicInteger(0);
public ParallelScraper(int maxConcurrency, int requestsPerSecond) {
this.executor = Executors.newFixedThreadPool(maxConcurrency);
this.rateLimiter = new Semaphore(requestsPerSecond);
this.baseClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.executor(executor)
.build();
}
public CompletableFuture<ScrapeResult> scrapeUrl(String url, String sessionId) {
return CompletableFuture.supplyAsync(() -> {
try {
rateLimiter.acquire();
// Sticky session dla residential proxy
String username = BASE_USER + "-session-" + sessionId;
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of(
new InetSocketAddress(PROXY_HOST, PROXY_PORT)
))
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, PASSWORD.toCharArray());
}
})
.connectTimeout(Duration.ofSeconds(30))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(60))
.header("User-Agent", randomUserAgent())
.GET()
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
successCount.incrementAndGet();
return new ScrapeResult(url, response.statusCode(), response.body(), null);
} catch (Exception e) {
failureCount.incrementAndGet();
return new ScrapeResult(url, -1, null, e.getMessage());
} finally {
rateLimiter.release();
}
}, executor);
}
public CompletableFuture<List<ScrapeResult>> scrapeUrls(List<String> urls) {
List<CompletableFuture<ScrapeResult>> futures = urls.stream()
.map(url -> scrapeUrl(url, generateSessionId()))
.toList();
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.toList());
}
private String generateSessionId() {
return java.util.UUID.randomUUID().toString().substring(0, 8);
}
private String randomUserAgent() {
String[] agents = {
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
};
return agents[ThreadLocalRandom.current().nextInt(agents.length)];
}
public void shutdown() {
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
public void printStats() {
System.out.println("Success: " + successCount.get() + ", Failures: " + failureCount.get());
}
public record ScrapeResult(String url, int statusCode, String body, String error) {}
public static void main(String[] args) throws Exception {
ParallelScraper scraper = new ParallelScraper(20, 10);
List<String> urls = List.of(
"https://httpbin.org/ip",
"https://httpbin.org/headers",
"https://httpbin.org/user-agent",
"https://httpbin.org/get"
);
List<ScrapeResult> results = scraper.scrapeUrls(urls).join();
results.forEach(r -> {
if (r.error() == null) {
System.out.println(r.url() + " -> " + r.statusCode());
} else {
System.out.println(r.url() + " -> ERROR: " + r.error());
}
});
scraper.printStats();
scraper.shutdown();
}
}TLS/SSL i custom SSLContext
Przy pracy z proxy możesz napotkać problemy z TLS — szczególnie gdy upstream serwer używa self-signed certificates lub niestandardowych konfiguracji. W takich przypadkach potrzebujesz własnego SSLContext:
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.http.HttpClient;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
public class TlsConfigurations {
// UWAGA: Tylko dla developmentu! Nie używaj w produkcji.
public static SSLContext createTrustAllContext() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
return sslContext;
}
// Produkcyjna konfiguracja z custom truststore
public static SSLContext createProductionContext() throws Exception {
// Użyj domyślnego truststore z dodatkowymi certyfikatami
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, null, new SecureRandom());
return sslContext;
}
public static HttpClient createClientWithCustomTls(SSLContext sslContext) {
return HttpClient.newBuilder()
.sslContext(sslContext)
.connectTimeout(java.time.Duration.ofSeconds(30))
.build();
}
}
// Dla OkHttp:
import okhttp3.OkHttpClient;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
public class OkHttpTlsConfig {
public static OkHttpClient createTrustAllClient() throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] {
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}
}, new SecureRandom());
return new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) null)
.hostnameVerifier((hostname, session) -> true)
.build();
}
}Porównanie bibliotek HTTP dla proxy
| Feature | Java HttpClient | OkHttp | Apache HttpClient | Jsoup |
|---|---|---|---|---|
| Wbudowany w JDK | Tak (Java 11+) | Nie | Nie | Nie |
| Async/Reactive | Tak (CompletableFuture) | Tak (Call.enqueue) | Tak (Future callbacks) | Nie |
| Connection pooling | Tak (automatyczny) | Tak (ConnectionPool) | Tak (PoolingHttpClient) | Brak |
| Proxy auth | Authenticator | proxyAuthenticator | CredentialsProvider | Header ręczny |
| HTTP/2 | Tak | Tak | Tak (5.x) | Nie |
| WebSocket | Tak | Tak | Nie | Nie |
| HTML parsing | Nie | Nie | Nie | Tak (główna funkcja) |
| Rozmiar zależności | 0 (JDK) | ~800KB | ~1.2MB | ~400KB |
Best practices dla produkcyjnych aplikacji
1. Timeouty i retry logic
Zawsze ustawiaj timeouty na wszystkich poziomach: connect, read, write. Domyślne wartości często są zbyt długie dla scrapowania:
// HttpClient z odpowiednimi timeoutami
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(15))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(30))
.build();2. Circuit breaker dla awarii proxy
Gdy proxy przestaje odpowiadać, nie próbuj w nieskończoność. Użyj circuit breaker:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class SimpleCircuitBreaker {
private final int failureThreshold;
private final long timeoutMs;
private final AtomicInteger failures = new AtomicInteger(0);
private final AtomicLong lastFailureTime = new AtomicLong(0);
private volatile boolean open = false;
public SimpleCircuitBreaker(int failureThreshold, long timeoutMs) {
this.failureThreshold = failureThreshold;
this.timeoutMs = timeoutMs;
}
public boolean allowRequest() {
if (!open) return true;
if (System.currentTimeMillis() - lastFailureTime.get() > timeoutMs) {
open = false;
failures.set(0);
return true;
}
return false;
}
public void recordFailure() {
int count = failures.incrementAndGet();
lastFailureTime.set(System.currentTimeMillis());
if (count >= failureThreshold) {
open = true;
}
}
public void recordSuccess() {
failures.set(0);
open = false;
}
}3. Logging i monitoring
Dodaj logowanie dla każdego żądania z proxy. Śledź: czas odpowiedzi, status code, IP wyjściowe:
public class ProxyMetrics {
private final String proxyAddress;
private final String targetUrl;
private final long durationMs;
private final int statusCode;
private final String outboundIp;
private final String errorMessage;
// Constructor, getters...
public void log() {
System.out.printf(
"[%s] %s -> %d (%dms) IP: %s%n",
proxyAddress, targetUrl, statusCode, durationMs, outboundIp
);
}
}Key Takeaways
- Java HttpClient (Java 11+) to nowoczesny wybór z wbudowaną obsługą HTTP/2 i asynchronicznością — używaj
ProxySelectoriAuthenticatordla proxy.- OkHttp oferuje lepsze API i automatyczny connection pooling — konfiguruj przez
OkHttpClient.Builder().proxy()iproxyAuthenticator().- Jsoup proxy wymaga ręcznego ustawienia nagłówka autoryzacji lub integracji z OkHttp dla lepszej kontroli.
- Zawsze ustawiaj timeouty na wszystkich poziomach i implementuj retry z exponential backoff.
- Dla równoległego scrapowania używaj
ExecutorServicez rate limiting i sticky sessions przez residential proxy.- TLS issues rozwiąż przez custom
SSLContext— ale nigdy nie używaj „trust all” w produkcji.
Często zadawane pytania
Czy mogę używać SOCKS5 proxy z Java HttpClient?
Tak. Zmień typ proxy na Proxy.Type.SOCKS w ProxySelector. Dla ProxyHat użyj portu 1080: new InetSocketAddress("gate.proxyhat.com", 1080). Pamiętaj, że SOCKS5 działa na niższym poziomie i może wymagać innej konfiguracji autoryzacji.
Jak obsłużyć rotację IP w Java?
Najprościej: użyj sticky sessions z residential proxy. Nazwa użytkownika zawiera identyfikator sesji: user-country-US-session-abc123. Dla każdej sesji proxy przydziela stały IP. Aby zmienić IP, wygeneruj nowy session ID. Alternatywnie, zaimplementuj własny ProxySelector z pulą proxy i rotacją round-robin.
Dlaczego moje żądania przez proxy są wolniejsze?
Proxy dodaje dodatkowy hop w trasie żądania. Residential proxy mogą być wolniejsze niż datacenter ze względu na naturę peer-to-peer. Zminimalizuj opóźnienia przez: connection pooling, utrzymywanie sticky sessions (TCP keep-alive), wybór lokalizacji geo blisko targetu, i równoległe żądania z odpowiednim concurrency.
Jak debugować problemy z proxy w Java?
Włącz JVM logging: -Djava.util.logging.config.file=logging.properties lub użyj system properties: -Djavax.net.debug=all dla TLS. Dodaj własny interceptor/logging w OkHttp lub HttpRequest.BodyPublishers w HttpClient. Sprawdź odpowiedź 407 Proxy Authentication Required — oznacza problem z credentials.
Czy mogę używać tego samego klienta HTTP dla wielu proxy?
HttpClient jest immutable — każda zmiana proxy wymaga nowego klienta. OkHttp pozwala na zmianę proxy przez OkHttpClient.newBuilder().proxy(newProxy).build() — tworzy „płytką kopię” z nowym proxy, współdzieląc connection pool. Dla wysokiej wydajności z wieloma proxy, rozważ pulę klientów lub własny ProxySelector.
Zakończenie
Java oferuje solidne narzędzia do pracy z HTTP proxy — od natywnego HttpClient, przez popularny OkHttp, po specjalistyczne biblioteki jak Jsoup. Klucz do sukcesu to właściwa konfiguracja: timeouty, retry logic, connection pooling i monitoring.
Dla poważnego scrapowania produkcyjnego, residential proxy z sticky sessions i geo-targeting to must-have. ProxyHat oferuje oba — z prostą autoryzacją w nazwie użytkownika i wysoką dostępnością.
Sprawdź ceny residential proxy lub dostępne lokalizacje na ProxyHat. Masz pytania o implementację? Zostaw komentarz lub skontaktuj się z supportem.






