5

我想在协作参与者系统中隐式传播请求上下文。

为了简化和呈现这种情况,我的系统有多个参与者,传递给这些参与者的消息需要包含这个 RequestContext 对象。

ActorA 接收 MessageA 类型的消息 ActorB 接收 MessageB 类型的消息

当 ActorA 需要向 ActorB 发送消息时,作为 MessageA 处理的一部分,它会执行业务逻辑,然后根据逻辑结果以及 MessageA 中可用的 RequestContext 构造一个 MessageB,然后将其发送给 ActorB

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

我们要处理大量消息,并且显式传递 requestContext 很麻烦。

我正在尝试使用 Scala 的隐式功能的创造性方法来避免将嵌入在传入消息中的 RequestContext 显式注入到传出消息中。

这些消息是案例类(它们必须是)。我已经阅读了有关隐式规则的内容,但是将对象的属性带入当前的隐式范围似乎有些牵强。

我相信这应该是一个普遍的要求。有什么建议么 ?

谢谢。

4

3 回答 3

6

在您的示例中,您对相关消息的处理已经被分解为一个方法,这使得这很简单:

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)
  }
}

但正如您所见,在声明消息类型时仍然存在一些开销,并且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)
}
于 2013-04-06T16:38:50.610 回答
6

在我看来,最简单的方法是让你的 val 隐含在案例类中。

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)
}
于 2013-04-06T18:23:48.830 回答
0

创建一个包含原始消息和上下文的通用信封类。创建一个特征扩展Actor(你必须把它放在akka._包中)。解包信封的覆盖方法aroundReceive(),初始化参与者的受保护变量以存储当前上下文,使用解包消息调用原始接收方法,取消初始化上下文变量。相当可疑的方法,但这正是Actor.sender()行为方式。

要将消息发送给这样一个上下文绑定的参与者,您必须手动将消息包装到上面提到的信封中,或者您也可以通过引入一个扩展来自动执行此任务,该扩展ActorRef通过将消息提升到上下文来实现告诉/询问操作-边界。

这不仅仅是一个概念,而是我最近成功尝试的方法的简要总结。此外,我通过引入另一个抽象概念对它进行了更多概括——隐式上下文环境,如基于 actor、基于 ThreadLocal、基于 Future 等,这使我能够轻松地在所有同步/异步操作链中隐式传递上下文数据。

于 2015-04-27T19:33:01.457 回答