How to use Java libraries asynchronously in a Scala Play 2.0 application?

I see in the Play 2.0 Scala doc for invoking web services that the idiomatic approach is to use Scala arithmetic mechanisms to invoke web services. So if I use Java libraries, say, to download images from S3 and upload to Facebook and Twitter (restfb and twitter4j), it makes for a very inefficient use of resources (what resources?) Or doesn't matter much (or no difference at all )?

If it matters, how can I do something like the following asynchronous? Is there a quick way or will I have to write libraries from scratch?

Note that this will be performed on the hero if it matters in this discussion.

def tweetJpeg = Action(parse.urlFormEncoded) { request =>
  val form = request.body
  val folder = form("folder").head
  val mediaType = form("type").head
  val photo = form("photo").head
  val path = folder + "/" + mediaType + "/" + photo
  val config = Play.current.configuration;
  val awsAccessKey = config.getString("awsAccessKey").get
  val awsSecretKey = config.getString("awsSecretKey").get
  val awsBucket = config.getString("awsBucket").get
  val awsCred = new BasicAWSCredentials(awsAccessKey, awsSecretKey)
  val amazonS3Client = new AmazonS3Client(awsCred)
  val obj = amazonS3Client.getObject(awsBucket, path)
  val stream = obj.getObjectContent()    

  val twitterKey = config.getString("twitterKey").get
  val twitterSecret = config.getString("twitterSecret").get
  val token = form("token").head
  val secret = form("secret").head
  val tweet = form("tweet").head
  val cb = new ConfigurationBuilder();
  cb.setDebugEnabled(true)
    .setOAuthConsumerKey(twitterKey)
    .setOAuthConsumerSecret(twitterSecret)
    .setOAuthAccessToken(token)
    .setOAuthAccessTokenSecret(secret)
  val tf = new TwitterFactory(cb.build())
  val twitter = tf.getInstance()
  val status = new StatusUpdate(tweet)
  status.media(photo, stream)
  val twitResp = twitter.updateStatus(status)

  Logger.info("Tweeted " + twitResp.getText())
  Ok("Tweeted " + twitResp.getText())
}

def facebookJpeg = Action(parse.urlFormEncoded) { request =>
  val form = request.body
  val folder = form("folder").head
  val mediaType = form("type").head
  val photo = form("photo").head
  val path = folder + "/" + mediaType + "/" + photo
  val config = Play.current.configuration;
  val awsAccessKey = config.getString("awsAccessKey").get
  val awsSecretKey = config.getString("awsSecretKey").get
  val awsBucket = config.getString("awsBucket").get
  val awsCred = new BasicAWSCredentials(awsAccessKey, awsSecretKey)
  val amazonS3Client = new AmazonS3Client(awsCred)
  val obj = amazonS3Client.getObject(awsBucket, path)
  val stream = obj.getObjectContent()

  val token = form("token").head
  val msg = form("msg").head
  val facebookClient = new DefaultFacebookClient(token)
  val fbClass = classOf[FacebookType]
  val param = com.restfb.Parameter.`with`("message", msg)
  val attachment = com.restfb.BinaryAttachment`with`(photo + ".png", stream)
  val fbResp = facebookClient.publish("me/photos", fbClass, attachment, param)

  Logger.info("Posted " + fbResp.toString())
  Ok("Posted " + fbResp.toString())
}

      

My attempt at guessing:

  • Yes, it's better to do things asynchronously; you bind threads if you do everything synchronously. Threads are pigs of memory, so your server can only use that much; the more that are related to waiting, the fewer requests your server can respond to.
  • No, it's not a problem. With node.js (and Rails? Django?) This is a huge problem because there is only one thread and so it blocks your entire web server. The JVM server is multithreaded, so you can serve new requests.
  • You can easily wrap the whole thing up in the future, or do it in more detail, but you are not really buying anything because you are calling the same methods, so you just swap the wait from one thread to another.
  • If these Java libraries offer asynchronous methods, you can wrap them in the future to get the real benefits of asynchrony <-how to do? ... Otherwise, yes, you are looking at a record from scratch.
  • I don't know if it works for the hero. Is one dyno == one concurrent request?
+3


source to share


1 answer


I think it is best to execute these requests asynchronously for two main reasons:

  • high latency (network calls)
  • glitches

With Play, you have to use Akka actors to do your actions, and these are great ways to deal with those two issues. Synchronous code of the problem is that it will block the web server. Thus, it will not be available for other requests. Here we will make the wait in other threads not connected to the web server.

You can do something like:



// you will have to write the TwitterActor
val twitterActor = Akka.system.actorOf(Props[TwitterActor], name = "twitter-actor")

def tweetJpeg = Action(parse.urlFormEncoded) { request =>
    val futureMessage = (twitterActor ? request.body).map {
         // Do something with the response from the actor
         case ... => ...
     }
     async {
         futureMessage.map( message =>
             ok("Tweeted " + message)
         )
     }
}

      

Your actor will receive the body and send a response to the service. Plus, with Akka, you can customize your process to have multiple actors, have a circuit breaker ...

Next: http://doc.akka.io/docs/akka/2.1.2/scala/actors.html

Ps: I have never tried playing Heroku, so I don’t know the influence of one dynamo.

+1


source







All Articles