일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- 엔터프라이즈 블록체인
- play2 강좌
- 하이퍼레저 패브릭
- hyperledger fabric
- 스칼라 강좌
- Hyperledger fabric gossip protocol
- 하이브리드앱
- 스위프트
- 파이썬
- 스칼라
- 파이썬 데이터분석
- 파이썬 강좌
- 안드로이드 웹뷰
- 그라파나
- Actor
- Golang
- 플레이프레임워크
- 주키퍼
- CORDA
- Play2 로 웹 개발
- Adapter 패턴
- 블록체인
- 스칼라 동시성
- play 강좌
- akka 강좌
- 파이썬 동시성
- 파이썬 머신러닝
- Akka
- Play2
- 이더리움
- Today
- Total
HAMA 블로그
[Play2] ScalaAsync (번역) 본문
비동기 결과에 대한 처리
Play2 의 가장 중요한 부분이라 본다. 현재 나도 잘 이해하고 있지 못한 Reactive Web 개발 모델과 밀접하며
Reactive Web Applications: Covers Play, Akka, and Reactive Streams
와Akka in Action
을 요즘 틈틈히 읽고 있는데 짧게 짧게 블로그를 통해 이해한 것을 정리 할 생각이다.컨트롤러를 비동기 방식으로 다루기
내부적으로 플레이프레임워크는 상향식으로 비동기적이다. 플레이는 매 요청을 비동기적이며 논블럭 방식으로 다룬다.
기본 설정이 비동기식 컨트롤러로 바뀌었는데, 다른 말로 하면 어플리켕션 코드가 컨트롤러에서 블럭되는것을 피해야 한다는 말이다. 즉 JDBC 콜, 스트리밍 API, HTTP 요청 및 오랜 시간이 걸리는 계산 같은 연산에 관련된 코드들 말이다.
블럭되는 컨트롤러에 의해 처리를 하기 위해 동시에 처리되는 요청들을 더 많이 수행하기 위해서는 기본 실행되는 쓰레드의 숫자를 마구 늘릴 수 도 있겠지만 컨트롤러를 비동기식으로 접근하는것이 확장성에 있어서 더 나으며 부하에 대한 시스템의 반응에도 더 유리하다.
(역주: 이런 건 많은 수의 클라이언트 처리에 대해 멀티쓰레딩으로 처리했던 톰캣 보다 node 나 vert.x 가 훨씬 반응성이 좋다는게 입증해준다. 하나의 쓰레드로 좀 더 효율적인 i/o 를 하면 여러개의 쓰레드로는 엄청나나 효율을 가져다 줄 수 있으니..)
논블록 액션 만들기
만약 우리가 아직 결과를 얻지 못했는데 바로 액션에 대한 결과를 처리하려면 어떻게 해야하나? 해답은 퓨쳐 이다. 리턴 받을 때 까지 기다리는게 아니라 즉시 Future[Result]
를 리턴 받아서 다른 클라이언트를 대응하는 일을 할 수 있게 된다. Result 에 실제 리턴값이 담기면 그 때 처리 할 것이다.
웹 클라이언트는 응답을 기다리는 동안 블럭 될 것이긴 하지만 그 클라이언트에 대한 처리를 위해 서버 자체가 블럭 되진 않는 다는 것이다. 즉 서버는 다른 클라이언트들을 처리하는데 사용 될 것이며 약속이 이행되면 바로 그 때 그 결과를 돌려 줄 것이다.
Future[Result]
를 다룰까
어떻게 실제 값을 우리에게 줄 퓨처와 그것을 통해 결과를 얻는 퓨처 :
import play.api.libs.concurrent.Execution.Implicits.defaultContext
val futurePIValue: Future[Double] = computePIAsynchronously()
val futureResult: Future[Result] = futurePIValue.map { pi =>
Ok("PI value computed: " + pi)
}
플레이 비동기 API 콜 모두는 퓨처를 줄 것이다. 이건 니가 외부로 웹서비스를 요청 하거나 (play.api.libs.WS
API) Akka 로 비동기 업무를 수행 할 때 도 사용된 다는 뜻이다. 즉 수행이 끝 날때까지 쓰레드가 놀고 있느게 아니라 퓨처를 받은 후에 즉시 퓨처에 실제 값이 들어 올때까지 다른 일을 하게 된다는 야그..
여기 비동기 블럭을 실행하고 결과를 얻는 Future 예제가 있다.
import play.api.libs.concurrent.Execution.Implicits.defaultContext
val futureInt: Future[Int] = scala.concurrent.Future {
intensiveComputation()
}
참고 : 퓨처로 실행되는 스레드 코드를 이해하는 것이 중요합니다. 위의 두 코드 블록에서 기본 실행 컨텍스트를 가져 오고 있습니다. 이것은 콜백을 허용하는 퓨처 API의 모든 메소드로 전달되는 암시 적 매개 변수입니다. 실행 컨텍스트는 종종 스레드 풀과 동일하지만 필수적이지는 않습니다.
퓨처상에서 래핑하여 동기식 IO를 마술처럼 비동기로 전환 할 수는 없습니다. 응용 프로그램의 구조를 변경하여 작업을 차단하지 못하는 경우, 어떤 시점에서 작업을 실행해야하며 해당 스레드가 차단됩니다. 따라서 Future에 작업을 포함하는 것 외에도 예상되는 동시성을 처리하기에 충분한 스레드로 구성된 별도의 실행 컨텍스트에서 실행되도록 구성해야합니다. 자세한 내용은 플레이 스레드 풀 이해를 참조하십시오.
액터를 사용하여 작업을 차단하는 것도 도움이 될 수 있습니다. 액터는 타임 아웃 및 오류 처리, 실행 컨텍스트 블로킹 설정 및 서비스와 관련된 모든 상태 관리를위한 명확한 모델을 제공합니다. 또한 액터는 동시 캐시 및 데이터베이스 요청을 처리하고 백엔드 서버 클러스터에서 원격 실행을 허용하는 ScatterGatherFirstCompletedRouter와 같은 패턴을 제공합니다. 그러나 액터는 필요에 따라 과도하게 동작 될 수 도 있습니다.
퓨쳐 되돌려주기
지금까지 액션을 만들기 위해 Action.apply 빌더 메소드를 사용 했지만 비동기 결과를 보내기 위해 Action.async
빌더도 사용된다.
import play.api.libs.concurrent.Execution.Implicits.defaultContext
def index = Action.async {
val futureInt = scala.concurrent.Future { intensiveComputation() }
futureInt.map(i => Ok("Got result: " + i))
}
액션은 기본적으로 비동기적이다
플레이 액션은 기본적으로 비동기적이다. 예를 들어 아래 컨트롤러 코드 처럼 말이다. { Ok(...) }
이 부분은 컨트롤러의 메소드 바디가 아니다. 익명 함수이며 Action 객체의 apply 메소드에 전달되며 Action 타입의 객체를 만든다. 내부적으로 익명 함수가 호출 될 것이며 그것의 결과는 퓨처로 감싸질 것이다. 그럼 Action.async 는 왜 만든거지?
val echo = Action { request =>
Ok("Got request [" + request + "]")
}
Note:
Action.apply
과Action.async
양쪽 모두Action
objects 를 만들며 내부적으로 동일하게 다루어 진다..async
빌더는 그냥 퓨처를 돌려주는 API 기반의 액션을 간단히 만들기 위한 장치일 뿐이다. 비동기코드를 쓰는것을 좀 더 쉽게 만들어 준다는 것일 뿐.
타임아웃 다루기
종종 타임아웃을 적절히 다루게 되는데 웹브라우저가 너무 오랫동안 블럭되면 사용자들이 짜증날테니깐...프라미스에 타임아웃을 설정해서 그 때 까지 결과가 도착 안하면 님이 만든 예러 메세지를 그냥 돌려주게 하자. 아래는 InternalServerError 를 돌려준다.
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
def index = Action.async {
val futureInt = scala.concurrent.Future { intensiveComputation() }
val timeoutFuture = play.api.libs.concurrent.Promise.timeout("Oops", 1.second)
Future.firstCompletedOf(Seq(futureInt, timeoutFuture)).map {
case i: Int => Ok("Got result: " + i)
case t: String => InternalServerError(t)
}
}
Next: Streaming HTTP responses
'PlayFramework2' 카테고리의 다른 글
[Play2] Action 과 Action.async 의 차이점 (0) | 2017.02.26 |
---|---|
[Play2] Iteratee & Enumerators 간단 정리 (0) | 2017.02.18 |
[Play2] WebSockets (0) | 2016.10.13 |
[Play2] 외부의 원격액터와 통신 (0) | 2016.10.11 |
[Play2] WS API (번역) (0) | 2016.10.11 |