2015-01-16 4 views
2

저는 Scala와 Akka를 사용하여 주식 시장 애플리케이션을 구축 중입니다. 시장은 구매자와 판매자 일치하고 트랜잭션 처리하기 위해 구매자와 (어떤 시점에서) 완료 될 필요가 판매자 모두에두 명의 배우가 완료해야하는 Akka/Scala 약속

Promise[Transaction] 

를 보냅니다.

문제 중 하나

  1. 구매자가 충분한 자금을 가지고 있기 때문에 약속
  2. 판매자가 충분한 주식을 보유하고, 실패에 완료 할 수 있다는 것입니다.

두 배우의 협조가 필요한 스칼라 약속을 어떻게 만들 수 있습니까?

+2

구매자와 판매자 모두에게 두 개의 'ask'메시지를 보내고 'for-comprehension'을 사용하여 원하는 약속을 반환하는 방법은 무엇입니까? – Peter

+0

구매자와 판매자가 모두 응답 할 때까지 시장이 기다릴 필요가 없습니까? 시장에서 단순히 약속을 생성하고 구매자와 판매자 중 하나 (또는 ​​둘 모두)가 기다리는 것을 선호합니다 (필요한 경우). – davidrpugh

+0

질문에 답변 한 경우 답변을 수락하십시오.:) – mdm

답변

1

가능하면 ask을 사용하지 않으려면 피해야합니다.

약속을 구매자와 판매자 배우에게 보내고, 해당 선물을 구성하고 약속이 이행되면 거래를 처리하는 방법은 다음과 같습니다. 당신은 구매자로부터 자금을 얻을 수 있기 때문에, 당신은 판매자로부터 주식을 얻을 수 있기 때문에, 또는 뭔가 transact에 잘못된 때문에 : 궁극적 인 결과는 여러 가지 이유로 실패 할 수 있다는

import scala.concurrent.{future, promise} 
import scala.concurrent.ExecutionContext.Implicits.global 

val fundsPromise = promise[Funds] 
val sharesPromise = promise[Shares] 

buyerActor ! GetFunds(amount, fundsPromise) 
sellerActor ! GetShares(numShares, stock, sharesPromise) 

val futureFunds = fundsPromise.future 
val futureShares = sharesPromise.future 

val purchase = 
    for { funds <- futureFunds; shares <- futureShares } 
    yield transact(funds, shares) 

purchase onComplete { 
    case Success(transactionResult) => 
    buyerActor ! PutShares(numShares) 
    sellerActor ! PutFunds(amount) 
    // tell somebody it worked 
    case Failure(t) => 
    futureFunds onSuccess { case _ => buyerActor ! PutFunds(amount) } 
    futureShares onSuccess { case _ => sellerActor ! PutShares(numShares) } 
    // tell somebody it failed because of t 
} 

참고. 그래서 실패 할 때 우리는 자금을 확인하고 미래를 나눠 가지고 있는지 확인하고 만약 그렇다면 우리가 취한 것을 돌려줍니다.

또한 선물로는 amountnumShares을 마감합니다. 즉, val이어야합니다. 만약 그들이 vars 였다면, future가 실제로 실행될 때 잘못된 값을 사용하게 될 수도 있습니다.

bsmk가 지적했듯이 여기서는 과 sellerActor이 위의 코드와 동일한 JVM에 있다고 가정합니다. 만약 그렇지 않다면, 당신은 지역 배우가 원격 배우의 말을 듣고 약속을 처리하게 할 수 있습니다; 더 좋은 방법이 있다면 의견에 알려주십시오.

+0

이것은 원래 내가 생각했던 것과 훨씬 가깝습니다. 왜 이것이 트랜잭션이 대기열에 들어가게 할 것이라고 생각합니까? 더 많은 배경 : 단일 구매자 배우 나 판매자 행위자가 아니라 오히려 많은 구매자와 판매자 (나는 지금까지 1e5 ~ 1e6 구매자/판매자 행위자의 순서로 모델을 운영했다). 현재 시장 행위자는 특정 구매자 배우와 특정 판매자 배우를 일정한 가격 및 수량으로 일치시킵니다. 마켓은이 정보를 관련 배우에게 메시지로 전송하고 다른 구매자 액터와 판매자 액터로 돌아갑니다. – davidrpugh

+0

