작성
·
61
·
수정됨
0
강사님 강의를 듣고 배치 단위로 요청을 가져와 Redis 서버에서 작업을 처리하는 부분에 코루틴을 적용하려고 합니다. 강의를 통해 최소한의 스레드로 여러 코루틴을 실행해야 코루틴을 극대화할 수 있다고 이해했습니다. n개의 요청은 모두 독립적이며 배치 단위로 가져온 이유는 1개의 스레드로 여러 코루틴을 실행하기 위함입니다.
Redis는 마스터에서만 작업이 가능하므로 요청을 보내고, 응답받아 후속 처리하는 부분을
Redis 서버로 요청 (코루틴1)
Redis 서버로부터 응답받아 후속 처리 (코루틴2)
로 분리했습니다.
1개의 스레드에서 여러 코루틴을 동작시키기 위해 이런 기준으로 코루틴의 중단 지점을 생성하는 것이 적절한지 궁금합니다.
또한 n개의 요청을 모두 Redis 서버로 보낸 후 응답 받는 것이 아니라
응답을 받고 후속 처리가 가능한 요청은 ‘모든 요청을 Redis 서버로 전달했다는 여부와 상관없이’ 후속처리를 하기 위해 다음과 같이 코드를 작성했습니다.
coroutineScope {
val jobs = userIds.map { userId ->
async {
// main 스레드가 처리
val result = 레디스 접근 메서드 (RedisTemplate)
Pair(userId, result)
}
}
jobs.forEach { job ->
launch(Dispatchers.IO) {
val (userId, status) = job.await()
// 응답에 대한 후속 처리
// 결과마다 다른 DefaultDispatcher-worker 가 처리
}
}
}
위와 같이 작성하면 Redis로 요청을 보내는 부분은 1개의 main스레드가 처리하지만, 후속 처리는 결과마다 다른 DefaultDispatcher-workder가 처리합니다. 이는 결과마다 다른 스레드가 처리하는 것이 맞다면 이 코드는 코루틴을 제대로 사용하지 못하고 있다고 생각하는데 제 생각이 맞는지 궁금합니다.
coroutineScope {
launch {
for(userId in userIds) {
val result = 레디스 접근 메서드
channel.send(Pair(userId, result))
}
channel.close()
}
launch {
for(result in channel) {
// 응답에 대한 후속 처리
}
}
}
위와 같이 작성하면 1개의 main 스레드가 모든 작업을 처리합니다. 이렇게 작성해야 코루틴을 제대로 사용하는 것이 맞는지 궁금합니다.
강의 잘 들었고 감사합니다!
답변 1
0
안녕하세요 가나다라님! 🙂 질문 주셔서 감사합니다. 어떤 작업을 하시는지 배경지식이 제한된 상태로 답변 드려보면 제 생각은 다음과 같습니다.
적어주신 내용에 대해 하나씩 말씀드려 보겠습니다.
강의를 통해 최소한의 스레드로 여러 코루틴을 실행해야 코루틴을 극대화할 수 있다고 이해했습니다.
결과마다 다른 스레드가 처리하는 것이 맞다면 이 코드는 코루틴을 제대로 사용하지 못하고 있다고 생각하는데
두 멘트가 서로 연관이 있어 함께 답변 드리겠습니다.
이론상 적은 스레드를 여러 코루틴으로 실행시킬 경우 코루틴의 효과를 극대화 할 수 있는 것이 맞습니다.
다만 그러한 행위 자체가 코루틴을 제대로 사용하고 있는지, 사용하고 있지 못한지에 대한 판단 기준은 아니라고 생각합니다.
여러 작업을 동시에 실행시키고, CPU 작업이 필요할 때만 적은 스레드를 배정하는 non-blocking 방식은 코루틴의 목표라기 보다, 오히려 그런 non-blocking /비동기 작업을 동기 작업 처럼 가독성 좋은 코드로 구현할 수 있게 해주는 것이 코루틴의 목표에 가깝다고 생각합니다.
이런 관점에서 배치 단위로 요청을 가져와 Redis 서버에서 작업을 처리 해야 한다면... 적어주신 코드를 볼 때
scheduler 작업인건지, Batch 작업인건지, API 작업인건지
API 작업이라면 MVC 같은 thread-per-request 구조인지 webflux 구조인지
에 따라
코루틴을 사용하는 것이 좋을지, 사용하지 않는 것이 좋을지
코루틴을 어떤 방식으로 사용하는 것이 좋을지
가 달라진다고 생각합니다.
만약 webflux + coroutine 조합이라면 스레드가 한 개인지 두 개인지를 크게 신경쓰지 않고 다음과 같이 코드를 작성해도 문제가 없다고 생각합니다.
suspend fun handleUserIds(userIds: List<Long>) {
userIds.forEach {
val redisResult = 레디스 접근 메소드
// channel이 무엇을 의미하는지 정확히 모르겠군요!
// 첫 번째 코드에는 channel 코드가 없고 두 번째 코드에만 있어서요
val result = channel.send(Pair(userId, redisResult.await()))
// 후속 처리
}
}
위와 같이 작성하면 1개의 main 스레드가 모든 작업을 처리합니다
꼭 그렇지는 않습니다. 작성해주신 코드가 어떤 환경에 실행되는지에 따라 여러 스레드로 분산되어 처리될 수도 있습니다.
감사합니니다. ☺