Модульная функция отправки сообщений Akka

#scala #akka #spray

#scala #akka #распыление

Вопрос:

Я работаю с Spray Akka (я хочу сказать «Фреймворк», но они хотят, чтобы их называли «Библиотекой»). Я получаю вызов REST и возвращаю ответ. Однако я нахожу свою обработку немного повторяющейся. Вот два блока, когда я получил POST запрос от двух API:

 post {
                entity(as[JObject]) { company =>
                  if (AuthInfo.rejectedOrNot) {
                    val response = (secCompanyActor ? jObjectFromWeb(company))
                      .mapTo[TransOk]
                      .map(result => result.succeedOrNot match {
                      case true => (OK, "transaction successful")
                      case false => (BadRequest, result.errorMessage)
                    }).recover { case _ => (BadRequest, "An error has occurred! We will fix this")}
                    complete(response)

                  } else {
                    complete(Unauthorized, "Please use HTTP header to authorize this command.")
                  }
                }
  

Другой блок поразительно похож, но единственное отличие заключается в том, что вместо отправки jObjectFromWeb(...) я должен отправить это сообщение: jObjectFromComputer(...) .

Я хотел создать функцию, в которой я передаю сообщение, а затем отправляю сообщение из этой функции, но сообщения Akka не печатаются! Я понимаю философию нетипизированного сообщения и ни за что не спорю, но мне нужно хорошее решение. Да, одно из решений заключается в том, что я передаю строку типа sendMessageToActor(actor, "jObjectFromWeb") , а в самой функции я использую соответствие шаблону для отправки соответствующего сообщения.

Но она немного уродлива и не очень модульна (что, если мне нужно отправить 10 сообщений?).

Есть ли какой-либо способ сделать это? Я не совсем знаком с отражением Java, но было бы очень хорошо, если бы я мог просто найти класс / объект обращения к сообщению через строку.


Это моя часть реализации актера Akka:

 class SECCompanyActor extends Actor with ActorLogging {
  import com.mturk.tasks.SECcompany.SECCompanyProtocol._

  def receive = {

    case jObjectFromCasper(jObject) =>
      //Do some logic
      sender ! TransOk(result._1, result._2, result._3)

    case jObjectFromWeb(jObject) =>
      // Do some other logic
      sender ! TransOk(result._1, result._2, result._3)
  }
  

Вот мое сообщение Akka:

 object SECCompanyProtocol {
  case class jObjectFromCasper(jObject: JObject)
  case class TransOk(company: Option[Company.Company], succeedOrNot: Boolean, errorMessage: Option[String])
  case class jObjectFromWeb(jObject: JObject)
}
  

Ответ №1:

Как насчет этого:

 def doPost(messageConstructor: Company => AnyRef): Route = {
  entity(as[JObject]) { company =>
    if (AuthInfo.rejectedOrNot) {
      val response = (secCompanyActor ? messageConstructor(company))
        .mapTo[TransOk]
        .map(result => result.succeedOrNot match {
          case true => (OK, "transaction successful")
          case false => (BadRequest, result.errorMessage)
        }).recover { case _ => (BadRequest, "An error has occurred! We will fix this")}
      complete(response)
    } else {
      complete(Unauthorized, "Please use HTTP header to authorize this command.")
    }
  }
}    

post {
  doPost(jObjectFromWeb.apply)
}
  

Как указано в вашем комментарии, это работает, когда jObjectFromWeb / jObjectFromComputer являются классами case. jObjectFromWeb.apply соответствует apply методу сопутствующего объекта класса case, который автоматически создается для классов case. Более типизируемый дизайн наследовал бы оба класса case от одного признака:

 trait GenericJObject {
  val company: Company
}

case class JObjectFromWeb(override val company: Company) extends GenericJObject

case class JObjectFromComputer(override val company: Company) extends GenericJObject

def doPost(messageConstructor: Company => GenericJObject): Route = {
...
}
  

Кроме того, для удобства сопровождения и понимания вашего кода было бы лучше соблюдать соглашение о кодировании Scala, где имена классов начинаются с заглавной буквы, иначе кто-то может запутаться и предположить, что jObjectFromXXX это метод, а не класс case.

Комментарии:

1. Ну, хотя Akka.ask может использоваться с методами, jObjectFromWeb это не метод, а message . Это класс case. Однако ваше предложение может быть более интересным, но я хочу использовать Akka Actor прямо сейчас вместо Akka Future. (Приводятся аргументы, предполагающие, что Future это лучше, чем Actor )

2. Только что отредактировал ее, как я уже говорил в отредактированной версии, я запутался, потому что обычно в Scala методы начинаются со строчной буквы, а классы — с прописной.

3. ДА! Это моя вина. Мне нужно это изменить. Спасибо!

4. Извините! Одна маленькая вещь: я начинаю путаться, потому что я незнаком с используемой вами конструкцией (я понимаю apply ). Во-первых, doPost() я все еще отправляю prepareMessage ?

5. Я только что исправил опечатку: вызов ask должен получать messageConstructor(company) вместо prepareMessage(company) . Короче говоря, в Scala вы можете использовать любой объект так, как если бы это была функция, определив для него apply метод. Итак, когда вы вызываете, JObjectFromWeb(company) вы на самом деле вызываете JObjectFromWeb.apply(company) . При использовании классов case компилятор Scala автоматически определяет apply метод для своего сопутствующего объекта, который вызывает конструктор класса с теми же параметрами.