아, 알겠습니다 - 두 배우를 말했을 때, 당신은 거래에 참여한 두 배우를 의미합니다. 단 하나의 구매자 배우와 단일 판매자 배우가 아닙니다. 나는 그것에 너무 많이 읽었다. 그리고 저는 주/기금과 주식을 업데이트해야하기 때문에 주어진 구매자/판매자에 대한 요청을 직렬화하고자한다고 생각합니다. 당신은 그 때 옳은 길입니다, David, 저는 제 대답을 업데이트했습니다. – AmigoNico

+0

'futureFunds onSuccess {buyerActor! PutFunds (금액)}'는 (는)'buyerActor! PutFunds (amount)'는 부분적인 함수가 아닙니다. 어쨌든 당신이 그에 따라 당신의 대답을 수정할 수 있습니까? – davidrpugh

2

당신은 다음과 같이 뭔가를 할 수 :

def querySellingActor(seller : ActorRef) : Future[Boolean] = ??? 
def queryBuyingActor(buyer : ActorRef) : Future[Boolean] = ??? 

// Calling these outside the for-comprehension 
// ensures that they are really parallel 
val f1 = querySellingActor(sellerActor) 
val f2 = queryBuyingActor(buyerActor) 

val niceResult = for{ 
    sellerCanSell <- f1 
    buyerCanBuy <- f2 
} yield { sellerCanSell && buyerCanBuy } 

niceResult

지금 미래 [부울]입니다 주위에 전달 될 수 있으며, 당신이 그것에 당신의 답신 전화를 설치할 수 있습니다.

+0

시장은 기다리지 않는 것이 중요합니다. 시장은 배우를 질의하고 응답을 기다릴 수 없습니다. @ EndeNeu의 답변과 비슷한 수명이 짧은 액터 패턴에 효과적으로 그것을 묻지 않고 내 맥락에서이 솔루션을 사용할 수있는 방법을 모르겠습니다. – davidrpugh

2

내가 단명 배우 패턴을 사용, 당신은 당신이 정말로 필요한 경우에만 ask 패턴을 사용해야합니다 :

case object TransactionTimeout 
case class NewTransaction(buyer: ActorRef, seller: ActorRef) 

class TransactionHandlerActor extends Actor with ActorLogging { 

    var _scheduled: Cancellable = null 
    var resultReceiver = Actor.noSender 
    var hasBuyerAnswered = false 
    var hasSellerAnswered = false 

    def receive: Receive = { 
    case NewTransaction(buyer, seller) => 
     resultReceiver = sender() 
     buyer ! BuyRequest(...) 
     seller ! SellRequest(...) 
     _scheduled = context.system.scheduler.scheduleOnce(5 seconds)(self ! TransactionTimeout) 

    case TransactionTimeout => 
     resultReceiver ! TransactionError(...) 
     context.stop(self) 

    case BuyerResponse(...) => 
     hasBuyerAnswered = true 
     checkIfComplete() 
    case SellerResponse(...) => 
     hasSellerAnswered = true 
     checkIfComplete() 
    } 

    def checkIfComplete() = { 
    if(hasBuyerAnswered && hasSellerAnswered) { 
     /* finalize operation */ 
     resultReceiver ! TransactionCompleted(...) 
     _scheduled.cancel() 
     context.stop(self) 
    } 
    } 
} 

기본적으로 임시 배우가 트랜잭션을 처리하고 완료되면 자신을 중지, 제한 시간이있다 그가 응답을 영원히 기다리지 않을 것입니다. 이것은 당신이 할 수있는 것, 코드가 테스트되지 않은 것의 아주 간단한 예입니다.

+0

합리적인 접근 방법으로 보입니다. 그러나 나는 가능하다면 약속을 사용하기를 열렬히 원한다. (또는이 약속에서 약속이 왜/왜 가능하지 않은지 이해하는 것이 중요하다.) – davidrpugh

+0

정말로 약속과 선물을 사용하고 싶다면 ask 패턴을 사용해야하지만 'ask'로 akka는 단순히 내가하는 일을하는 임시 액터를 인스턴스화하기 만하면됩니다. 그것을 제어하십시오, 제가 말하고자하는 것은, akka는 식별자로 이름을 지정하고 실패 할 경우에만 그 식별자를 할당합니다. 일반적으로 코드 냄새가 있는지 물어보십시오. 항상 피할 수있는 방법이 있습니다. 더 많은 생각이 필요하지만 다시 사용 사례가 있기 때문에 적합하지 않습니다. –

관련 문제