Spring @Async Annotation을 활용한 Thread 구현

공부는 혼자하는 거·2021년 8월 21일
1

Spring Tip

목록 보기
2/52

spring에서 Async, 즉 비동기 기능을 사용하는 방법은 아주 간단하다.

  • @EnableAsync로 비동기 기능을 활성화
  • 비동기로 동작을 원하는 메소드(public 메소드)에 @Async 어노테이션을 붙여준다.

개요

Spring Async annotation를 이용하여 간단하게 비동기 처리가 가능하다.@async 를 선언한 메소드를 호출한 호출자는 즉시 리턴하고 실제 실행은 Spring TaskExecutor에 의해 실행.메서드는 Future 타입 값을 리턴하여 해당 Future에 get() 메서드를 이용하여 작업 수행을 할 수 있다.

스프링 TaskExecutor

Executor는 스레드 풀의 개념으로 Java 5에서 도입된 개념. 구현체가 실제 Pool이라고 확신 할 수 없어 Executor(직역: 집행자)라 사용.TaskExecutor 인터페이스는 실행하는 Task를 받고 execute 메서드를 갖는다.

TaskExecutor 종류

  • SimpleAsyncTaskExecutor이 구현에는 어떤 스레드도 재사용하지 않고 호출마다 새로운 스레드를 시작동시접속 제한(concurrency limit)을 지원 제한 수가 넘어서면 빈 공간이 생길 때까지 모든 요청을 block
  • SyncTaskExecutor호출을 비동기적으로 수행하지 않는다. 대신, 각각의 호출은 호출 쓰레드로 대체된다. 간단한 테스트케이스와 같이 필요하지 않은 멀티쓰레드 상황에서 사용된다.
  • ConcurrentTaskExecutorjava.util.concurrent.Executor Wrapper.
  • SimpleThreadPoolTaskExecutorSpring의 생명주기 콜백을 듣는 Quartz의 SimpleThreadPool의 하위클래스.Quartz와 Quartz가 아닌 컴포넌트간에 공유될 필요가 있는 쓰레드 풀
  • ThreadPoolTaskExecutor자바 5에서 가장 일반적으로 사용.java.util.concurrent.ThreadPoolExecutor를 구성하는 bean 프로퍼티를 노출하고 이를 TaskExecutor로 감싼다.
  • TimerTaskExecutor지원되는 구현물 중 하나의 TimerTask를 사용.쓰레드에서 동기적이더라도 메소드 호출이 개별 쓰레드에서 수행되어 SyncTaskExecutor와 다르다.
  • WorkManagerTaskExecutor지원되는 구현물 중 하나로 CommonJ WorkManager을 사용Spring 컨텍스트에서 CommonJ WorkManager참조를 셋팅하기 위한 중심적이고 편리한 클래스SimpleThreadPoolTaskExecutor와 유사하게, 이 클래스는 WorkManager인터페이스를 구현하고 WorkManager만큼 직접 사용

@Async를 활용한 간단한 구현

1. AsyncConfigurer 구현

@Configuration
@EnableAsync
public class Config implements AsyncConfigurer {
   private static int TASK_CORE_POOL_SIZE = 2;
   private static int TASK_MAX_POOL_SIZE = 4;
   private static int TASK_QUEUE_CAPACITY = 10;
   private static String BEAN_NAME = "executorSample";   @Resource(name = "executorSample")
   private ThreadPoolTaskExecutor executorSample;}
  • @Configuration 을 이용한 bean등록
  • EnableAsync를 이용하여 @async를 이용하겠다 알린다.
  • AsyncConfigurer 구현
  • TASK_CORE_POOL_SIZE : 기본 Thread 수
  • TASK_MAX_POOL_SIZE : 최대 Thread 수
  • TASK_QUEUE_CAPACITY : queue 수
  • BEAN_NAME : bean 이름
  • 실행할 테스크의 수는 QUEUE_SIZE+MAX_POOL_SIZE 보다 크면 안된다.
  • POOL생성 과정1. 기본 thread(TASK_CORE_POOL_SIZE) 수까지 순차적으로 쌓인다2. 기본 thread(TASK_CORE_POOL_SIZE) 크기가 넘어 설 경우 queue에 쌓인다3. 큐에 최대치까지 쌓이면TASK_MAX_POOL_SIZE까지 순차적으로 한개씩 증가시킨다.

2. AsyncConfigurer 의 필수 Override 메서드 구현

