Kurumsal Java uygulamaları düzenli olarak dış API'lere erişir, veri ayrıştırır veya izole kurumsal ağlarda çalışır — ve bu durumların her birinde, bir noktada trafiğin bir proxy sunucusu üzerinden yönlendirilmesi sorunu ortaya çıkar. Java'da proxy ayarlamak basit bir görev gibi görünse de, RestTemplate, OkHttp ve WebClient'in farklı davrandığını ve JVM sistem özelliklerinin her yerde çalışmadığını gördüğünüzde işler karmaşıklaşır. Bu kılavuzda, belirli kod örnekleri, dikkat edilmesi gereken noktalar ve farklı görevler için uygun proxy türü seçimi hakkında öneriler bulacaksınız.
Java uygulamalarının proxyye neden ihtiyacı var
Java, kurumsal geliştirme için en önemli dillerden biri olmaya devam ediyor. Spring Boot hizmetleri, mikro hizmet mimarileri, batch görevleri, ETL boru hatları — bunların hepsi düzenli olarak dış dünyaya HTTP istekleri gönderir. Ve bu senaryoların her birinde, proxy belirli bir sorunu çözer:
- Proxy geçidi zorunlu kurumsal ağlar. Birçok şirkette, tüm çıkış trafiği kurumsal bir proxy (Squid, Zscaler, BlueCoat) üzerinden geçer. Bu ayar yapılmadan uygulama internete çıkamaz.
- Veri ayrıştırma ve toplama. Dış sitelerden veri toplayan hizmetler (fiyatlar, alıntılar, haberler), rate-limit ve IP engellerini aşmak için proxy kullanır.
- Coğrafi test. QA ekipleri, API veya web sitesinin farklı bölgelerden nasıl davrandığını kontrol eder — proxy, gerekli ülkeden gelen isteği taklit etmeyi sağlar.
- Hedef API tarafındaki kısıtlamaların aşılması. Bazı dış API'ler, bir IP'den gelen istek sayısına sınırlamalar koyar. Proxy döngüsü, yükü dağıtmaya yardımcı olur.
- Güvenlik ve anonimlik. Dış hizmetlere erişirken uygulamanın gerçek IP'sini gizlemek, hassas entegrasyonlar için standart bir uygulamadır.
Göreve bağlı olarak, Java'da proxy ayarlama yaklaşımları önemli ölçüde farklılık gösterir. JVM küresel sistem özellikleri en basit seçenektir, ancak bazı HTTP istemcileriyle çalışmaz. OkHttp ve WebClient, açık bir yapılandırma gerektirir. Her durumu sırayla inceleyelim.
JVM sistem özellikleri ile küresel ayarlama
Java uygulamasının tüm HTTP trafiğini bir proxy üzerinden yönlendirmenin en hızlı yolu, JVM sistem özelliklerini kullanmaktır. Bu, standart HttpURLConnection ve bunu arka planda kullanan birçok kütüphane için çalışır.
Proxy ayarlamak için birkaç yol vardır:
1. JVM argümanları ile başlatma sırasında:
java -Dhttp.proxyHost=proxy.example.com \
-Dhttp.proxyPort=8080 \
-Dhttps.proxyHost=proxy.example.com \
-Dhttps.proxyPort=8080 \
-Dhttp.nonProxyHosts="localhost|127.0.0.1|*.internal.corp" \
-jar myapp.jar
2. Uygulama kodu üzerinden (örneğin, main metodunda veya @PostConstruct içinde):
System.setProperty("http.proxyHost", "proxy.example.com");
System.setProperty("http.proxyPort", "8080");
System.setProperty("https.proxyHost", "proxy.example.com");
System.setProperty("https.proxyPort", "8080");
// Hariç tutulanlar — proxy üzerinden geçmemesi gereken hostlar
System.setProperty("http.nonProxyHosts", "localhost|127.0.0.1|*.internal.corp");
3. Spring Boot'taki application.properties dosyası üzerinden (sonrasında @PostConstruct ile uygulanır):
# application.properties proxy.host=proxy.example.com proxy.port=8080 proxy.username=user proxy.password=secret
⚠️ Bilinmesi gerekenler
JVM sistem özellikleri yalnızca HttpURLConnection için çalışır. OkHttp, Apache HttpClient ve Reactor Netty (WebClient) bu özellikleri görmezden gelir — bunlar için aşağıda açıklanan açık bir yapılandırma gereklidir.
Proxy kimlik doğrulaması gerektiriyorsa, Authenticator kullanın:
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication("username", "password".toCharArray());
}
return null;
}
});
Spring Boot'ta proxy: RestTemplate ve Apache HttpClient
RestTemplate, Spring'deki standart senkron HTTP istemcisidir. Varsayılan olarak SimpleClientHttpRequestFactory kullanır, bu da HttpURLConnection'a dayanır ve JVM sistem özelliklerini okur. Ancak daha esnek bir yapılandırma (zaman aşımı, bağlantı havuzu, kimlik doğrulamalı proxy) için Apache HttpClient kullanılması önerilir.
Maven bağımlılığı:
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
Apache HttpClient 5 ile proxy ile RestTemplate yapılandırması:
@Configuration
public class RestTemplateConfig {
@Value("${proxy.host}")
private String proxyHost;
@Value("${proxy.port}")
private int proxyPort;
@Value("${proxy.username:}")
private String proxyUsername;
@Value("${proxy.password:}")
private String proxyPassword;
@Bean
public RestTemplate restTemplate() {
HttpHost proxy = new HttpHost("http", proxyHost, proxyPort);
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
if (!proxyUsername.isEmpty()) {
credentialsProvider.setCredentials(
new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUsername, proxyPassword.toCharArray())
);
}
CloseableHttpClient httpClient = HttpClients.custom()
.setProxy(proxy)
.setDefaultCredentialsProvider(credentialsProvider)
.build();
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(httpClient);
factory.setConnectTimeout(5000);
factory.setReadTimeout(10000);
return new RestTemplate(factory);
}
}
Bu yapılandırma, proxy parametrelerini application.properties veya ortam değişkenlerine taşımayı sağlar; bu, farklı proxy'lere sahip prod/staging/dev ortamları için kritik öneme sahiptir.
Bazı isteklerin proxy üzerinden, diğerlerinin ise doğrudan gitmesini istiyorsanız, farklı isimlere sahip iki ayrı RestTemplate bean'i oluşturun ve gerekli olanı enjekte etmek için @Qualifier kullanın.
Spring WebClient'ta proxy (reaktif yığın)
WebClient, Reactor Netty üzerine inşa edilmiş reaktif bir HTTP istemcisidir. JVM sistem özelliklerini proxy için okumaz — yapılandırma, istemci oluşturulurken ProxyProvider aracılığıyla yapılır.
Bağımlılık (spring-boot-starter-webflux içinde bulunur):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
HTTP proxy ile WebClient yapılandırması:
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
HttpClient httpClient = HttpClient.create()
.proxy(proxy -> proxy
.type(ProxyProvider.Proxy.HTTP)
.host("proxy.example.com")
.port(8080)
.username("user")
.password(user -> "password")
);
ClientHttpConnector connector =
new ReactorClientHttpConnector(httpClient);
return WebClient.builder()
.clientConnector(connector)
.build();
}
}
WebClient'ta SOCKS5 proxy için:
HttpClient httpClient = HttpClient.create()
.proxy(proxy -> proxy
.type(ProxyProvider.Proxy.SOCKS5)
.host("socks5.proxy.example.com")
.port(1080)
.username("user")
.password(user -> "password")
);
💡 Yapılandırma önerisi
Reaktif uygulamalarda, farklı dış hizmetler için farklı proxy'lerle birden fazla WebClient örneği oluşturmak sıkça gereklidir. WebClient.Builder'ı prototip bean olarak kullanın ve ayarları .mutate() ile geçersiz kılın.
OkHttp'de proxy ayarlama
OkHttp, birçok Java ve Android uygulaması tarafından kullanılan popüler bir HTTP kütüphanesidir ve bazı Spring entegrasyonları (örneğin, OkHttp arka ucu ile Feign) tarafından da kullanılmaktadır. Kendi proxy ayarlama mekanizmasına sahiptir ve JVM sistem özelliklerini de okumaz.
OkHttp'de HTTP/HTTPS proxy için temel ayar:
import okhttp3.*;
import java.net.InetSocketAddress;
import java.net.Proxy;
// Proxy oluşturma
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress("proxy.example.com", 8080)
);
// Proxy kimlik doğrulaması
Authenticator proxyAuthenticator = (route, response) -> {
String credential = Credentials.basic("username", "password");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
};
// İstemciyi oluşturma
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator(proxyAuthenticator)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
// İstek örneği
Request request = new Request.Builder()
.url("https://api.example.com/data")
.build();
try (Response response = client.newCall(request).execute()) {
System.out.println(response.body().string());
}
Spring Boot ile OkHttp entegrasyonu üzerinden bean:
@Configuration
@ConditionalOnProperty(name = "proxy.enabled", havingValue = "true")
public class OkHttpConfig {
@Value("${proxy.host}")
private String proxyHost;
@Value("${proxy.port}")
private int proxyPort;
@Bean
public OkHttpClient okHttpClient() {
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress(proxyHost, proxyPort)
);
return new OkHttpClient.Builder()
.proxy(proxy)
.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
.build();
}
}
@ConditionalOnProperty özelliğine dikkat edin — bu, kodu değiştirmeden proxy'yi yalnızca gerekli ortamlarda etkinleştirmeyi sağlar.
Eğer OkHttp'yi konut proxy'leri ile veri ayrıştırma için kullanıyorsanız, bağlantı havuzunu doğru bir şekilde ayarlamak önemlidir. Konut proxy'leri genellikle veri merkezi proxy'lerine göre daha yüksek gecikmeye sahiptir, bu nedenle zaman aşımını artırın ve havuz boyutunu azaltın, böylece mevcut bağlantıları tüketmeyin.
Java'da SOCKS5 proxy: özellikler ve örnekler
SOCKS5, HTTP proxy'ye göre daha düşük seviyeli bir protokoldür. Taşıma katmanında çalışır ve her türlü protokolü (HTTP, HTTPS, FTP vb.) destekler ve ayrıca proxy tarafında DNS çözümlemesi yapar. Java uygulamaları için SOCKS5, yalnızca HTTP'yi değil, diğer TCP bağlantılarını da proxy'lemek gerektiğinde özellikle faydalıdır.
SOCKS5, JVM sistem özellikleri üzerinden:
System.setProperty("socksProxyHost", "socks5.proxy.example.com");
System.setProperty("socksProxyPort", "1080");
System.setProperty("java.net.socks.username", "user");
System.setProperty("java.net.socks.password", "password");
OkHttp'de SOCKS5 (java.net.Proxy üzerinden):
Proxy socks5Proxy = new Proxy(
Proxy.Type.SOCKS,
new InetSocketAddress("socks5.proxy.example.com", 1080)
);
OkHttpClient client = new OkHttpClient.Builder()
.proxy(socks5Proxy)
.build();
SOCKS5 ve DNS ile ilgili önemli bir özellik: Java varsayılan olarak DNS'i yerel olarak çözer ve proxy üzerinden yalnızca IP adresini gönderir. Bu, gerçek DNS isteğini açığa çıkarabilir. DNS çözümlemesinin de proxy üzerinden gitmesini istiyorsanız, OkHttp ile aşağıdaki yaklaşımı kullanın:
// SOCKS5 üzerinden çözümlemeyi zorlayan özel DNS
OkHttpClient client = new OkHttpClient.Builder()
.proxy(socks5Proxy)
.dns(hostname -> {
// Çözülmemiş hostu döndür — OkHttp bunu proxy'ye iletecek
return Collections.singletonList(InetAddress.getByName(hostname));
})
.build();
SOCKS5 ve HTTP proxy'yi ne zaman seçmeli
| Kriter | HTTP Proxy | SOCKS5 Proxy |
|---|---|---|
| Protokoller | HTTP/HTTPS | Herhangi bir TCP/UDP |
| Proxy üzerinden DNS | Uygulamaya bağlı | Destekleniyor |
| Java ile uyumluluk | Yerel | Yerel (java.net.Proxy.Type.SOCKS) |
| Kullanım | Kurumsal geçitler, web ayrıştırma | Anonimlik, standart dışı protokoller |
Java uygulamalarında proxy döngüsü
Uygulama bir kaynağa çok sayıda istek gönderiyorsa, tek bir proxy IP'sinin kullanılması hızlı bir şekilde engellemeye neden olur. Proxy döngüsü — farklı IP adresleri arasında geçiş yapmak — bu sorunu çözer. Java'da bu birkaç şekilde uygulanabilir.
Seçenek 1: Döngüsel proxy uç noktası üzerinden döngü
En basit yaklaşım, her istekte veya belirli bir aralıkta IP'yi otomatik olarak değiştiren tek bir uç noktaya sahip bir proxy sağlayıcısı kullanmaktır. Tek bir host ve port belirtirsiniz ve döngü sağlayıcı tarafında gerçekleşir. Döngü için herhangi bir kod yazmanıza gerek yoktur.
Seçenek 2: Proxy listesi üzerinden uygulama tarafında döngü
@Component
public class ProxyRotator {
private final List<ProxyConfig> proxies;
private final AtomicInteger counter = new AtomicInteger(0);
public ProxyRotator(@Value("${proxy.list}") List<String> proxyList) {
this.proxies = proxyList.stream()
.map(this::parseProxy)
.collect(Collectors.toList());
}
public OkHttpClient getClientWithNextProxy() {
int index = counter.getAndIncrement() % proxies.size();
ProxyConfig config = proxies.get(index);
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress(config.getHost(), config.getPort())
);
return new OkHttpClient.Builder()
.proxy(proxy)
.build();
}
private ProxyConfig parseProxy(String proxyStr) {
// "host:port" veya "user:pass@host:port" biçimindeki dizeyi ayrıştırma
String[] parts = proxyStr.split(":");
return new ProxyConfig(parts[0], Integer.parseInt(parts[1]));
}
}
Seçenek 3: ProxySelector üzerinden döngü
Java, ProxySelector adında yerleşik bir mekanizma sağlar; bu, hedef URL'ye göre dinamik olarak proxy seçmeyi mümkün kılar. Bu, HttpURLConnection ve Apache HttpClient ile çalışır:
ProxySelector.setDefault(new ProxySelector() {
private final List<Proxy> proxyPool = Arrays.asList(
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy1.example.com", 8080)),
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy2.example.com", 8080)),
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy3.example.com", 8080))
);
private final AtomicInteger idx = new AtomicInteger(0);
@Override
public List<Proxy> select(URI uri) {
// Alan adına göre seçim mantığı eklenebilir
int i = idx.getAndIncrement() % proxyPool.size();
return Collections.singletonList(proxyPool.get(i));
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
// Erişilemeyen proxy için işleme mantığı
log.warn("Proxy failed: {}", sa);
}
});
Java görevleri için hangi proxy türünü seçmeli
Proxy türü seçimi, belirli bir göreve bağlıdır. Kurumsal Java uygulamaları için genellikle üç ana tür düşünülmektedir:
| Proxy Türü | Hız | Anonimlik | En iyi kullanım alanı |
|---|---|---|---|
| Veri merkezi | Yüksek | Orta | Açık veri ayrıştırma, API test etme, yüksek yük |
| Konut | Orta | Yüksek | Korunan sitelerden veri ayrıştırma, coğrafi test, engelleri aşma |
| Mobil | Orta | Maksimum | Mobil API'lerle entegrasyon, sıkı anti-fraud korumalı hizmetler |
Görev türleri için pratik öneriler:
- Kurumsal geçit (Squid, Zscaler): Proxy türü IT departmanı tarafından belirlenir. Apache HttpClient veya JVM sistem özellikleri aracılığıyla kimlik doğrulamalı HTTP proxy kullanın.
- Açık veri ayrıştırma (haberler, alıntılar, açık API'ler): Veri merkezi proxy'leri, hız ve maliyet açısından en iyi seçimdir.
- E-ticaret ayrıştırma (Wildberries, Ozon, Amazon): Döngü ile konut proxy'leri. Bu platformlar, veri merkezi IP'lerini aktif olarak engeller.
- Coğrafi test (yerelleştirme kontrolü, bölgelere göre fiyatlar): Ülke/şehir seçimi ile konut proxy'leri.
- Mobil hizmetler ve uygulamalarla entegrasyon: Mobil proxy'ler — gerçek mobil ağlardan IP'ler, gerçek kullanıcıları en iyi şekilde taklit eder.
Java'da proxy ayarlarken sık karşılaşılan hatalar ve nasıl düzeltileceği
Deneyimli geliştiriciler bile Java'da proxy ayarlarken aynı sorunlarla karşılaşabilir. En yaygın sorunları inceleyelim.
1. Proxy ayarlandı, ancak trafik yine de doğrudan gidiyor
Sebep: JVM sistem özelliklerini ayarladınız, ancak OkHttp veya Reactor Netty (WebClient) kullanıyorsunuz, bu da onları görmezden geliyor.
Çözüm: Proxy'yi açıkça belirli bir HTTP istemcisinin yapılandırmasında ayarlayın, yukarıdaki bölümlerde gösterildiği gibi.
2. HTTPS üzerinden proxy ile javax.net.ssl.SSLHandshakeException
Sebep: Kurumsal proxy, SSL denetimi (MITM) yaparak sertifikayı değiştirir. Java, kurumsal CA'ya güvenmez.
Çözüm: Kurumsal sertifikayı Java truststore'a içe aktarın:
keytool -import -trustcacerts \ -alias corporate-ca \ -file corporate-ca.crt \ -keystore $JAVA_HOME/lib/security/cacerts \ -storepass changeit
3. java.net.ConnectException: Bağlantı reddedildi
Sebep: Proxy'nin yanlış host veya portu; proxy sunucusu erişilemez; güvenlik duvarı bağlantıyı engelliyor.
Tanı:
# Proxy'nin erişilebilirliğini kontrol etme curl -x http://proxy.example.com:8080 https://httpbin.org/ip # Ya da telnet ile telnet proxy.example.com 8080
4. Proxy HTTP için çalışıyor, ancak HTTPS için çalışmıyor
Sebep: Sadece http.proxyHost/Port özellikleri ayarlandı, ancak https.proxyHost/Port ayarlanmadı.
Çözüm: Her iki özellik setini de ayarlayın. HTTPS üzerinden HTTP proxy ile CONNECT tünelleme yöntemi kullanılır — proxy'nin bunu desteklediğinden emin olun.
5. 407 Proxy Kimlik Doğrulaması Gerekiyor
Sebep: Proxy kimlik doğrulaması gerektiriyor, ancak ayarlanmamış.
Çözüm: JVM sistem özellikleri için Authenticator ekleyin veya Apache HttpClient için CredentialsProvider veya OkHttp için proxyAuthenticator ayarlayın (örnekler yukarıda).
6. Proxy döngüsünde bellek sızıntısı
Sebep: Her istekte yeni bir OkHttpClient örneği oluşturuluyor. Her istemci kendi bağlantı ve thread havuzunu tutar.
Çözüm: İstemcileri yeniden kullanın. Birkaç istemciden (her proxy için bir tane) oluşan bir havuz oluşturun ve bunları döndürün, yeni istemciler oluşturmayın.
7. Tanılama için hata ayıklama kaydını etkinleştirme
# application.properties — proxy tanılama için logları etkinleştir logging.level.org.apache.http=DEBUG logging.level.org.apache.http.wire=DEBUG logging.level.reactor.netty.http.client=DEBUG # JVM için — sistem özelliği -Djava.net.debug=all
Sonuç
Java uygulamalarında proxy ayarlamak, arka planda hangi HTTP istemcisinin kullanıldığını anlamayı gerektirir. JVM sistem özellikleri, HttpURLConnection için hızlı bir başlangıçtır, ancak OkHttp, WebClient ve Apache HttpClient açık bir yapılandırma gerektirir. Kurumsal uygulamalar için proxy parametrelerini application.properties dosyasına taşımak ve farklı ortamlar için Spring profilleri kullanmak önerilir. Proxy döngüsünü, döngüsel uç noktaya sahip bir sağlayıcıya devretmek — bunu kendiniz uygulamaktan daha kolay ve güvenilir.
Eğer Java uygulamanız korunan platformlardan veri ayrıştırıyorsa veya dış isteklere yüksek anonimlik gerektiriyorsa, konut proxy'lerini değerlendirmenizi öneririz — bunlar gerçek ev kullanıcılarının IP'lerine sahiptir, bu da anti-fraud sistemleri tarafından engellenme olasılığını önemli ölçüde azaltır. Yüksek hacimli açık veri ayrıştırma için en iyi seçenek veri merkezi proxy'leridir — bunlar, daha düşük trafik maliyeti ile maksimum hız sağlar.
```