스프링부트 다양한 기능 6. 비동기 처리하기

min seung moon·2021년 6월 29일
0

Spring

목록 보기
37/50

1. 프로젝트 테스트

  • async project, web, lombok
  • Package : controller, service
  • Class : ApiController, AsyncService

01. 간단한 Async 처리

  • Main AsyncApplication.java
    • @EnableAsync
      • 스프링의 Async 어노테이션을 감지하며 EJB 3.1javax.ejb.Asynchronous; 이 옵션으로 사용자 정의된 다른 어노테이션 또한 감지할 수 있다.
package com.example.async;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class AsyncApplication {

	public static void main(String[] args) {
		SpringApplication.run(AsyncApplication.class, args);
	}

}
  • controller / ApiController.java
package com.example.async.controller;

import com.example.async.service.AsyncService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class ApiController {

    final private AsyncService asyncService;

    @GetMapping("/hello")
    public String hello() {
        asyncService.hello();
        log.info("method end");
        return "hello";
    }
}
  • service / AsyncService.java
    • @Async
      • 비동기 처리를 위한 어노테이션, spring에서 제공하는 thread
      • @Async 어노테이션을 빈bean에 넣으면 별도의 쓰레드에서 실행되도록 동작
      • public method에만 적용가능
    • @Service
      • 서비스 레이어 클래스들에 사용되어지는 어노테이션으로 @Component를 사용해도 상관 없다
      • @Service 어노테이션을 사용함으로써 해당 클래스가 서비스 레이어 클래스라는 것을 명확하게 할 수 있다
      • @Service 어노테이션은 해당 클래스를 루트 컨테이너에 빈(Bean) 객체로 생성해주는 어노테이션
package com.example.async.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class AsyncService {

    @Async
    public void hello() {
        for(int i = 0; i < 10; i ++) {
            try {
                Thread.sleep(2000);
                log.info("thread sleep");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

02. CompletableFuture 사용

  • CompletableFuture
    • @Async같은 비동기 메소드를 사용할 때, Void형태를 사용한다면 문제가 되지 않는다. 하지만 return이 존재한다면 기존에는 Future나 ListenableFuture를 이용하여 해결했지만 JAVA 8버전 부터는 CompletableFuture를 제공
    • 단일 api 전송 보다는 여러개의 api를 동시에 전송을 한 다음에 그 결과를 조인해서 보일 때 사용하는 것이 더 효율적이기도 하다
  • AsyncService.java
package com.example.async.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
public class AsyncService {

    // CompletableFuture
    // 단일 api 전송 보다는 여러개의 api를 동시에 전송을 한 다음에
    // 그 결과를 조인해서 보일 때 사용하는 것이 더 효율적이기도 하다
    @Async
    public CompletableFuture run() {
        return new AsyncResult(hello()).completable();
    }


    public String hello() {
        for(int i = 0; i < 10; i ++) {
            try {
                Thread.sleep(2000);
                log.info("thread sleep");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "async hello";
    }
}
  • ApiController.java
package com.example.async.controller;

import com.example.async.service.AsyncService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class ApiController {

    final private AsyncService asyncService;

    // CompletableFuture
    // @Async같은 비동기 메소드를 사용할 때, Void형태를 사용한다면 문제가 되지 않는다. 하지만 return이 존재한다면 기존에는 Future나 ListenableFuture를 이용하여 해결했지만 JAVA 8버전 부터는 CompletableFuture를 제공

    @GetMapping("/hello")
    public CompletableFuture hello() {
        log.info("completable future init");
        return asyncService.run();
    }
}

03. Thread 만들기

  • Package : config
  • Class : AppConfig
  • AppConfig.java
package com.example.async.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
public class AppConfig {

    @Bean("async-thread")
    public Executor asyncThread() {
        ThreadPoolTaskExecutor threadPoolExecutor = new ThreadPoolTaskExecutor();
        threadPoolExecutor.setMaxPoolSize(100); // 최대 갯수
        threadPoolExecutor.setCorePoolSize(10);  // 코어 갯수
        threadPoolExecutor.setQueueCapacity(10);  // Queue 갯수
        threadPoolExecutor.setThreadNamePrefix("Async-");
        // 동작은 먼저 Pool의 갯수만큼 쓰레드가 먼저 작동을 하고 가득차면
        // Queue의 스레드 만큼 사용하고 또 가득차면 Pool 이렇게 계속 번갈아가면서
        // 최대 갯수 만큼 쓰레드가 증가한다
        return threadPoolExecutor;
    }
}
  • AsyncService.java
package com.example.async.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
public class AsyncService {

    // CompletableFuture
    // 단일 api 전송 보다는 여러개의 api를 동시에 전송을 한 다음에
    // 그 결과를 조인해서 보일 때 사용하는 것이 더 효율적이기도 하다
    @Async("async-thread")
    public CompletableFuture run() {
        return new AsyncResult(hello()).completable();
    }


    public String hello() {
        for(int i = 0; i < 10; i ++) {
            try {
                Thread.sleep(2000);
                log.info("thread sleep");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "async hello";
    }
}

  • @Async 어노테이션에 @Bean으로 등록한 thread 호출
profile
아직까지는 코린이!

0개의 댓글