@Bean(name = "executorTest")
@Override
public Executor getAsyncExecutor() {   ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
   executor.setCorePoolSize(TASK_CORE_POOL_SIZE);
   executor.setMaxPoolSize(TASK_MAX_POOL_SIZE);
   executor.setQueueCapacity(TASK_QUEUE_CAPACITY);
   executor.setBeanName(BEAN_NAME);
   executor.initialize();   return executor;}@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
   return new AsyncUncaughtExceptionHandler();}
  • 위에서 설정한 상수들 할당하여 쓰레드, 큐, bean name을 설정한다.
  • getAsyncUncaughtExceptionHandler 에 대해 설정 AsyncUncaughtExceptionHandler에 대해 구현해주어야 한다.
  • 만약 멀티로 executor를 생성할 경우 @Override대신 @Qualifier를 선언해준다.

3. AsyncTask 생성

@Service("asyncTask")
public class AsyncTask{  @Async("executorTest")
  public void executor(String str) {
     System.out.println("result:"+str);  }
}
  • task 클래스 메서드 @Async 어노테이션에 Executor명을 적어준다.

4. 실행

public class AsyncController{
   @Resource(name = "asyncTask")
   private AsyncTask asyncTask;
   @Resource(name = "Config")
   private Config config;    @RequestMapping("/test.do")
    public ModelAndView doTask(HttpServletRequest request, HttpServletResponse response) throws Exception {
       asyncTask.executor("TEST");   }}
  • 새로운 쓰레드가 생기며, test가 출력되는 것을 확인 할 수 있다.

스레드 결과 콜백 받기


	public SocketChannel socketChannel2; //일단은 public으로
	private boolean bLoop = true;

	
	@Async
	public CompletableFuture<SocketChannel> csSocketStart() throws IOException {
				
		socketChannel2 = null; // HL7 Test Panel에 보낼 프로토콜
		socketChannel2 = SocketChannel.open();
		
		logger.debug("central로 보내는 socket channel");

		try {
			socketChannel2.connect(new InetSocketAddress("localhost", 5051));
			logger.debug("socketChannel connected to port 5051");
			socketChannel2.configureBlocking(true);// Non-Blocking I/O

		} catch (Exception e2) {
			logger.debug("connected refused!!!");
			// e2.printStackTrace();
			socketChannel2.close();
		}
		
		return CompletableFuture.completedFuture(socketChannel2);
		
	}
	



---다른 클래스에서 호출


		try {
			CompletableFuture<SocketChannel> completableFuture = csSocketService.csSocketStart();
			SocketChannel channel = completableFuture.get(); //일단은 그냥 blocking 시켜서 보내자. 후에 thencombine으로 교체
			System.out.println(channel);
			
			csSocketService.hl7ProtocolSendThread(sb.toString(), channel);
			
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

[참고]

**[자바와 스프링의 비동기 기술해당 포스팅은 토비님의 토비의 봄 TV 8회 스프링 리액티브 프로그래밍 (4) 자바와 스프링의 비동기 기술 라이브 코딩을 보며 따라했던 실습 내용을 바탕으로 정리한 글입니다. 실습 코드들은 IntelliJ를 이용해…
jongmin92.github.io](https://jongmin92.github.io/2019/03/31/Java/java-async-1/)

**[[Spring 레퍼런스] 26장 태스크(Task) 실행과 스케줄링 :: Outsider's Dev Story이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다. 스프링 프레임워크는…
blog.outsider.ne.kr](https://blog.outsider.ne.kr/1066)

**[SPRING @Async를 활용한 multi thread 구현 - 2 - AsyncConfigurer 생성Spring 에서 비동기 처리를 하기 위해서 AsyncConfigurer@Asynk를 사용하려고 한다. 본 포스팅은 이번 시간에는 AsyncConfigurer 을 활용하여 Executor 를 생성하는 방법까지이다…
cofs.tistory.com](https://cofs.tistory.com/319)

https://pakss328.medium.com/spring-async-annotation을-활용한-thread-구현-f5b4766d49c5

https://jsonobject.tistory.com/233

주의 할 점은 private 메소드는 @Async 를 적용해도 비동기로 동작하지 않으며, 반드시 public 메소드에 @Async 를 적용해야 한다.

self-invocation(자가 호출)해서는 안된다. -> 같은 클래스 내부의 메서드를 호출하는 것은 안된다.

https://jeong-pro.tistory.com/187

profile
시간대비효율

0개의 댓글