2013-04-06 2 views
5

저는 액터 협업 시스템에 암묵적으로 요청 컨텍스트를 전파하고자합니다.액터 시스템에 암묵적으로 요청 컨텍스트 전달

상황을 단순화하고 표시하려면 시스템에 여러 개의 액터가 있고이 액터로 전달 된 메시지에이 RequestContext 객체가 포함되어 있어야합니다.

ActorA 타입 MessageA의 메시지를 수신 ActorB 타입 MessageB의 메시지 ActorA가 MessageA의 처리의 일부로서, ActorB에 메시지를 전송할 필요

, 그것은 비즈니스 로직을 수행하고, 그 다음부터 MessageB를 구성 받는다 다음 논리뿐만 아니라 MessageA에서 사용할 수있는 RequestContext의 결과와는

def handle(ma:MessageA) { 
val intermediateResult = businessLogic(ma) 
actorB ! MessageB(intermediateResult, ma.requestContext) 
} 

을 ActorB에 보내는 우리는 메시지의 회전을 어떻게 처리해야하고, 명시 적으로 requestContext 주위에 통과하는 것이 부담이다.

스칼라의 implicits 기능을 사용하여 들어오는 메시지에 포함 된 RequestContext를 나가는 메시지에 명시 적으로 주입하는 것을 피하기 위해 창의적인 방법을 시도하고 있습니다.

메시지는 사례 클래스이며 필요합니다. implicits 규칙에 대해서 읽었지 만, 객체의 속성을 현재 암시 적 범위로 가져 오는 것은 상당히 어려워 보인다.

이것은 공통 요구 사항이어야합니다. 제안 사항이 있으십니까?

감사합니다.

trait RequestContext 

case class MessageA(req: RequestA, ctx: RequestContext) 
object MessageA { 
    def apply(req: RequestA)(implicit ctx: RequestContext) = MessageA(req, ctx) 
} 

case class MessageB(req: RequestB, ctx: RequestContext) 
object MessageB { 
    def apply(req: RequestB)(implicit ctx: RequestContext) = MessageB(req, ctx) 
} 

class Example extends Actor { 

    def receive = { 
    case MessageA(req, ctx) => handle(req)(ctx) 
    } 

    def handle(req: RequestA)(implicit ctx: RequestContext): Unit = { 
    val intermediateResult = businessLogic(req) // could take implicit ctx as well 
    actorB ! MessageB(intermediateResult) 
    } 
} 

그러나 선언 할 때 약간의 오버 헤드가 아직 거기 당신이 볼 수 있듯이이 바로 전달하게 문제의 메시지 당신의 처리는 이미 방법으로 밖으로 고려 귀하의 예에서

답변

6

는 가장 쉬운 방법은 경우 클래스에 발을 암시하는 것입니다.

case class MessageA(req: RequestA)(implicit val ctx: RequestContext) 

case class MessageB(req: RequestB)(implicit val ctx: RequestContext) 

def businessLogic(req:RequestA):RequestB 


def handle(ma: MessageA): Unit = { 
    // import all the members of ma so that there is a legal implicit RequestContext in scope 
    import ma._ 
    val intermediateResult = businessLogic(req) 
    actorB ! MessageB(intermediateResult) 
} 
+0

답변 해 주셔서 감사합니다. 나는이 접근법을 진지하게 고려했지만 필자는 매개 변수를 명시 적으로 전달하는 것이 기능을 사용하기 위해 모든 수입과 함축을 추가하는 것보다 훨씬 읽기 쉽고 덜 장황하다고 생각했다. – vishr

+0

이것이 문제라면 MessageA => MessageB 인 메시지 프로세서를 제외하고 액터 몸체에서 호출하십시오. 이것은 또한 테스트 가능성을 향상시킵니다. – Edmondo1984

6

메시지 유형 및 handle 메소드의 서명도 변경해야합니다. 이 제도가 가치가 있는지 여부는 이러한 암시 적 값의 소비자와 생산자 사이의 비율에 따라 달라집니다 (즉, handle에있는 두 가지 이상의 것이 더 의미가있는 경우).

의 변형은 위의 수 : 제 생각에는

case class MessageA(req: RequestA, ctx: RequestContext) 
object MessageA { 
    def apply(req: RequestA)(implicit ctx: RequestContext) = MessageA(req, ctx) 
    implicit def toContext(implicit msg: MessageA) = msg.ctx 
} 

case class MessageB(req: RequestB, ctx: RequestContext) 
object MessageB { 
    def apply(req: RequestB)(implicit ctx: RequestContext) = MessageB(req, ctx) 
    implicit def toContext(implicit msg: MessageB) = msg.ctx 
} 

... 
def handle(implicit ma: MessageA): Unit = { 
    val intermediateResult = businessLogic(req) 
    actorB ! MessageB(intermediateResult) 
} 
+0

왜 암시 적 메시지입니까? 암묵적이어야하는 요청이 아닌가? 내 대답을보십시오 – Edmondo1984

+0

@ 롤랜드, 당신의 아이디어는 깔끔한 접근법에 영감을 불어 넣었습니다. MessageA와 MessageB가 AbstractMessage를 상속하고 AbstractMessage에서 Companion 객체 내의 RequestContext로 암시 적 변환을 제공하면 문제가 해결됩니다! 각 메시지가해야 할 일은 암시 적 요청 컨텍스트 매개 변수를 선언하는 것 뿐이며, 사용할 수있는대로 전달됩니다. – vishr

+0

음 ... 나는 흥분했다 ... 암시적인 MessageA 매개 변수를 사용하여 모든 handle 메소드를 표시하는 것이 작동하려면 필요하지만 읽을 수는 없다는 것이 밝혀졌다. 암시 적 선언은 비즈니스 로직 코드에 너무 많이 전파됩니다. – vishr

0

원본 메시지와 컨텍스트를 모두 포함하는 일반 봉투 클래스를 만듭니다. Actor의 특성을 만듭니다 (akka._ 패키지에 넣어야합니다). 봉투를 언팩하는 오버라이드 메소드 aroundReceive()은 현재 컨텍스트를 저장하기 위해 액터의 보호 된 변수를 초기화하고 원래의 수신 메소드를 압축 해제 된 메시지로 호출하고 컨텍스트 변수를 초기화하지 않습니다. 오히려 모호한 접근이지만, 이것은 정확히 Actor.sender()이 어떻게 행동 하는지를 보여줍니다.

이러한 컨텍스트 바운드 액터에 메시지를 보내려면 위에서 언급 한 봉투에 메시지를 수동으로 포장해야합니다. 또는 ActorRef 이상의 확장 기능을 도입하여이 작업을 자동화 할 수도 있습니다. 상황에 맞는 메시지를 들려줍니다.

이것은 단순한 개념이 아니라 최근에 성공으로 시도한 접근 방식에 대한 간략한 요약입니다. 게다가, 나는 액터 기반, ThreadLocal 기반, Future 기반 등과 같은 암시 적 컨텍스트 환경을 도입하여 암시 적으로 모든 동기화/비동기 체인을 통해 컨텍스트 데이터를 쉽게 전달할 수있게 해주는 또 다른 추상적 개념을 도입하여 좀 더 일반화했습니다.