Spring에서의 기본적인 처리는 순차적인 처리이다. 이런 방식은 bocking 현상을 야기하고 만약 덩치가 큰 비즈니스 로직을 수행해야 한다면 여러 Thread로 나누어서 처리 후 다시 결과를 합치는 과정을 거쳐야 한다.
Spring에서 Async 사용
Async process를 사용하려면 @EnableAsync 어노테이션을 붙이고 thread pool에 대한 설정을 해주어야 한다. 아래는 kotlin 문법으로 작성한 설정이다.
@Configuration
@EnableAsync
class AsyncConfig {
@Bean
fun taskExecutor(): TaskExecutor {
val taskExecutor = ThreadPoolTaskExecutor()
taskExecutor.corePoolSize = 10
taskExecutor.setQueueCapacity(50)
taskExecutor.maxPoolSize = 30
taskExecutor.setTaskDecorator(LoggingTaskDecorator())
return taskExecutor
}
}
corePoolSize 는 최초 가용 thread 개수이다.
QueueCapaCity 는 최대로 대기할 수 있는 task 개수를 의미 한다.
maxPoolSize 는 Queue에 있는 대기열이 찼을경우 가용 thread 개수의 최대치를 설정한다.
TaskDecorator 는 함수 호출 되기전에 호출되는 녀석이라고 보면 된다
(Asnyc로 동작하면 새 ThreadContext로 바뀌므로 이전 Context를 복사하는 역할을 함. 참고 블로그)
위 방법외에도 xml파일로도 설정할 수 있는데 개인적으로 비추이다.
<task:executor id="taskExecutor" pool-size="10" />
<task:annotation-driven executor="taskExecutor"/>
Spring Async는 두가지 모드를 지원한다. Fire and forget mode 와 Result retrieval mode.
아래 코드는 두가지 방식에 대하여 간단히 구현한 코드이다.
// **Fire and forget mod** 구현, 무거운 job를 호출전 단계에서 쪼개여 실행 가능
@Async("taskExecutor")
fun updateUser(userPasswordDTO: UserPasswordDTO){
Thread.sleep(1000)
var user = userRepository.findOne(user.name.eq(userPasswordDTO.name)).get()
//이전 암호 체크와 새 비번으로 수정
if(user!=null){
if(user.password==userPasswordDTO.prePassword) {
user.password = BCrypt.hashpw(userPasswordDTO.password, BCrypt.gensalt())
}
}
}
//**Result retrieval mode**로 구현, 호출하는 쪽에서 다른 작업을 한 후 대기하여 결과를 취합 용도 사용
@Async("taskExecutor")
fun creatUser(userDTO: UserDTO):CompletableFuture<String>{
var user = userRepository.findOne(user.name.eq(userDTO.name)).get()
//이전 암호 체크와 새 비번으로 수정
if(user!=null){
return CompletableFuture.completedFuture("The user name already used")
}
else{
val newUser = User(userDTO.name,userDTO.password)
return CompletableFuture.completedFuture(newUser.toString())
}
}
사용자 생성과 비번 수정을 예로 코드를 작성해 보았다. 사실 이런 예를 들면 비현실적이지만 thread sleep를 이용하여 Asnc을 구현해 보았다.
아래는 위의 Service에 대한 Test 코드 이다.
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(properties = ["server.port=8081"])
class OrderApplicationTests() {
@Autowired
private lateinit var userService: UserService
@Test
fun test_register_user() {
//test creat user
var usrDto = UserDTO("techfox","123")
//return 이 있는 async 함수
var future = userService.creatUser(usrDto)
var userPasswordDTO = UserPasswordDTO("techfox","123","456")
userService.updateUser(userPasswordDTO)
Thread.sleep(2000)
future.join()
}
}
마지막 sleep은 Asnyc 기다리기를 위한 sleep이다.
Ref:
https://jeong-pro.tistory.com/187
https://brunch.co.kr/@springboot/267
'Spring boot' 카테고리의 다른 글
[Spring] Transaction 정리 (0) | 2021.11.24 |
---|---|
[Spring] Logging 구성 및 구현 (0) | 2021.11.22 |
[Spring] 사용자 Token 인증 방식 (0) | 2021.11.20 |
[Spring] Spring AOP 개념 및 구현 (0) | 2021.11.15 |
[Spring] Kotlin + Spring boot 에 MongoDB을 도입(Spring Data MongoDB, Querydsl) (0) | 2021.11.14 |
Comment