Spring Boot HTTP 통신 작업(Feat. TreadPool)

최민길(Gale)·2023년 2월 21일
1

Spring Boot 적용기

목록 보기
18/46

안녕하세요 오늘은 Spring Boot에서 다른 서버와 통신할 수 있는 방법에 대해 알아보고 이를 구현해보는 내용에 대해 포스팅하도록 하겠습니다.

Spring Boot에서 HTTP 통신을 하는 방법은 크게 3가지입니다.

  1. HttpURLConnection
  2. HttpClient
  3. RestTemplate

HttpURLConnection의 경우 Java 자체적으로 HTTP 통신을 진행하는 클래스입니다. 반면 HttpClient는 Apache 기반으로 HTTP 통신을 진행하게 하는 클래스입니다. RestTemplate의 경우 HttpClient를 추상화하여 제공하는 클래스입니다.

링크 : http://web.mit.edu/~mkgray/project/bha/HTTPClient/doc/urlcon_vs_httpclient.html

다음 표에서 HttpURLConnection과 HttpClient의 장단점이 나타납니다. 전반적으로 HttpClient 경우 다양한 기능들을 제공하여 사용성에 포커스가 맞춰진 것에 비해 HttpURLConnection의 경우 기능이 일부 부족하지만 상대적으로 가볍다는 장점이 있습니다. 저는 Apache를 기반으로 안정적으로 돌아가며 여러 기능들을 제공하는 HttpClient를 사용하기로 하였습니다. 현재는 알림 서버와의 통신만 진행하기 때문에 HttpClient를 추상화할 필요성을 느끼지 못해 추후 통신 서버가 증가할 경우 RestTemplate 방식을 적용할 예정입니다. 마찬가지로 추후 성능 이슈가 발생할 경우 HttpURLConnection 역시 고려하여 테스트해볼 예정입니다.

...
        // DB 접근
        Long postID = postRepository.addPost(
	        ...
        );

        // 알림 발송
        Long alarmLogID = alarmService.sendAlarmToAddPostMembersService(
                ...,
                postID
		);
...

다음은 게시글 작성 코드의 일부입니다. DB에 접근하여 요청받은 데이터를 저장한 후 얻은 게시글 ID값을 이용하여 푸시 알림을 발송합니다.

    public Long sendAlarmToAddPostMembersService( ..., Long postID) {
        // 알림 발송 명단 가져오기
        ...
        
        // 알림 발송 데이터로 가공
        ...
        
        // 알림 발송
        sendAlarmServer("알림 서버 URL",data);
        ...
    }

다음은 위에서 알림 발송 시 사용한 메소드인 sendAlarmToAddPostMembersService 내부입니다. 알림 발송 명단을 가져온 후 가공하여 알림을 발송합니다.

    @Autowired
    private TaskExecutor executor;
    
    public void sendAlarmServer(String uri, Object body){
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try{
                    String url = alarmServerDomain + uri;
                    HttpClient client = HttpClientBuilder.create().build();
                    HttpPost postRequest = new HttpPost(url);

                    ObjectMapper mapper = new ObjectMapper();
                    postRequest.setHeader("Content-Type",contentType);
                    postRequest.setHeader("Authorization",token);
                    postRequest.setEntity(new StringEntity(mapper.writeValueAsString(body)));

                    client.execute(postRequest);
                }
                catch (Exception e){
                    logger.info("FCM 발송 오류");
                }
            }
        });
    }

다음은 알림 서버로 HTTP 통신을 진행하는 sendAlarmServer 메소드 내부입니다. 알림 서버와 Post 메소드로 통신을 진행하기 때문에 HttpPost 객체를 생성하였고 ObjectMapper 클래스에 데이터를 넣어 HttpPost 객체에 세팅합니다. 이후 HttpClient 객체에 HttpPost 객체를 넣어 실행시켜 HTTP 통신을 진행합니다.

@Configuration
public class ThreadPoolConfig {
    // 서버 코어 수만큼 할당
    @Value("${spring.task.execution.pool.core-size}")
    int CORE_POOL_SIZE;

    // 스레드 풀 최대 스레드 할당 수
    @Value("${spring.task.execution.pool.max-size}")
    int MAX_POOL_SIZE;

    @Bean
    public TaskExecutor taskExecutor(){
        // ThreadPoolTaskExecutor 생성
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        executor.setCorePoolSize(CORE_POOL_SIZE);
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.initialize();
        return executor;
    }
}

위의 코드에서 주목할 부분은 TaskExecutor를 이용하여 알림 발송 메소드를 실행시킨 것입니다. 다른 서버와 HTTP 통신을 진행하여 데이터를 가져오는 작업은 무겁기 때문에 더 많은 스레드를 필요로 합니다. 이 때 스레드 재생성 및 제거에 소요되는 리소스를 최소화하며 더 많은 스레드가 해당 작업을 처리할 수 있도록 ThreadPool을 이용하여 처리합니다. 이를 별도 Configuration 클래스에서 Bean을 주입시켜 싱글톤 패턴으로 최소한의 객체만 생성하도록 처리했습니다.

threadpool 적용

2023-02-21T17:38:11.123+09:00  INFO 6263 --- [    Test worker] .a.a.MethodInvocationProceedingJoinPoint : -----------> RESPONSE : 패키지 경로 = <200 OK OK,패키지 경로.ResponseDTO@eb77241,[]> (253ms)


threadpool 미적용

2023-02-21T17:40:59.016+09:00  INFO 6614 --- [    Test worker] .a.a.MethodInvocationProceedingJoinPoint : -----------> RESPONSE : 패키지 경로 = <200 OK OK,패키지 경로.ResponseDTO@589cc8eb,[]> (934ms)

위의 실행 결과는 각각 ThreadPool을 적용 및 미적용 시 API 실행 속도 측정값입니다. 보시는 것과 같이 ThreadPool을 적용 시 약 370%의 성능 향상이 일어난 것을 확인할 수 있습니다.

앞서 말씀드렸던 것처럼 추후 HttpURLConnection 또는 RestTemplate에 대해서도 조금 더 공부하여 적용해보는 시간을 가져보도록 하겠습니다. 긴 글 읽어주셔서 감사합니다!

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글