Wookim

spring Async(비동기, 멀티스레드) 메소드 적용하기(2) 본문

programming language/Java

spring Async(비동기, 멀티스레드) 메소드 적용하기(2)

개발자인 경우 2021. 5. 20. 12:54

지난 시간에 간단하게 주의점과 적용 레벨에 대해 알아보았다.

https://wookim789.tistory.com/52

 

spring Async(비동기, 멀티스레드) 메소드 적용하기(1)

스프링 비동기 메소드 적용하기 스프링에 비동기 메소드를 적용해보자. 적용 이유 더보기 특정 데이터를 매우 비효율적으로 조회하여 특정 로직을 태워야 하는 상황이다. 가능 하다면 다른 방

wookim789.tistory.com

 

이번 시간엔 구체적인 내용과 예제 코드를 통해 공부해 보자.

 

 


설정 파일 작업하기

지난 시간에 메소드를 비동기 처리하기 위한 레벨에 대해 설명했다.

2가지 레벨에 대해 각각의 가장 기본적인 설정 파일들을 작성해 보자.

 

1. method 단위 설정 파일

@Configuration
@EnableAsync
public class SpringAsyncConfig {
    
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }
}

https://www.baeldung.com/spring-async

 

위와 같이 굉장히 기본적인 설정파일을 하나 생성했다.

어노테이션부터 살펴보자.

 

@Configuration

클래스가 하나 이상의 @Bean 메소드를 선언하고

Spring 컨테이너가 런타임에 Bean 정의 및 서비스 요청을 생성하기 위해 처리 할 수 있음을 나타냅니다.

출처 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html

 

옛날에는 bean 객체들을 xml 파일에 관리 했는데,

해당 어노테이션을 이용하면 xml이 아닌 java파일로 bean을 등록 및 설정 관리할수 있다. (by wookim)

 

@EnableAsync

Spring의 <task : *> XML 네임 스페이스에있는 

기능과 유사한 Spring의 비동기 메서드 실행 기능을 활성화합니다.


다음과 같이 @Configuration 클래스와 함께 사용하여 

전체 Spring 애플리케이션 컨텍스트에 대한 주석 기반 비동기 처리를 가능하게합니다.

출처 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html

 

간단하게 설명하자면 해당 설정 파일에 비동기 메소드 실행기능을 사용하겠다고 명시하는것! (by wookim)

 

@Bean(name = "threadPoolTaskExecutor")

JavaConfig가 이러한 메소드를 발견하면 해당 메소드를 실행하고 

반환 값을 BeanFactory 내에서 Bean으로 등록합니다. 

기본적으로 Bean 이름은 메소드 이름과 동일합니다 

출처 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html

 

Bean (Spring Framework 5.3.7 API)

The optional name of a method to call on the bean instance upon closing the application context, for example a close() method on a JDBC DataSource implementation, or a Hibernate SessionFactory object. The method must have no arguments but may throw any exc

docs.spring.io

해당 어노테이션이 적용된 메소드가 리턴하는 객체를 스프링에서 관리하는 Bean 객체로 등록하겠다고 보면 된다.

name은 빈 객체의 이름을 직접 설정하고 싶을 때 사용하면 된다. (by wookim)

 

Executor

이 인터페이스는 스레드 사용, 예약 등에 대한 세부 정보를 포함하여 각 작업이 실행되는 방식의 메커니즘에서 작업 제출을 분리하는 방법을 제공합니다. 

일반적으로 스레드를 명시 적으로 생성하는 대신 Executor가 사용됩니다. (new Thread x)


일반적으로 작업은 호출자의 스레드가 아닌 다른 스레드에서 실행됩니다.
많은 Executor 구현은 작업을 예약하는 방법과시기에 대해 일종의 제한을 부과합니다. (비동기 실행)


그러나 Executor 인터페이스는 반드시 비동기 실행을 요구하지는 않습니다. 

가장 간단한 경우 실행자는 제출 된 작업을 호출자의 스레드에서 즉시 실행할 수 있습니다. (동기 실행도 가능)

이 패키지에 제공된 Executor 구현은 보다 광범위한 인터페이스 인 ExecutorService를 구현합니다.

