Aplikasi Java perusahaan secara teratur mengakses API eksternal, mengurai data, atau beroperasi dalam jaringan perusahaan yang terisolasi — dan dalam setiap kasus ini, cepat atau lambat muncul pertanyaan tentang pengaturan lalu lintas melalui server proxy. Mengatur proxy dalam Java tampak seperti tugas yang sederhana, sampai Anda menghadapi kenyataan bahwa RestTemplate, OkHttp, dan WebClient berperilaku berbeda, dan properti sistem JVM tidak berfungsi di semua tempat. Dalam panduan ini — contoh kode konkret, penjelasan tentang jebakan, dan rekomendasi untuk memilih jenis proxy untuk berbagai tugas.
Mengapa aplikasi Java memerlukan proxy
Java tetap menjadi salah satu bahasa utama dalam pengembangan perusahaan. Layanan Spring Boot, arsitektur mikroservis, tugas batch, pipeline ETL — semuanya secara teratur melakukan permintaan HTTP ke dunia luar. Dan dalam setiap skenario ini, proxy menyelesaikan tugas tertentu:
- Jaringan perusahaan dengan gateway proxy yang wajib. Di banyak perusahaan, semua lalu lintas keluar melewati proxy perusahaan (Squid, Zscaler, BlueCoat). Tanpa pengaturannya, aplikasi tidak akan dapat mengakses internet.
- Penguraian dan pengumpulan data. Layanan yang mengumpulkan data dari situs eksternal (harga, kutipan, berita) menggunakan proxy untuk menghindari batasan kecepatan dan pemblokiran berdasarkan IP.
- Pengujian geo. Tim QA memeriksa bagaimana API atau situs berperilaku dari berbagai wilayah — proxy memungkinkan untuk meniru permintaan dari negara yang diinginkan.
- Menghindari batasan di sisi API target. Beberapa API eksternal memiliki batasan pada jumlah permintaan dari satu IP. Rotasi proxy memungkinkan untuk mendistribusikan beban.
- Keamanan dan anonimisasi. Menyembunyikan IP asli aplikasi saat mengakses layanan eksternal — praktik standar untuk integrasi yang sensitif.
Tergantung pada tugasnya, pendekatan untuk mengatur proxy dalam Java sangat bervariasi. Properti sistem global JVM adalah pilihan paling sederhana, tetapi tidak berfungsi dengan beberapa klien HTTP. OkHttp dan WebClient memerlukan konfigurasi eksplisit. Mari kita bahas setiap kasus satu per satu.
Pengaturan global melalui properti sistem JVM
Cara tercepat untuk mengarahkan semua lalu lintas HTTP aplikasi Java melalui proxy adalah dengan menggunakan properti sistem JVM. Ini berfungsi untuk HttpURLConnection standar dan banyak pustaka yang menggunakannya di belakang layar.
Anda dapat mengatur proxy dengan beberapa cara:
1. Melalui argumen JVM saat menjalankan:
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. Melalui kode aplikasi (misalnya, dalam metode main atau dalam @PostConstruct):
System.setProperty("http.proxyHost", "proxy.example.com");
System.setProperty("http.proxyPort", "8080");
System.setProperty("https.proxyHost", "proxy.example.com");
System.setProperty("https.proxyPort", "8080");
// Pengecualian — host yang tidak boleh melalui proxy
System.setProperty("http.nonProxyHosts", "localhost|127.0.0.1|*.internal.corp");
3. Melalui file application.properties di Spring Boot (dengan penerapan melalui @PostConstruct):
# application.properties proxy.host=proxy.example.com proxy.port=8080 proxy.username=user proxy.password=secret
⚠️ Penting untuk diketahui
Properti sistem JVM hanya berfungsi untuk HttpURLConnection. OkHttp, Apache HttpClient, dan Reactor Netty (WebClient) mengabaikan properti ini — mereka memerlukan konfigurasi eksplisit, yang dijelaskan di bawah ini.
Jika proxy memerlukan autentikasi, gunakan Authenticator:
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication("username", "password".toCharArray());
}
return null;
}
});
Proxy di Spring Boot: RestTemplate dan Apache HttpClient
RestTemplate — klien HTTP sinkron standar di Spring. Secara default, ia menggunakan SimpleClientHttpRequestFactory, yang bergantung pada HttpURLConnection dan membaca properti sistem JVM. Namun, untuk pengaturan yang lebih fleksibel (timeout, kolam koneksi, proxy dengan autentikasi), disarankan untuk menggunakan Apache HttpClient sebagai backend.
Dependensi di Maven:
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
Konfigurasi RestTemplate dengan proxy melalui Apache HttpClient 5:
@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);
}
}
Konfigurasi ini memungkinkan untuk memindahkan parameter proxy ke application.properties atau variabel lingkungan, yang sangat penting untuk lingkungan prod/staging/dev dengan proxy yang berbeda.
Jika perlu agar sebagian permintaan melalui proxy, dan sebagian lagi langsung, buat dua bean terpisah RestTemplate dengan nama yang berbeda dan gunakan @Qualifier untuk injeksi yang diperlukan.
Proxy di Spring WebClient (tumpukan reaktif)
WebClient — klien HTTP reaktif Spring, dibangun di atas Reactor Netty. Ia tidak membaca properti sistem JVM untuk proxy — pengaturan dilakukan melalui ProxyProvider saat membuat klien.
Dependensi (termasuk dalam spring-boot-starter-webflux):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Konfigurasi WebClient dengan proxy HTTP:
@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();
}
}
Untuk proxy SOCKS5 di WebClient:
HttpClient httpClient = HttpClient.create()
.proxy(proxy -> proxy
.type(ProxyProvider.Proxy.SOCKS5)
.host("socks5.proxy.example.com")
.port(1080)
.username("user")
.password(user -> "password")
);
💡 Tips untuk konfigurasi
Dalam aplikasi reaktif, sering kali perlu membuat beberapa instance WebClient dengan proxy yang berbeda untuk layanan eksternal yang berbeda. Gunakan WebClient.Builder sebagai bean prototipe dan override pengaturan melalui .mutate().
Pengaturan proxy di OkHttp
OkHttp — pustaka HTTP populer yang digunakan oleh banyak aplikasi Java dan Android, serta beberapa integrasi Spring (misalnya, Feign dengan backend OkHttp). Ia memiliki mekanisme pengaturan proxy sendiri dan juga tidak membaca properti sistem JVM.
Pengaturan dasar proxy HTTP/HTTPS di OkHttp:
import okhttp3.*;
import java.net.InetSocketAddress;
import java.net.Proxy;
// Membuat proxy
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress("proxy.example.com", 8080)
);
// Autentikasi proxy
Authenticator proxyAuthenticator = (route, response) -> {
String credential = Credentials.basic("username", "password");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
};
// Membangun klien
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator(proxyAuthenticator)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
// Contoh permintaan
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());
}
Integrasi OkHttp dengan Spring Boot melalui 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();
}
}
Perhatikan @ConditionalOnProperty — ini memungkinkan untuk mengaktifkan proxy hanya di lingkungan yang diperlukan melalui konfigurasi, tanpa mengubah kode.
Jika Anda menggunakan OkHttp bersama dengan proxy residensial untuk mengurai data, penting untuk mengatur kolam koneksi dengan benar. Proxy residensial sering memiliki latensi yang lebih tinggi dibandingkan dengan data center, jadi tingkatkan timeout dan kurangi ukuran kolam untuk tidak kehabisan koneksi yang tersedia.
Proxy SOCKS5 di Java: fitur dan contoh
SOCKS5 adalah protokol yang lebih rendah dibandingkan dengan proxy HTTP. Ia beroperasi di tingkat transport dan mendukung semua protokol (HTTP, HTTPS, FTP, dll.), serta resolusi DNS di sisi proxy. Untuk aplikasi Java, SOCKS5 sangat berguna ketika perlu memproxy tidak hanya HTTP, tetapi juga koneksi TCP lainnya.
SOCKS5 melalui properti sistem JVM:
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");
SOCKS5 di OkHttp (melalui java.net.Proxy):
Proxy socks5Proxy = new Proxy(
Proxy.Type.SOCKS,
new InetSocketAddress("socks5.proxy.example.com", 1080)
);
OkHttpClient client = new OkHttpClient.Builder()
.proxy(socks5Proxy)
.build();
Fitur penting SOCKS5 dan DNS: Secara default, Java meresolusi DNS secara lokal, dan melalui proxy mengirimkan alamat IP. Ini dapat mengungkapkan permintaan DNS yang sebenarnya. Untuk memastikan resolusi DNS juga melalui proxy, gunakan pendekatan berikut dengan OkHttp:
// DNS kustom yang memaksa resolusi melalui SOCKS5
OkHttpClient client = new OkHttpClient.Builder()
.proxy(socks5Proxy)
.dns(hostname -> {
// Mengembalikan host yang tidak teresolusi — OkHttp akan mengirimkannya ke proxy
return Collections.singletonList(InetAddress.getByName(hostname));
})
.build();
Kapan memilih SOCKS5 vs HTTP-proxy
| Kriteria | HTTP-proxy | SOCKS5-proxy |
|---|---|---|
| Protokol | HTTP/HTTPS | Semua TCP/UDP |
| DNS melalui proxy | Tergantung pada implementasi | Didukung |
| Kompatibilitas dengan Java | Natif | Natif (java.net.Proxy.Type.SOCKS) |
| Penggunaan | Gateway perusahaan, penguraian web | Anonimisasi, protokol non-standar |
Rotasi proxy dalam aplikasi Java
Jika aplikasi melakukan banyak permintaan ke satu sumber daya, penggunaan satu IP proxy dengan cepat akan mengarah pada pemblokiran. Rotasi proxy — beralih antara alamat IP yang berbeda — menyelesaikan masalah ini. Dalam Java, ini diimplementasikan dengan beberapa cara.
Opsi 1: Rotasi melalui endpoint proxy yang berputar
Pendekatan paling sederhana adalah menggunakan penyedia proxy dengan satu endpoint, yang secara otomatis mengubah IP pada setiap permintaan atau melalui interval yang ditentukan. Anda hanya perlu menentukan satu host dan port, dan rotasi terjadi di sisi penyedia. Tidak perlu menulis kode untuk rotasi.
Opsi 2: Rotasi di sisi aplikasi melalui daftar proxy
@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) {
// Mengurai string dalam format "host:port" atau "user:pass@host:port"
String[] parts = proxyStr.split(":");
return new ProxyConfig(parts[0], Integer.parseInt(parts[1]));
}
}
Opsi 3: Rotasi melalui ProxySelector
Java menyediakan mekanisme bawaan ProxySelector, yang memungkinkan pemilihan proxy secara dinamis berdasarkan URL tujuan. Ini berfungsi dengan HttpURLConnection dan Apache HttpClient:
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) {
// Anda dapat menambahkan logika pemilihan berdasarkan domain
int i = idx.getAndIncrement() % proxyPool.size();
return Collections.singletonList(proxyPool.get(i));
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
// Logika penanganan proxy yang tidak tersedia
log.warn("Proxy gagal: {}", sa);
}
});
Jenis proxy mana yang harus dipilih untuk tugas Java
Pemilihan jenis proxy tergantung pada tugas spesifik. Untuk aplikasi Java perusahaan, biasanya ada tiga jenis utama yang dipertimbangkan:
| Jenis proxy | Kecepatan | Anonimitas | Terbaik untuk |
|---|---|---|---|
| Data center | Tinggi | Sedang | Penguraian data publik, pengujian API, beban tinggi |
| Residen | Sedang | Tinggi | Penguraian situs yang dilindungi, pengujian geo, menghindari pemblokiran |
| Mobile | Sedang | Maksimal | Integrasi dengan API mobile, layanan dengan perlindungan anti-fraud yang ketat |
Rekomendasi praktis untuk jenis tugas:
- Gateway perusahaan (Squid, Zscaler): Jenis proxy ditentukan oleh departemen TI. Gunakan HTTP-proxy dengan autentikasi melalui Apache HttpClient atau properti sistem JVM.
- Penguraian data publik (berita, kutipan, API terbuka): Proxy data center — pilihan optimal untuk rasio kecepatan dan biaya.
- Penguraian e-commerce (Wildberries, Ozon, Amazon): Proxy residensial dengan rotasi. Platform ini aktif memblokir IP data center.
- Pengujian geo (memeriksa lokalisasi, harga berdasarkan wilayah): Proxy residensial dengan pemilihan negara/kota.
- Integrasi dengan layanan dan aplikasi mobile: Proxy mobile — IP dari jaringan mobile nyata paling baik meniru pengguna nyata.
Kesalahan umum saat mengatur proxy di Java dan cara memperbaikinya
Bahkan pengembang berpengalaman menghadapi masalah yang sama saat mengatur proxy di Java. Mari kita bahas yang paling umum.
1. Proxy sudah diatur, tetapi lalu lintas tetap langsung
Penyebab: Anda telah mengatur properti sistem JVM, tetapi menggunakan OkHttp atau Reactor Netty (WebClient), yang mengabaikannya.
Solusi: Atur proxy secara eksplisit dalam konfigurasi klien HTTP tertentu, seperti yang ditunjukkan di bagian di atas.
2. javax.net.ssl.SSLHandshakeException saat HTTPS melalui proxy
Penyebab: Proxy perusahaan melakukan inspeksi SSL (MITM), mengganti sertifikat. Java tidak mempercayai CA perusahaan.
Solusi: Impor sertifikat perusahaan ke truststore Java:
keytool -import -trustcacerts \ -alias corporate-ca \ -file corporate-ca.crt \ -keystore $JAVA_HOME/lib/security/cacerts \ -storepass changeit
3. java.net.ConnectException: Koneksi ditolak
Penyebab: Host atau port proxy salah; server proxy tidak tersedia; firewall memblokir koneksi.
Diagnosis:
# Memeriksa ketersediaan proxy curl -x http://proxy.example.com:8080 https://httpbin.org/ip # Atau melalui telnet telnet proxy.example.com 8080
4. Proxy berfungsi untuk HTTP, tetapi tidak untuk HTTPS
Penyebab: Hanya properti http.proxyHost/Port yang diatur, tetapi tidak https.proxyHost/Port.
Solusi: Atur kedua set properti. Untuk HTTPS melalui proxy HTTP, metode tunneling CONNECT digunakan — pastikan proxy mendukungnya.
5. 407 Proxy Authentication Required
Penyebab: Proxy memerlukan autentikasi, tetapi tidak diatur.
Solusi: Tambahkan Authenticator untuk properti sistem JVM atau atur CredentialsProvider untuk Apache HttpClient / proxyAuthenticator untuk OkHttp (contoh di atas).
6. Kebocoran memori saat rotasi proxy
Penyebab: Setiap permintaan membuat instance baru OkHttpClient. Setiap klien menyimpan kolam thread dan koneksinya sendiri.
Solusi: Gunakan kembali klien. Buat kolam dari beberapa klien (satu untuk setiap proxy) dan rotasi mereka, bukan membuat yang baru.
7. Mengaktifkan logging debug untuk diagnosis
# application.properties — mengaktifkan log untuk diagnosis proxy logging.level.org.apache.http=DEBUG logging.level.org.apache.http.wire=DEBUG logging.level.reactor.netty.http.client=DEBUG # Untuk JVM — properti sistem -Djava.net.debug=all
Kesimpulan
Mengatur proxy dalam aplikasi Java memerlukan pemahaman tentang klien HTTP mana yang digunakan di belakang layar. Properti sistem JVM — awal yang cepat untuk HttpURLConnection, tetapi OkHttp, WebClient, dan Apache HttpClient memerlukan konfigurasi eksplisit. Untuk aplikasi perusahaan, disarankan untuk memindahkan parameter proxy ke application.properties dan menggunakan profil Spring untuk lingkungan yang berbeda. Rotasi proxy sebaiknya didelegasikan kepada penyedia dengan endpoint yang berputar — ini lebih mudah dan lebih andal daripada mengimplementasikannya sendiri.
Jika aplikasi Java Anda melakukan penguraian data dari platform yang dilindungi atau memerlukan anonimitas tinggi saat melakukan permintaan eksternal, kami merekomendasikan untuk mempertimbangkan proxy residensial — mereka memiliki IP nyata dari pengguna rumah, yang secara signifikan mengurangi kemungkinan pemblokiran oleh sistem anti-fraud. Untuk penguraian data publik yang sangat tinggi, proxy data center adalah pilihan optimal — mereka memberikan kecepatan maksimum dengan biaya lalu lintas yang lebih rendah.
```