如何在Scala Play 2.0应用程序中异步使用Java库?

时间:2013-02-07 15:38:51

标签: scala playframework-2.0

我在Play 2.0 Scala doc for calling web services中看到惯用方法是使用Scala的异步机制来调用Web服务。因此,如果我使用Java库,比如从S3下载图像并上传到Facebook和Twitter(restfb和twitter4j),这是否会导致资源的高效使用(什么资源?)或者它没有太大区别(或者根本没有区别)?

如果它有所作为,我将如何制作类似下面的异步?有快速的方法,还是我必须从头开始编写库?

注意这将在heroku上运行,如果这在讨论中很重要。

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

我的猜测:

  1. 是的,最好是异步做事;如果您同步执行所有操作,那么就会占用线程。线程是内存占用,所以你的服务器只能使用这么多;捆绑等待的次数越多,服务器响应的请求就越少。
  2. 不,这不是巨大的问题。使用node.js(和Rails?Django?), 是一个很大的问题,因为只有一个线程,因此它会阻止整个Web服务器。 JVM服务器是多线程的,因此您仍然可以为新请求提供服务。
  3. 您可以轻松地将整个事物包装在未来,或者更精细地完成它,但是这并不能真正为您买任何东西,因为您调用相同的方法,所以您只是从一个线程转移等待另一个。
  4. 如果这些Java库提供异步方法,您可以将这些方法包装起来,以获得异步< -how to do?的真正好处。否则是的,你正在考虑从头开始写作。
  5. 不知道在heroku上运行是否很重要。是一个dyno ==一个同时请求吗?

1 个答案:

答案 0 :(得分:1)

我认为最好异步执行这些请求有两个主要原因:

  • 高延迟(网络电话)
  • 故障

使用Play,你应该使用Akka演员来制作你的行动,它提供了处理这两个问题的好方法。 问题同步代码是它将阻止Web服务器。因此它不会被其他请求使用。在这里,我们将在与Web服务器无关的其他线程中进行等待。

您可以执行以下操作:

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

你的演员会收到尸体并发回服务的回复。 此外,通过Akka,你可以调整你的过程,让几个演员可用,有一个断路器......

更进一步:http://doc.akka.io/docs/akka/2.1.2/scala/actors.html

Ps:我从未尝试过在Heroku上玩,所以我不知道单个发电机的影响。