简体   繁体   中英

Injecting an ActorSystem, with actors created by Guice, in Play 2.4 for Scala

What I want to do, is send out a bunch of emails, where the content is changed a little bit for each user (their name and so on).

Problem is, that it's incredibly slow to iterate trough the list of users, and and send each one, after waiting for the blocking previous one.

So I thought I'd do it in parallel, using Akka.

I use

  • Play 2.4.3
  • play-mailer 3.0.1

This is my actor:

import javax.inject.Inject
import akka.actor._
import play.api.libs.mailer.{MailerClient, Email}

object EmailActor {
  def props = Props[EmailActor]
  case class SendEmail(email: Email)
}

class EmailActor @Inject()(mailerClient: MailerClient) extends Actor {
  import EmailActor._

  def receive = {
    case SendEmail(email: Email) =>
      // send mail
      mailerClient.send(email)
      sender() ! "Sent"
  }
}

I managed to inject my actor directly into my controller, but it's still slow, and I guess it's using the same actor for each message. I want a bunch of actors working on my emails!

This is my module

class MyModule extends AbstractModule with AkkaGuiceSupport {
  override def configure = {
    bindActor[EmailActor]("email-actor")
  }
}

This is my Controller

@Singleton
class Emails @Inject()(system: ActorSystem) extends Controller with InjectedActorSupport {
  ...

  def sendEmailToUsers(event : Event, request:RequestHeader) : Future[Seq[String]] = {
    implicit val timeout : akka.util.Timeout = 5.seconds

    val results = event.people
      .map(user => {
        val actor = system.actorOf(EmailActor.props, "email-actor")
        (actor ? SendEmail(createEmail(event, user, request))).mapTo[String]
      })

    Future.sequence(results)
  }

I get the exception

IllegalArgumentException: no matching constructor found on class actors.EmailActor for arguments []

I'm not surprised that the ActorSystem in Akka doesn't use Plays dependency injection. So what should I do?

Question

How can I (using best practices in Play) , send e-mails out in parallel?

change controller

class Emails @Inject()(@Named("email-actor") emailActor:ActorRef)(implicit ec: ExecutionContext) extends Controller{

   def sendEmailToUsers(event : Event, request:RequestHeader) : Future[Seq[String]] = {
      implicit val timeout : akka.util.Timeout = 5.seconds

      val results = event.people.map(user => {
         (emailActor ? SendEmail(createEmail(event, user, request))).mapTo[String]
      })
      Future.sequence(results)
   }
}

Please check first that the SMTP server is not throttling you. ;-)

Take a look at Akka routing . Refactor your actor to a gateway and delegate the email sending to worker actors via a router. You can use RoundRobinRoutingLogic or SmallestMailboxRoutingLogic . But don't create too many worker Actors or the SMTP server may throttle/block you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM