httpClient 수정

This commit is contained in:
cho
2026-02-12 14:15:07 +09:00
parent 86ccc7047a
commit d1c2b75e18
3 changed files with 131 additions and 101 deletions
@@ -132,7 +132,7 @@ public class AlarmStateManager {
payload = UmsRequestPayload.builder() payload = UmsRequestPayload.builder()
.content(ObpMonitoringUmsTemplate.builder().message(event.getMessage()).build()) .content(ObpMonitoringUmsTemplate.builder().message(event.getMessage()).build())
.cellphone(cellPhone).build(); .cellphone(cellPhone).build();
logger.info("Alarm is disabled, UmsRequestPayload = {}", payload);
usmService.send(payload); usmService.send(payload);
} }
@@ -1,6 +1,7 @@
package com.eactive.eai.custom.alarm.ums; package com.eactive.eai.custom.alarm.ums;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@@ -49,7 +50,17 @@ public class UmsSendManager {
void sendMessage() throws Exception { void sendMessage() throws Exception {
UmsRequestPayload payload; UmsRequestPayload payload;
while ((payload = queue.poll()) != null) { while ((payload = queue.poll()) != null) {
umsService.send(payload); final UmsRequestPayload finalPayload = payload;
CompletableFuture.runAsync(() -> {
UmsCallResult result = null;
try {
result = umsService.send(finalPayload);
} catch (Exception e) {
logger.error("UMS 전송 실패", e);
}finally {
logger.info(result);
}
});
} }
} }
@@ -1,135 +1,154 @@
package com.eactive.eai.custom.alarm.ums; package com.eactive.eai.custom.alarm.ums;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.ExecutorService; import javax.annotation.PreDestroy;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.entity.EntityBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout; import org.apache.hc.core5.util.Timeout;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import com.eactive.eai.common.property.PropManager; import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Jsons; import com.eactive.eai.common.util.Jsons;
import com.eactive.eai.common.util.Logger; import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.alarm.ums.payload.UmsRequestPayload; import com.eactive.eai.custom.alarm.ums.payload.UmsRequestPayload;
import com.eactive.eai.custom.alarm.ums.payload.ValidationUtil; import com.eactive.eai.custom.alarm.ums.payload.ValidationUtil;
import com.google.gson.JsonSyntaxException;
@Component @Component
public class UmsService { public class UmsService {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT); protected static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private final static String UMS_GROUP_PROP_NAME = "UMS"; // 설정 상수
private final static String UMS_PROP_HOST = "host"; private final static String UMS_GROUP_PROP_NAME = "UMS";
private final static String UMS_PROP_URL = "url"; private final static String UMS_PROP_HOST = "host";
private final static String UMS_PROP_API_KEY = "apiKey"; private final static String UMS_PROP_URL = "url";
private final static String UMS_URL = "http://172.31.35.144:9021"; private final static String UMS_PROP_API_KEY = "apiKey";
private final static String API_KEY = "7cca6d58-e4f7-474d-9edd-525806bc99ff"; private final static String UMS_PROP_CONNECTION_TIMEOUT = "timeout.connection.ms";
private final static String SEND_URL = "/ums/openapi/send"; private final static String UMS_PROP_TIMEOUT = "timeout.ms";
private final static String CONTENT_TYPE = "application/json; charset=utf-8";
private final static String AUTHORIZATION_FMT = "Bearer %s"; // 기본값
private final static String DEFAULT_UMS_URL = "http://172.31.35.144:9021";
private final static String DEFAULT_SEND_URI = "/ums/openapi/send";
private final static String DEFAULT_API_KEY = "7cca6d58-e4f7-474d-9edd-525806bc99ff";
private final static String AUTHORIZATION_FMT = "Bearer %s";
public Future<UmsCallResult> send(UmsRequestPayload payload) throws Exception { // 재사용되는 HttpClient (Thread-Safe)
private final CloseableHttpClient httpClient;
if (payload.getContent() == null) { public UmsService() {
throw new IllegalArgumentException("content is null"); // 커넥션 풀 설정: 매번 소켓을 열고 닫지 않고 재사용함
} HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
.setMaxConnTotal(100) // 전체 최대 커넥션 수
.setMaxConnPerRoute(20) // 호스트당 최대 커넥션 수
.setConnectionTimeToLive(TimeValue.ofMinutes(3)) // TTL
.setValidateAfterInactivity(TimeValue.ofSeconds(5)) // 🔥 중요
.build();
try { this.httpClient = HttpClients.custom()
ValidationUtil.validateOrThrow(payload); .setConnectionManager(cm)
} catch (Exception ex) { .evictExpiredConnections() // 만료 제거
logger.debug(String.format("UMS Request payload validation failed - %s", payload)); .evictIdleConnections(TimeValue.ofSeconds(20)) // 20초 idle 제거
throw ex; .build();
} }
return call(payload); public UmsCallResult send(UmsRequestPayload payload) throws Exception {
} if (payload.getContent() == null) {
throw new IllegalArgumentException("content is null");
}
private CloseableHttpClient createHttpClient() { try {
return HttpClients.createDefault(); ValidationUtil.validateOrThrow(payload);
} } catch (Exception ex) {
logger.debug(String.format("UMS Request payload validation failed - %s", payload));
throw ex;
}
private Future<UmsCallResult> call(UmsRequestPayload requestPayload) throws Exception { return call(payload);
}
ExecutorService executor = Executors.newSingleThreadExecutor(); private UmsCallResult call(UmsRequestPayload requestPayload) throws Exception {
// 1. 프로퍼티 로드
String json = Jsons.GSON.toJson(requestPayload); PropManager prop = PropManager.getInstance();
String host = prop.getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_HOST, DEFAULT_UMS_URL);
String host = PropManager.getInstance().getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_HOST, UMS_URL); String uri = prop.getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_URL, DEFAULT_SEND_URI);
String url = PropManager.getInstance().getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_URL, SEND_URL); String apiKey = prop.getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_API_KEY, DEFAULT_API_KEY);
String apiKey = PropManager.getInstance().getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_API_KEY, API_KEY); long connectionTimeoutMs = Long.parseLong(prop.getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_CONNECTION_TIMEOUT, "3000"));
long timeoutMs = Long.parseLong(prop.getProperty(UMS_GROUP_PROP_NAME, UMS_PROP_TIMEOUT, "3000"));
// 2. 요청 생성
HttpPost httpPost = new HttpPost(host + uri);
httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, apiKey));
String json = Jsons.GSON.toJson(requestPayload);
httpPost.setEntity(EntityBuilder.create()
.setText(json)
.setContentType(ContentType.APPLICATION_JSON)
.setContentEncoding("UTF-8")
.build());
HttpPost httpPost = new HttpPost(host + url); // 3. 타임아웃 설정 (설정파일 값 적용)
httpPost.setHeader("Content-Type", CONTENT_TYPE); RequestConfig config = RequestConfig.custom()
httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, apiKey)); .setConnectTimeout(Timeout.ofMilliseconds(connectionTimeoutMs))
httpPost.setEntity(EntityBuilder.create().setText(json).setContentType(ContentType.APPLICATION_JSON).build()); .setResponseTimeout(Timeout.ofMilliseconds(timeoutMs))
.build();
httpPost.setConfig(config);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
try { logger.debug("UMS Request to: {}", host + uri);
logger.debug("Header - {}", httpPost.getHeader("Content-Type")); logger.debug("UMS Request Body: {}", json);
logger.debug("Header - {}", httpPost.getHeader("Authorization")); }
logger.debug("Request Body : {}",
StreamUtils.copyToString(httpPost.getEntity().getContent(), Charset.defaultCharset()));
} catch (ProtocolException | IOException e) {
throw new RuntimeException(e);
}
}
httpPost.setConfig(RequestConfig.custom().setConnectTimeout(Timeout.ofSeconds(3)) // 4. 실행 (HttpClient 재사용)
.setResponseTimeout(Timeout.ofSeconds(3)).build()); try {
Future<UmsCallResult> future; return httpClient.execute(httpPost, response -> {
try (CloseableHttpClient httpClient = createHttpClient()) { String body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
future = executor.submit(() -> {
return httpClient.execute(httpPost, res -> { if (logger.isDebugEnabled()) {
try { logger.debug("UMS Response Status: {}", response.getCode());
UmsCallResult result; logger.debug("UMS Response Body: {}", body);
String body = EntityUtils.toString(res.getEntity()); }
if (logger.isDebugEnabled()) {
logger.debug("Response - {}", body);
}
result = Jsons.GSON.fromJson(body, UmsCallResult.class);
result.setUmsMessageId(requestPayload.getMessageIdentityNo());
result.setSuccess(true);
return result;
} catch (JsonSyntaxException e) {
logger.error("UMS Call error(JSON Syntax)", e);
return UmsCallResult.builder().umsMessageId(requestPayload.getMessageIdentityNo())
.isSuccess(false).throwable(e).build();
} catch (Exception e) {
logger.error("UMS Call error", e);
return UmsCallResult.builder().umsMessageId(requestPayload.getMessageIdentityNo())
.isSuccess(false).throwable(e).build();
} finally {
logger.debug("Response status - {}", res.getCode());
if (res.getCode() != 200) { UmsCallResult result = Jsons.GSON.fromJson(body, UmsCallResult.class);
Arrays.asList(res.getHeaders()).forEach(o -> { result.setUmsMessageId(requestPayload.getMessageIdentityNo());
logger.warn("[Header] {}: {}", o.getName(), o.getValue());
}); // HTTP 200인 경우만 성공으로 간주 (필요에 따라 조정)
} result.setSuccess(response.getCode() == 200);
}
}); if (response.getCode() != 200) {
}); logger.warn("UMS Call failed with status: {}", response.getCode());
}
return result;
});
} catch (Exception e) {
logger.error("UMS call exception: " + e.getMessage(), e);
return UmsCallResult.builder()
.umsMessageId(requestPayload.getMessageIdentityNo())
.isSuccess(false)
.throwable(e)
.build();
}
}
return future; @PreDestroy
} catch (Exception e) { public void destroy() {
logger.error("UMS call exception", e); try {
throw new Exception("UMS call failed", e); if (httpClient != null) {
} finally { httpClient.close();
executor.shutdown(); logger.info("UMS HttpClient closed.");
} }
} catch (IOException e) {
} logger.error("Error closing UMS HttpClient", e);
} }
}
}