Я хотел бы неявно распространять контекст запроса в системе взаимодействующих субъектов.
Чтобы упростить и представить ситуацию, моя система имеет несколько субъектов, и сообщения, передаваемые этим субъектам, должны включать этот объект RequestContext.
ActorA получает сообщения типа MessageA ActorB получает сообщения типа MessageB
когда ActorA необходимо отправить сообщение ActorB, как часть обработки MessageA, он выполняет бизнес-логику, а затем создает MessageB из результатов логики, а также RequestContext, доступного в MessageA, и затем отправляет его ActorB.
def handle(ma:MessageA) {
val intermediateResult = businessLogic(ma)
actorB ! MessageB(intermediateResult, ma.requestContext)
}
У нас есть множество сообщений, которые нужно обработать, и явная передача requestContext обременительна.
Я пытаюсь найти творческие способы использования неявной функции Scala, чтобы избежать явного внедрения RequestContext, встроенного во входящее сообщение, в исходящее сообщение.
Сообщения являются классами case (и они должны быть). Я читал о неявных правилах, но перенос атрибута объекта в текущую неявную область видимости кажется надуманным.
Это, я уверен, должно быть общим требованием. Какие-либо предложения ?
Спасибо.
Ответ 2 · рейтинг 6 · Roland Kuhn
В вашем примере ваша обработка рассматриваемого сообщения уже учтена в методе, что делает это прямолинейным:
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)
}
Ответ 3 · Jiří Vypědřík
Создайте универсальный класс конверта, содержащий как исходное сообщение, так и контекст. Создайте трейт, расширяющий Actor (вы должны поместить его в пакет akka._). Переопределить метод aroundReceive(), который распаковывает ваш конверт, инициализирует защищенную переменную актера для хранения текущего контекста, вызывает исходный метод получения с распакованным сообщением, деинициализирует переменную контекста. Довольно сомнительный подход, но именно так ведет себя Actor.sender().
Чтобы отправить сообщение такому контекстно-зависимому действующему лицу, вы должны вручную завернуть свое сообщение в упомянутый выше конверт, или вы также можете автоматизировать эту задачу, введя расширение над ActorRef, которое реализует операции Tell/Ask, поднимая сообщение до контекстно-зависимый.
Это не просто концепция, а краткое изложение подхода, который я недавно успешно опробовал. Более того, я еще больше обобщил его, введя еще одно абстрактное понятие - среды неявного контекста, такие как на основе акторов, на основе ThreadLocal, на основе Future и т. д., что позволило мне легко передавать данные контекста по всей цепочке синхронных/асинхронных операций неявным образом.