(11) Spring Official Guide - Creating Asynchronous Methods

HEYDAY7·2022년 10월 30일
0

Learn Kotlin + Spring

목록 보기
12/25

시작하며

뜬금없을 수 있지만 이제부터는 프로젝트 시작이나, 여타 작업들을 자세히 설명하지 않으려 한다. 처음에는 공부에 대한 복습과 기록에 대한 느낌으로 이 글들을 적어두고 있었는데, 어느샌가 부터는 불필요한 내용들이 많이 들어가기 시작했다고 생각했다. 그래서 누군가가 이 글을 읽는다면 가이드를 직접 따라오면서 읽을 것이라 가정하고, 짤막한 가이드에서의 핵심적으로 중요한 내용만을 강조해가려고 한다.

Creating Asynchronous Methods

https://spring.io/guides/gs/async-method/

한줄 요약

CompletableFuture를 이용해 Async한 함수 동작해보기

코드 작업

기억하면 좋을 파트들만 설명을 짧게 남겨둔다.

## User.kt
@JsonIgnoreProperties(ignoreUnknown = true)
@Entity
data class User(
    var name: String?,
    var Blog: String?,
    @Id @GeneratedValue
    val id: Int? = null
)

## GitHubLookupService.kt
@Service
class GitHubLookupService(
        @Autowired private val restTemplate: RestTemplate
) {
    private val logger = LoggerFactory.getLogger(GitHubLookupService::class.java)

    @Async
    fun findUser(user: String): CompletableFuture<User> {
        logger.info("Looking up $user")
        val url = "https://api.github.com/users/$user"
        val result = restTemplate.getForObject(url, User::class.java)
        Thread.sleep(1000L)
        return CompletableFuture.completedFuture(result)
    }
}
  • @Async annotation을 함수에 달아주는 것.
  • CompletableFuture Class를 이용한다.
## ~Application.kt (기존 코드에 수정)
@SpringBootApplication
@EnableAsync
class CreatingAsynchronousMethodsApplication {
	@Bean
	fun restTemplate(): RestTemplate {
		return RestTemplate()
	}

	@Bean
	fun taskExecutor(): Executor {
		val executor = ThreadPoolTaskExecutor()
		executor.corePoolSize = 2
		executor.maxPoolSize = 2
		executor.queueCapacity = 500
		executor.setThreadNamePrefix("GithubLookup-")
		executor.initialize()
		return executor
	}
}
  • 이런 방식으로 asynchronous하게 짤것이라면 @EnableAsync annotation을 잊지 말자.
  • taskExecutor의 경우 특별할 설정이 없다면 @Async annotation이 해당 taskExecutor를 이용하게 되어 있다. 따라서 Thread에 여러 설정을 할 수 있는 방식이 이렇다는 것을 알아두면 좋을 것 같다.
## AppRunner.kt
@Component
class AppRunner(
        @Autowired private val gitHubLookupService: GitHubLookupService
): CommandLineRunner {
    private val logger = LoggerFactory.getLogger(AppRunner::class.java)

    override fun run(vararg args: String?) {
        val start = System.currentTimeMillis()

        val page1 = gitHubLookupService.findUser("PivotalSoftware")
        val page2 = gitHubLookupService.findUser("CloudFoundry")
        val page3 = gitHubLookupService.findUser("Spring-Projects")

        CompletableFuture.allOf(page1, page2, page3).join()

        logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
        logger.info("--> " + page1.get());
        logger.info("--> " + page2.get());
        logger.info("--> " + page3.get());
    }
}
  • CompletableFuture.allOf().join에서 allOf를 통해 CompletableFuture object array를 만들고, join을 통해서 모든 작업이 끝날 때 까지 기다릴 수 있도록 만들어준다.

Run

이후엔 실제로 돌려보면 가이드에서 알려주는 것과 같이 결과가 나올 것이다. 해당 가이드의 핵심은 asynchronous한 함수를 통해 여러 오래 걸릴 수 있는 작업을 동시에 진행시킬 수 있다~ 라는 것이다.

마치며

사실 실제적으로 이를 유용하게 쓰려나~ 싶기는 하다. kotlin의 coroutine을 활용하여 설계하는 방식도 있을 것이고, 이게 좀 더 익숙하기도 해서 후에 이 방식을 따로 알아봐야 할 것 같다.
코드는 여기서 확인할 수 있다.

profile
(전) Junior Android Developer (현) Backend 이직 준비생

0개의 댓글