ThreadPoolExecutor 클래스는 확장 가능한 스레드 풀 구현을 제공합니다.

Executors 클래스는 이러한 Executors에 편리한 팩토리 메서드를 제공합니다.

출처 https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html

 

ThreadPoolTaskExecutor (Spring Framework 5.3.7 API)

JavaBean that allows for configuring a ThreadPoolExecutor in bean style (through its "corePoolSize", "maxPoolSize", "keepAliveSeconds", "queueCapacity" properties) and exposing it as a Spring TaskExecutor. This class is also well suited for management and

docs.spring.io

해석이 좀 난해하다. 중요한 점만 집고 넘어가자

1. 직접 Thread 인스턴스를 만들지 말고 Excutor 인터페이스와 그 구현체를 이용하자.

2. 메소드를 호출한 스레드에서 작업을 처리 가능하다. (단일 스레드 - 동기)

3. 메소드를 호출한 스레드와 별개의 스레드에서 처리 가능하다. (다중 스레드 - 비동기)

4. 스레드에 대한 다양한 설정을 할 수 있다.

(by wookim)

 

 

이렇게 메소드 단위 설정을 마쳤고 

사용 예제를 살펴 보자

 

@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
    System.out.println("Execute method with configured executor - "
      + Thread.currentThread().getName());
}

@Async("설정 파일의 빈 객체 이름") 

해당 어노테이션은 적용된 메소드가 비동기 메소드로 실행됨을 의미한다.

메소드 단위로 설정했기 때문에 @Async 어노테이션에 설정 파일에서 선언한 빈객체의 이름을 넘겨줘야 한다.

 


 

2. application 단위 설정 파일

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
    
    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }
    
}

 

https://www.baeldung.com/spring-async

 

How To Do @Async in Spring | Baeldung

How to enable and use @Async in Spring - from the very simple config and basic usage to the more complex executors and exception handling strategies.

www.baeldung.com

 

위 소스에서 가장 눈여겨 보아야 하는 것 2가지

 

1. AsyncConfigure 인터페이스를 구현(implements)

2.  getAsyncExcutor 메소드를 재정의(@Override)

 

메소드 단위는 빈객체를 직접 설정했지만 

어플리케이션 단위 레벨에서는 AsyncConfigure 인터페이스와 getAsyncExcutor() 함수를 재정의 했다.

해당 함수는 application 전반적으로 Executor 객체를 리턴한다.

 

어플리케이션 전반적으로 메소드에 @Async 어노테이션을 적용하면 

별다른 설정없이(@Async 어노테이션에 빈객체 이름을 넣는 등) 비동기 호출이 가능하다.

 


Exception Handling

이번엔 에러 핸들링을 하기위한 예제 코드이다.

에러 클래스를 정의하는 이유는 다음과 같다.

 

비동기 메소드가 Futrue 타입을 반환하면 Future.get() 메소드로 예외처리가 간단해진다.

(해당 메소드 호출 시 스레드의 실패 여부에 따라 예외가 발생하는 것으로 예상됨)

 

하지만 비동기 메소드의 리턴 타입이 void이면

호출한 스레드에 비동기 스레드의 예외가 전파되지 않는다. 

 

따라서 아래의 예제와 같은 예외 클래스를 만들어야 한다.

 

public class CustomAsyncExceptionHandler
  implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(
      Throwable throwable, Method method, Object... obj) {
 
        System.out.println("Exception message - " + throwable.getMessage());
        System.out.println("Method name - " + method.getName());
        for (Object param : obj) {
            System.out.println("Parameter value - " + param);
        }
    }
    
}

위 코드의 특징을 보자

 

1. AsyncUncaughtExceptionHandler 인터페이스를 구현했다.

2. hanldeUncaughtException() 메소드를 재정의 했다.

 

hanldeUncaughtException 해당 메소드는 포착되지 않은 비동기 예외가있을 때 호출된다.

 

이제 예외 클래스를 위의 비동기 설정 파일에 메소드 재정의(오버라이드) 해줘야 한다.

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return new CustomAsyncExceptionHandler();
}

 

 

Comments