@Configuration
public class RestClientConfig {
@Bean
public RestTemplate restTemplate() throws Exception {
// 1. SSL 配置 (針對內網自簽憑證環境)
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(null, (chain, authType) -> true)
.build();
// 2. 建立 SSL 連線工廠,忽略 Hostname 驗證
SSLConnectionSocketFactory sslsf = SSLConnectionSocketFactoryBuilder.create()
.setSslContext(sslContext)
.setHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
// 3. 註冊 HTTP 與 HTTPS 處理方式 (建立連線池管理器)
PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(sslsf)
.setMaxConnTotal(500) // 總連線上限
.setMaxConnPerRoute(50) // 單一伺服器(Route)上限,避免單一 Server 出事拖垮 JBoss
.build();
// 4. 建立 HttpClient 並設定閒置清理
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.evictIdleConnections(TimeValue.ofSeconds(30)) // 30秒清理一次閒置連線,釋放 JBoss 埠號
.build();
// 5. 將 HttpClient 封裝進 RestTemplate
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
factory.setConnectTimeout(5000); // 建立連線逾時
factory.setReadTimeout(10000); // 資料傳輸逾時
return new RestTemplate(factory);
}
}
QENvbmZpZ3VyYXRpb24KCnB1YmxpYyBjbGFzcyBSZXN0Q2xpZW50Q29uZmlnIHsKCgoKICAgIEBCZWFuCgogICAgcHVibGljIFJlc3RUZW1wbGF0ZSByZXN0VGVtcGxhdGUoKSB0aHJvd3MgRXhjZXB0aW9uIHsKCiAgICAgICAgLy8gMS4gU1NMIOmFjee9riAo6Yed5bCN5YWn57ay6Ieq57C95oaR6K2J55Kw5aKDKQoKICAgICAgICBTU0xDb250ZXh0IHNzbENvbnRleHQgPSBTU0xDb250ZXh0QnVpbGRlci5jcmVhdGUoKQoKICAgICAgICAgICAgICAgIC5sb2FkVHJ1c3RNYXRlcmlhbChudWxsLCAoY2hhaW4sIGF1dGhUeXBlKSAtPiB0cnVlKQoKICAgICAgICAgICAgICAgIC5idWlsZCgpOwoKCgogICAgICAgIC8vIDIuIOW7uueriyBTU0wg6YCj57ea5bel5bug77yM5b+955WlIEhvc3RuYW1lIOmpl+itiQoKICAgICAgICBTU0xDb25uZWN0aW9uU29ja2V0RmFjdG9yeSBzc2xzZiA9IFNTTENvbm5lY3Rpb25Tb2NrZXRGYWN0b3J5QnVpbGRlci5jcmVhdGUoKQoKICAgICAgICAgICAgICAgIC5zZXRTc2xDb250ZXh0KHNzbENvbnRleHQpCgogICAgICAgICAgICAgICAgLnNldEhvc3RuYW1lVmVyaWZpZXIoTm9vcEhvc3RuYW1lVmVyaWZpZXIuSU5TVEFOQ0UpCgogICAgICAgICAgICAgICAgLmJ1aWxkKCk7CgoKCiAgICAgICAgLy8gMy4g6Ki75YaKIEhUVFAg6IiHIEhUVFBTIOiZleeQhuaWueW8jyAo5bu656uL6YCj57ea5rGg566h55CG5ZmoKQoKICAgICAgICBQb29saW5nSHR0cENsaWVudENvbm5lY3Rpb25NYW5hZ2VyIGNvbm5lY3Rpb25NYW5hZ2VyID0gUG9vbGluZ0h0dHBDbGllbnRDb25uZWN0aW9uTWFuYWdlckJ1aWxkZXIuY3JlYXRlKCkKCiAgICAgICAgICAgICAgICAuc2V0U1NMU29ja2V0RmFjdG9yeShzc2xzZikKCiAgICAgICAgICAgICAgICAuc2V0TWF4Q29ublRvdGFsKDUwMCkgICAgICAgLy8g57i96YCj57ea5LiK6ZmQCgogICAgICAgICAgICAgICAgLnNldE1heENvbm5QZXJSb3V0ZSg1MCkgICAgIC8vIOWWruS4gOS8uuacjeWZqChSb3V0ZSnkuIrpmZDvvIzpgb/lhY3llq7kuIAgU2VydmVyIOWHuuS6i+aLluWeriBKQm9zcwoKICAgICAgICAgICAgICAgIC5idWlsZCgpOwoKCgogICAgICAgIC8vIDQuIOW7uueriyBIdHRwQ2xpZW50IOS4puioreWumumWkue9rua4heeQhgoKICAgICAgICBDbG9zZWFibGVIdHRwQ2xpZW50IGh0dHBDbGllbnQgPSBIdHRwQ2xpZW50cy5jdXN0b20oKQoKICAgICAgICAgICAgICAgIC5zZXRDb25uZWN0aW9uTWFuYWdlcihjb25uZWN0aW9uTWFuYWdlcikKCiAgICAgICAgICAgICAgICAuZXZpY3RJZGxlQ29ubmVjdGlvbnMoVGltZVZhbHVlLm9mU2Vjb25kcygzMCkpIC8vIDMw56eS5riF55CG5LiA5qyh6ZaS572u6YCj57ea77yM6YeL5pS+IEpCb3NzIOWfoOiZnwoKICAgICAgICAgICAgICAgIC5idWlsZCgpOwoKCgogICAgICAgIC8vIDUuIOWwhyBIdHRwQ2xpZW50IOWwgeijnemAsiBSZXN0VGVtcGxhdGUKCiAgICAgICAgSHR0cENvbXBvbmVudHNDbGllbnRIdHRwUmVxdWVzdEZhY3RvcnkgZmFjdG9yeSA9IG5ldyBIdHRwQ29tcG9uZW50c0NsaWVudEh0dHBSZXF1ZXN0RmFjdG9yeShodHRwQ2xpZW50KTsKCiAgICAgICAgZmFjdG9yeS5zZXRDb25uZWN0VGltZW91dCg1MDAwKTsgLy8g5bu656uL6YCj57ea6YC+5pmCCgogICAgICAgIGZhY3Rvcnkuc2V0UmVhZFRpbWVvdXQoMTAwMDApOyAgIC8vIOizh+aWmeWCs+i8uOmAvuaZggoKCgogICAgICAgIHJldHVybiBuZXcgUmVzdFRlbXBsYXRlKGZhY3RvcnkpOwoKICAgIH0KCn0K