1、处理异步结果

       在内部,play框架是自下而上异步的。Play以异步、非阻塞方式处理每个请求。应用程序代码应尽量避免阻塞控制器,这种阻塞操作的常见例子有JDBC调用、流式API、HTTP请求和长计算。因此应尽量通过保持控制器异步的方法使得应用进行扩展,使系统在负载下保持响应。就是说,对于控制器中的一些阻塞的操作,尽量使用异步的方式进行处理。

CompletableFuture,这两个类不具体介绍,只需要知道可以实现使得程序异步又可以获取返回值,CompletionStage<result>最终将用result类型的值。返回CompletionStage<result>是一种编写非阻塞代码的技术。可以使用如下方式获取:

java stream parallel foreach 异步 java异步http_套接字

        实现异步处理、非阻塞编程的关键就在于CompletionStage,下边介绍一下如何使用,第一种可以通过配合HttpExecutionContext使用,可以通过依赖项注入play提供的play.libs.concurrent.httpExecutionContext实例,如下:

java stream parallel foreach 异步 java异步http_套接字_02

       只是使用HttpExecutionContext和CompletableFuture还是使用的play默认的上下文执行器,还需要使用CustomExecutionContext和HttpExecution实现非阻塞编程,如下:

java stream parallel foreach 异步 java异步http_分块_03

java stream parallel foreach 异步 java异步http_java_04

可以通过使用实现响应超时问题的处理:

java stream parallel foreach 异步 java异步http_套接字_05

2、流式HTTP响应

       为了保持单个连接的开放性以服务于多个HTTP请求和响应,服务器必须随响应一起发送适当的内容长度的HTTP头。对于响应一些简单的文本结果,play可以自动识别并自动添加头信息返回客户端,但是对于一些复杂的内容,就需要开发者自己指定头信息,play提供了类play.http.httpentity用来实现此功能,使用如下:   

java stream parallel foreach 异步 java异步http_套接字_06

       在这里就有一个问题,为了计算头长度获取头信息,就需要将所有返回内容加载到内存,这里就可能存在问题:返回内容太大怎么办?下边先来一个返回结果的例子:     

java stream parallel foreach 异步 java异步http_java_07

       这里返回到客户端一个流对象,由于需要判断头信息,而play不能在流中直接获取头信息,这是就需要将整个流添加到内存中进行分析。Play中对于此问题,早已提供实现,直接调用ok(new File(“文件路径”))即可。

       上边这种方式返回的文件大小必须是确定的,对于文件大小不能确定的、动态变化的就需要使用分块传输编码技术,使用如下: 

java stream parallel foreach 异步 java异步http_java_08

3、Comet套接字

      使用分块传输编码技术实现Comet。Comet套接字是一个分块的text/HTML响应,只包含<script>元素。对于每个块,我们编写一个包含JavaScript的<script>标记,该标记立即由Web浏览器执行。这样我们就可以从服务器向Web浏览器实时发送事件:对于每条消息,将其包装成一个调用javascript回调函数的<script>标记,并将其写入分块响应。

       因为ok().chunked利用akka流获取流<bytestring>,所以我们可以发送一个元素流并对其进行转换,以便在javascript方法中对每个元素进行转义和包装。Comet帮助程序自动执行Comet套接字,为浏览器兼容性推送初始空白缓冲区数据,并支持字符串和JSON消息。

下边介绍几个Comet的使用:

字符串流中使用Comet:    

java stream parallel foreach 异步 java异步http_HTTP_09

Json流中使用Comet:

java stream parallel foreach 异步 java异步http_java_10

Iframe中使用Comet:

java stream parallel foreach 异步 java异步http_分块_11

4、WebSockets(网络套接字)

        WebSockets是基于允许双向全双工通信的协议从Web浏览器使用的套接字。只要服务器和客户机之间有活动的WebSocket连接,客户机就可以发送消息,服务器就可以随时接收消息。WebSokets不能通过标准动作来处理。Play的WebSocket处理机制是围绕Akka流构建的。WebSocket被建模为流,传入的WebSocket消息被送入流中,流生成的消息被发送到客户机。play提供了一些在WebSocket中构造WebSocket的工厂方法。

接下来介绍play中如何处理websocket。

       使用actors处理websocket,可以使用play实用程序ActorFlow将actorRef转换为流。此实用程序接受一个函数,该函数将actorRef转换为向akka.actor.props对象发送消息,该对象描述在接收WebSocket连接时play应创建的参与者:  

java stream parallel foreach 异步 java异步http_java_12

java stream parallel foreach 异步 java异步http_HTTP_13

从客户端接收到的任何消息都将发送给参与者,而由play提供的任何消息都将发送给客户端,当websocket关闭时,actor将被停止,可以通过实现actors的 poststop方法来处理需要关闭的资源:

java stream parallel foreach 异步 java异步http_java_14

有时候希望拒绝WebSocket请求,例如,如果必须对用户进行身份验证才能连接到WebSocket,Play为此目的提供了AcceptorResult  WebSocket  Builder:

想要实现异步接收WebSocket,直接使用CompletionStage<WebSocket<A>>代替WebSocket<A>即可。

处理其他类型数据的请求:

java stream parallel foreach 异步 java异步http_分块_15

还可以直接使用akka流处理WebSockets,如下:

java stream parallel foreach 异步 java异步http_java_16

WebSocket可以访问请求头(来自启动WebSocket连接的HTTP请求),但是,它不能访问请求主体,也不能访问HTTP响应

发送hello之后丢弃输入数据并关闭套接字:

java stream parallel foreach 异步 java异步http_套接字_17

输入数据记录到标准输出,然后使用映射流发送回客户机:

java stream parallel foreach 异步 java异步http_分块_18

配置websocket的帧长度有两种方式,在启动应用是使用:

play.server.websocket.frame.maxLength 或者
Sbt -Dwebsocket.frame.maxLength=256k run