本文来自 fair-jm.iteye.com 转截请注明出处
play的官方文档(for scala) 第二章的笔记 有兴趣的可以看下 有错误欢迎指出
1、Handling asynchronous results
来自 <https://www.playframework.com/documentation/2.3.x/ScalaAsync>
Play自下而上都是异步的。Play以异步非阻塞的方式处理每个请求。
在controllers理应该避免阻塞。常见的阻塞操作包括JDBC调用流API HTTP请求和长时间的计算
当然也可以通过增加默认的线程数来允许controllers里的阻塞代码承受更多的并发访问遵循以下的推荐可以保持controllers的异步以此来保持更高的扩展性和高负载下的可用性
创建非阻塞的actions
Play的工作方式需要action越快越好(无阻塞)。具体的做法是通过返回一个future的结果(Future[Result])作为回应。
Play将会在承诺被取回(一个Future对应一个Promise承诺取回也就是Future得到结果)时服务结果。
如何创建Future[Result]
所有的Play异步API都会给你Future
play.api.libs.WS
play.api.libs.Akka
例子:
import play.api.libs.concurrent.Execution.Implicits.defaultContext
val futureInt:Future[Int]= scala.concurrent.Future{
intensiveComputation()
}
注意上面用的线程池是默认的线程池也就是如果要处理很耗时的任务会阻塞线程那最好用其他的线程池而不是play的默认线程池
也可以使用Actor
返回Futures
import play.api.libs.concurrent.Execution.Implicits.defaultContext
def index =Action.async {
val futureInt = scala.concurrent.Future{ intensiveComputation()}
futureInt.map(i =>Ok("Got result: "+ i))
}
来自 <https://www.playframework.com/documentation/2.3.x/ScalaAsync>
Action默认就是异步的
Action.apply和Action.async的处理机制是一样的只有一种Action类型(异步),两者的差别只是返回值不同.
超时机制
超时机制的实现是相当简单的:
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
def index =Action.async {
val futureInt = scala.concurrent.Future{ intensiveComputation()}
val timeoutFuture = play.api.libs.concurrent.Promise.timeout("Oops",1.second)
Future.firstCompletedOf(Seq(futureInt, timeoutFuture)).map {
case i:Int=>Ok("Got result: "+ i)
case t:String=>InternalServerError(t)
}
}
来自 <https://www.playframework.com/documentation/2.3.x/ScalaAsync>
可见scala本身的Future提供的功能还是很完善的基础设施完善所需的额外代码就少了
------------------------------------------------------------------------------------------------------------------------------------
2 Streaming HTTP responses
自HTTP 1.1以来,保持单个连接开放来处理多个HTTP请求和回应的话,服务器必须在回应中包含一个合适的Content-Length HTTP头
def index =Action{
Ok("Hello World")
}
默认下,返回一个简单的结果是不会指定Content-Length的,当然简单的回应的发送信息是已知的,play也会计算这个长度并放在头中。(要注意基于文本的内容计算长度可不是看上去那么简单,因为他要根据特定的字符集来计算)
上面的代码是下面的简化写法,实际用到了play.api.libs.iteratee.Enumerator:
def index =Action{
Result(
header =ResponseHeader(200),
body =Enumerator("Hello World")
)
}
这意味着要正确地计算Content-Length Play必须消费整个enumerator并且把内容载入内存中计算
发送大数据量的内容
将小的数据量的内容全部读入当然没问题
那大数据量的怎么办呢?
一个错误的例子:
def index =Action{
val file =new java.io.File("/tmp/fileToServe.pdf")
val fileContent:Enumerator[Array[Byte]]=Enumerator.fromFile(file)
Result(
header =ResponseHeader(200),
body = fileContent
)
}
为什么错? 因为没设置Content-Length 然后play就要把整个吃进去计算实在是浪费资源…
正确的姿势:
def index =Action{
val file =new java.io.File("/tmp/fileToServe.pdf")
val fileContent:Enumerator[Array[Byte]]=Enumerator.fromFile(file)
Result(
header =ResponseHeader(200,Map(CONTENT_LENGTH -> file.length.toString)),
body = fileContent
)
}
接着Play就会用惰加载的方式消费内容了
提供文件(Serving files)
play也提供了一些简便的方式来实现上面的需求:
def index =Action{
Ok.sendFile(new java.io.File("/tmp/fileToServe.pdf"))
}
回应中增加的内容:
Content-Disposition: attachment;filename=fileToServe.pdf
(关于Content-Disposition:http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html)
提供自定义的文件名:
def index =Action{
Ok.sendFile(
content =new java.io.File("/tmp/fileToServe.pdf"),
fileName = _ =>"termsOfService.pdf"
)
}
浏览器直接显示(inline) 而不是下载:
def index =Action{
Ok.sendFile(
content =new java.io.File("/tmp/fileToServe.pdf"),
inline =true
)
}
分块响应(Chunked responses)
上面的例子是对于可以在传输文件之前得到文件长度的
那么如果对于动态计算的内容事先不能知道内容长度怎么处理呢?
要使用Chunked transfer encoding
Chunked transfer encoding是HTTP 1.1的一种数据传输机制
web服务器提供以一系列的块的方式来提供文件
他使用Transfer-Encoding回应头而不是Content-Length
因为没有使用Content-Length 所以服务器不需要在传输前知道内容的长度可以用于传输动态生成的文件
数据的结束是通过最后传一个长度为0的chunk结束的
好处: 可以处理活数据(数据可用就立马传输)
坏处: 浏览器就显示不了下载进度条了
play中要实现:
def index =Action{
val data = getDataStream
val dataContent:Enumerator[Array[Byte]]=Enumerator.fromStream(data)
ChunkedResult(
header =ResponseHeader(200),
chunks = dataContent
)
}
和上面一样也有种简便的方式:
def index =Action{
val data = getDataStream
val dataContent:Enumerator[Array[Byte]]=Enumerator.fromStream(data)
Ok.chunked(dataContent)
}
当然里面的Enumerator是可以自己定义的:
def index =Action{
Ok.chunked(
Enumerator("kiki","foo","bar").andThen(Enumerator.eof)
)
}
HTTP的回应:
HTTP/1.1200 OK 4 |
------------------------------------------------------------------------------------------------------------------------------------
3 Comet sockets
Comet socket仅仅是内容为text/html只包含<script>元素的chunked回应
我们可以向浏览器发送事件
def comet =Action{
val events =Enumerator(
"""<script>console.log('kiki')</script>""",
"""<script>console.log('foo')</script>""",
"""<script>console.log('bar')</script>"""
)
Ok.chunked(events).as(HTML)
}
也可以用play.api.libs.iteratee.Enumeratee
这个是一个适配器从Enumerator[A]转换到Enumerator[B]
import play.twirl.api.Html
// Transform a String message into an Html script tag
val toCometMessage =Enumeratee.map[String]{ data =>
Html("""<script>console.log('"""+ data +"""')</script>""")
}
def comet =Action{
val events =Enumerator("kiki","foo","bar")
Ok.chunked(events &> toCometMessage)
}
使用 play.api.libs.Comet
上面的代码可以简单地写成:
def comet =Action{
val events =Enumerator("kiki","foo","bar")
Ok.chunked(events &>Comet(callback ="console.log"))
}
实际上这个帮助类干了更多的事比如为了浏览器的兼容性发送一个初始化的空白缓冲数据
支持String和JSON可以扩展类型类来支持更多的消息类型
永远的iframe技术(The forever iframe technique)
标准的输出Comet socket的技术是载入一个无限的chunked comet回应在iframe里
并且调用父框架上的回调函数
def comet =Action{
val events =Enumerator("kiki","foo","bar")
Ok.chunked(events &>Comet(callback ="parent.cometMessage"))
}
html页:
<scripttype="text/javascript">
var cometMessage =function(event){
console.log('Received event: '+ event)
}
</script>
<iframesrc="/comet"></iframe>
------------------------------------------------------------------------------------------------------------------------------------
4 websocket
允许浏览器进行全双工通信
使用webSocket可以复用存在的play使用的TCP端口
处理 WebSocket
webSocket是个完全不同的野兽不能用标准的Action来驾驭
play提供了两种方式来驾驭WebSocket
第一种是使用actors
第二种是使用iteratees
两种方式都可以通过WebSocket这个构造者获得
通过Actor处理WebSocket
机制比较简单 Play会给用来发送数据的akka.actor.ActorRef
我们使用这个来创建一个akka.actor.Props
import play.api.mvc._
import play.api.Play.current
def socket =WebSocket.acceptWithActor[String,String]{ request => out =>
MyWebSocketActor.props(out)
}
actor:
import akka.actor._
objectMyWebSocketActor{
def props(out:ActorRef)=Props(newMyWebSocketActor(out))
}
classMyWebSocketActor(out:ActorRef)extendsActor{
def receive ={
case msg:String=>
out !("I received your message: "+ msg)
}
}
所有从客户端接收的数据都会发送给上面这个actor
任何发送给out那个ActorRef的数据都会发送给客户端
当WebSocket关闭时 Play会自动停止那个actor
想主动关闭一个websocket连接时也只需要关闭那个actor就行了
一颗毒药丸:
import akka.actor.PoisonPill
self !PoisonPill
(注:这个是触发 ActorKilledException默认的策略是Stop来实现的)
拒绝操作也比较简单改变就是方法从acceptWithActor变为了tryAcceptWithActor:
import scala.concurrent.Future
import play.api.mvc._
import play.api.Play.current
def socket =WebSocket.tryAcceptWithActor[String,String]{ request =>
Future.successful(request.session.get("user")match{
caseNone=>Left(Forbidden)
caseSome(_)=>Right(MyWebSocketActor.props)
})
}
上面的代码如果在session中没有user这个属性那么返回Forbidden否则建立连接
处理不同格式的消息
上面的例子全部都是基于String的 Play内置支持Array[Byte]和JsValue
比如:
import play.api.mvc._
import play.api.libs.json._
import play.api.Play.current
def socket =WebSocket.acceptWithActor[JsValue,JsValue]{ request => out =>
MyWebSocketActor.props(out)
}
也可以自定义需要处理的格式
比如下面这个例子i我们接收JSON消息并且将其转化为InEvent 返回的消息转化为OutEvent
第一件事是完成 InEvent和OutEvent的JSON转换:(PS:能直接用format说明InEvent和OutEvent是case class)
import play.api.libs.json._
implicitval inEventFormat =Json.format[InEvent]
implicitval outEventFormat =Json.format[OutEvent]
第二件事是创建FrameFormatter:
import play.api.mvc.WebSocket.FrameFormatter
implicitval inEventFrameFormatter =FrameFormatter.jsonFrame[InEvent]
implicitval outEventFrameFormatter =FrameFormatter.jsonFrame[OutEvent]
最后可以用于WebSocket:
import play.api.mvc._
import play.api.Play.current
def socket =WebSocket.acceptWithActor[InEvent,OutEvent]{ request => out =>
MyWebSocketActor.props(out)
}
使用Iteratees来处理WebSocket
Actors对于处理消息来说是个更好的抽象
Iteratee对于处理流来说是个更好的抽象
例子:
import play.api.mvc._
import play.api.libs.iteratee._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
def socket =WebSocket.using[String]{ request =>
// Log events to the console
val in =Iteratee.foreach[String](println).map { _ =>
println("Disconnected")
}
// Send a single 'Hello!' message
val out =Enumerator("Hello!")
(in, out)
}
这种方式最后返回的是那两个channel
in是Iteratee[A,Unit] 当接收到EOF时说明客户端那边关闭了socket
out是Enumerator[B] 当发送EOF时说明服务端这边关闭了Socket
第二个例子是忽视输入直接输出后关闭:
import play.api.mvc._
import play.api.libs.iteratee._
def socket =WebSocket.using[String]{ request =>
// Just ignore the input
val in =Iteratee.ignore[String]
// Send a single 'Hello!' message and close
val out =Enumerator("Hello!").andThen(Enumerator.eof)
(in, out)
}
Concurrent.broadcase可以用于广播:
import play.api.mvc._
import play.api.libs.iteratee._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
def socket = WebSocket.using[String]{ request =>
// Concurrent.broadcast returns (Enumerator, Concurrent.Channel)
val(out, channel)=Concurrent.broadcast[String]
// log the message to stdout and send response back to client
val in =Iteratee.foreach[String]{
msg => println(msg)
// the Enumerator returned by Concurrent.broadcast subscribes to the channel and will
// receive the pushed messages
channel push("I received your message: "+ msg)
}
(in,out)
相关推荐
《Android 4.X 应用与开发实战手册:适用Android 4.X-2.X(第2版)》还介绍了如何将应用程序上传到Google Play(原Android Market)供全球Android移动设备用户下载,以及如何将Ad Mob广告板置入应用程序,即使应用...
默认情况下与 Play 2.x + TypeScript + Webpack + Scalariform + Scalastyle + 配置的安全选项捆绑在一起的 Scala 项目模板。 内容 客户端: 替代JS :TypeScript 是 JavaScript 的超集,可以编译为干净的 ...
play2-auth, Play2.x 身份验证和授权模块 用于身份验证和授权的 Play2.x MODULE [Gitter](https://badges.gitter.im/Join chat 。svg )!这里 MODULE 为 Play2.x 应用程序提供身份验证和授权功能 Sc
### 如何部署Play 1.2.x:详细指南 Play框架是基于Java和Scala的轻量级Web开发框架,以其简单、快速的开发方式而受到欢迎。本文将详细介绍如何在Windows和Linux环境下部署Play 1.2.x版本,并提供具体的步骤和注意...
适用于Play框架2.x的Memcached插件 Play 2.x的Cache API的Memcached实现。 在内部使用spymemcached,与Play 1.x的默认Cache实现相同。 用法 将以下依赖项添加到您的Play项目中: 库依赖 对于Play 2.6.x及更高版本:!...
标题"play1.x连接mongodb"表明我们将讨论如何在Play 1.x版本中集成和使用MongoDB。首先,为了连接到MongoDB,你需要在项目中引入MongoDB的Java驱动程序和相关的ORM(对象关系映射)库。Morphia是一个常见的选择,它...
在发布APK到Google Play商店或者安装在设备上之前,开发者必须先用私钥对APK进行签名,以证明其来源并确保代码完整性。`signapk.jar`的工作原理是它接受一个公钥证书(`.x509.pem`)和一个私钥文件(`.pk8`),然后...
为了确保安全性和验证应用的来源,每个发布到Google Play或其他平台的APK都需要经过签名。"signapk.jar + .x509.pem + .pk8.zip" 是一个用于对Android APK进行系统级别签名的工具包,它涉及到Android应用签名过程中...
play2-war-plugin, 用于 Play Framework 2.x的WAR插件 用于 Play Framework 2.x的 WAR插件Current versions: Play 2.2.x : 1.2.1 Play 2.3.0 -> 2.3.1 : 1.3-beta1 (Scala 2.1
微信v7.0.21.1821谷歌版+obb数据包.rar
《Cocos2d-x 3.X游戏开发实战》是由肖文吉编著的一本专业级游戏开发指南,专为想要掌握Cocos2d-x 3.X框架进行游戏开发的程序员设计。Cocos2d-x是一个开源的游戏开发框架,广泛应用于跨平台的2D游戏制作,支持iOS、...
A macro can now be repeated by using the Repeat play function. You can specify the number of repeats, or let the macro run to the end of the file A new Code Assistant preference has been added: ...
在这个示例中,我们关注的是如何将Cocos2dx 3.x游戏与Google Play Game Services集成,以便实现游戏排行榜功能。Google Play Game Services为开发者提供了丰富的社交和竞技特性,比如成就、排行榜和多人游戏,这可以...
让play2.x支持ebean6.x 1,修改play项目的plugins.sbt //注释以在初始化期间获取更多信息logLevel:= Level.Warn //位于“ ”的Typesafe资源库解析器+ =“ Typesafe资源库” libraryDependencies ++ = Seq(“ ...
Google Play services_v22.42.12 (040400-481949630)_apkpure.com.xapk
WAR Play框架2.x插件Current versions: Play 2.2.x : 1.2.1 Play 2.3.0 -> 2.3.1 : 1.3-beta1 (Scala 2.10 & 2.11) Play 2.3.2+ : 1.3-beta3 (Scala 2.10 & 2.11) Play 2.4.x : 1.4.0 (Scala 2.10 & 2.11) Play 2.5....
黑群晖最经典的版本 DSM_DS918+_24922.pat
A macro can now be repeated by using the Repeat play function. You can specify the number of repeats, or let the macro run to the end of the file A new Code Assistant preference has been added: ...
Google Play Store_40.3.31-23 [0] [PR] 619730322_Apkpure.xapk