RestTemplateでコルーチンを使おうとして失敗

結論的にはRestTemplateではなくWebClientを使用する。
SpringBootでRestTemplateを使って外部APIを並列的に呼び出すためにコルーチンを使用したら並列的に呼び出されずに直列的に呼び出されてしまった。

Kotlin公式のページを見て以下のように外部APIの呼び出しを並列化するように改造してみたが上手くいかなかった。

    fun getTask(taskId: Int): Task {
        callApiInParallel()
    }
    
    // asyncとawaitを使って外部API呼び出しを並列化する関数
    fun callApiInParallel() = runBlocking<Unit> {
        val time = System.currentTimeMillis()

        val data1 = async { getTaskDataAsync(1) }
        val data2 = async { getTaskDataAsync(2) }
        val data3 = async { getTaskDataAsync(3) }

        data1.await()
        data2.await()
        data3.await()

        println("Completed in ${System.currentTimeMillis() - time} ms")
    }

    // Repository層を呼び出してtaskデータを取得する関数
    suspend fun getTaskDataAsync(taskId: Int): Task? {
        val startTime = System.currentTimeMillis()
        println("start $taskId")

        val response = taskRepository.getTask(taskId)

        val elapsedTime = System.currentTimeMillis() - startTime
        println("end   $taskId time=$elapsedTime ms")
        return response
    }

    // Repository層の外部API呼び出し関数
    suspend fun getTask(taskId: Int): Task? {
        val uri = "$taskApiUrl/tasks/$taskId"
        return restTemplate?.getForObject(uri, Task::class)
    }

結果は以下のように直列的にAPIが呼び出されてしまった。

start 1
end   1 time=3008 ms
start 2
end   2 time=3010 ms
start 3
end   3 time=3008 ms
Completed in 9040 ms

Repository層の外部API呼び出し関数をRestTemplateからWebClientに変更。
WebClientを使用する為にbuild.gradleのdependenciesにspring-boot-starter-webfluxを追加。

dependencies {
・・・
    implementation("org.springframework.boot:spring-boot-starter-webflux")
   suspend fun getTask(taskId: Int): Task? {
        val uri = "$taskApiUrl/tasks/$taskId"
//        return restTemplate?.getForObject(uri, Task::class)
        val webClient = WebClient.builder()
                .baseUrl("http://localhost:50000")
                .build();

        val task: Task? = webClient
                .get()
                .uri(uri)
                .awaitExchange()
                .awaitBody<Task>()
        return task
    }

並列的に呼び出されていることを確認。

start 1
start 2
start 3
end   2 time=3016 ms
end   1 time=3017 ms
end   3 time=3016 ms
Completed in 3019 ms

コメントを残す

メールアドレスが公開されることはありません。

ABOUT US
little
15年以上プログラマーをしているエンジニアです。Kotlin, Java, Python, C++を使用したServerSideの開発に携わってきました。とりあえずやってみるスタイルで記事を更新していきます。