2014-07-11 2 views
7

질문 :올바른 패턴

Akka 배우의 상태를 축적에 대한 올바른 패턴은 무엇입니까?

상황 :

의 내가 몇 가지 서비스를 모두 반환 데이터가 있다고 가정 해 봅시다.

class ServiceA extends Actor { 
    def receive = { 
    case _ => sender ! AResponse(100) 
    } 
} 

class ServiceB extends Actor { 
    def receive = { 
    case _ => sender ! BResponse("n") 
    } 
} 

// ... 

나는 이러한 모든 서비스에 이야기 한 후 다시 원래의 보낸 사람에게 모든 데이터와 응답을 보내, 자신의 응답을 추적 유지 좌표 하나 개의 제어/감독 배우를 갖고 싶어.

class Supervisor extends Actor { 
    def receive = { 
    case "begin" => begin 
    case AResponse(id) => ??? 
    case BResponse(letter) => ??? 
    } 

// end goal: 
def gotEverything(id: Int, letter: String) = 
    originalSender ! (id, letter) 

    def begin = { 
    ServiceA ! "a" 
    ServiceB ! "b" 
    } 
} 

서비스 응답이 들어 오면 어떻게 해당 상태를 모두 연관시킬 수 있습니까? 알다시피, 만약 내가 ARTSponse의 값을 var aResponse: Int에 할당한다면, 다른 메시지가 수신됨에 따라 var가 끊임없이 변하기 때문에 메시지를 기다리는 동안에는 그 메시지에 의존 할 수 없다. BResponse 메시지 .

나는 ask을 사용할 수 있고 단지/flatMap Future의 중첩을 사용할 수 있다는 것을 알았지 만, 나는 그것이 나쁜 패턴이라는 것을 읽었습니다. 미래가 없어도이 모든 것을 성취 할 수있는 방법이 있습니까?

+0

'aResponse'가 두 번 이상 변경되는 이유는 무엇입니까? 'ServiceA'에게만 하나의 메세지를 보냅니다. 당신의 목표는 정확히 무엇입니까? 'AResponse'와'BResponse'를받을 때까지 기다리고'gotEverything'을 값으로 호출하십시오. – vptheron

+0

>> 목표가 정확히 무엇입니까?AResponse와 BResponse를 받고 값이있는 gotEverything을 호출 할 때까지 기다리십시오. 예 –

+0

식별자 또는 액터 ref가있는 목록에 응답을 저장할 수 있습니다 – wedens

답변

15

액터가 여러 스레드에서 동시에 액세스되지 않으므로 원하는 액터를 쉽게 저장하고 변형 할 수 있습니다. 예를 들어 다음과 같이 할 수 있습니다.

class Supervisor extends Actor { 
    private var originalSender: Option[ActorRef] = None 
    private var id: Option[WhateverId] = None 
    private var letter: Option[WhateverLetter] = None 

    def everythingReceived = id.isDefined && letter.isDefined 

    def receive = { 
    case "begin" => 
     this.originalSender = Some(sender) 
     begin() 

    case AResponse(id) => 
     this.id = Some(id) 
     if (everythingReceived) gotEverything() 

    case BResponse(letter) => 
     this.letter = Some(letter) 
     if (everythingReceived) gotEverything() 
    } 

    // end goal: 
    def gotEverything(): Unit = { 
    originalSender.foreach(_ ! (id.get, letter.get)) 
    originalSender = None 
    id = None 
    letter = None 
    } 

    def begin(): Unit = { 
    ServiceA ! "a" 
    ServiceB ! "b" 
    } 
} 

더 좋은 방법이 있습니다. 명시 적 상태 변수없이 배우와 함께 어떤 종류의 상태 머신을 에뮬레이트 할 수 있습니다. 이것은 become() 메커니즘을 사용하여 수행됩니다.

class Supervisor extends Actor { 
    def receive = empty 

    def empty: Receive = { 
    case "begin" => 
     AService ! "a" 
     BService ! "b" 
     context become noResponses(sender) 
    } 

    def noResponses(originalSender: ActorRef): Receive = { 
    case AResponse(id) => context become receivedId(originalSender, id) 
    case BResponse(letter) => context become receivedLetter(originalSender, letter) 
    } 

    def receivedId(originalSender: ActorRef, id: WhateverId): Receive = { 
    case AResponse(id) => context become receivedId(originalSender, id) 
    case BResponse(letter) => gotEverything(originalSender, id, letter) 
    } 

    def receivedLetter(originalSender: ActorRef, letter: WhateverLetter): Receive = { 
    case AResponse(id) => gotEverything(originalSender, id, letter) 
    case BResponse(letter) => context become receivedLetter(originalSender, letter) 
    } 

    // end goal: 
    def gotEverything(originalSender: ActorRef, id: Int, letter: String): Unit = { 
    originalSender ! (id, letter) 
    context become empty 
    } 
} 

이것은 약간 자세한 정보 일 수 있지만 명시 적 변수는 포함되어 있지 않습니다. 모든 상태는 암시 적으로 Receive 메서드의 매개 변수에 포함되며이 상태를 업데이트해야하는 경우 액터의 수신 함수가이 새로운 상태를 반영하기 위해 전환됩니다.

위의 코드는 매우 간단하며 많은 "원본 발신자"가있을 수 있으면 제대로 작동하지 않습니다. 이 경우 모든 메시지에 ID를 추가하고이를 사용하여 어떤 응답이 "원래 보낸 사람"상태에 속하는지 결정하거나 "원본 보낸 사람"모두에 대해 각각 여러 명의 역할을 만들 수 있습니다.

1

저는 Akka가 actor-per-request 패턴을 사용한다고 생각합니다. 이렇게하면 어떤 응답이 무엇에 해당하는지 파악하는 대신 요청할 때마다 새 배우를 만듭니다. 이것은 매우 싸다. 사실, 당신이 ask() 할 때마다 일어난다.

대개 이러한 요청 프로세서 (즉,이 요청 프로세서라고 함)는 간단한 응답 필드가 있습니다. 요청이 도착했는지 여부를 확인하는 것은 단순한 null 비교의 문제입니다.

재시도/실패도이 체계에서 훨씬 쉽게됩니다. 타임 아웃도 마찬가지입니다.

관련 문제