本章译者:@Sam Liu (译者未留下自己的主页,请Sam Liu见此文,加入群168013302联系‘大黄蜂@翻译play’)

这一章主要讲解如何运用异步模式实现典型的长连接(long-polling)、流(streaming)和 推送方式(Comet-style ) 的编程,以便于响应数以千万计的并发请求。

延缓(Suspending) HTTP 请求

Play主要是用于处理很短的请求。它使用一个固定的线程池来处理用户的HTTP请求。为获得最佳效果,这个线程池当然是越小越好。我们一般用@处理器数+1@这个比较恰当的值来作为默认的池大小。

这也就意味着如果一个请求持续很长时间(比如等待一个长时间的计算)它就会阻塞线程池并且降低服务的响应能力。当然你也可以给池中增加更多的线程,但是那会浪费很多的资源而且池的大小总不可能是无限的。

设想一个聊天服务,当各个浏览器终端发出请求等待显示新的消息。这些请求一般都会很长(通常都要好几秒钟)这必然会阻塞线程池。如果你打算同时支持100个人聊天,那你就要准备至少100个线程。好吧这不算什么,但是如果是1000个呢?10000个呢?

为了解决这类总是,Play允许你临时延缓(suspend)一个请求。这个HTTP请求将保持连接,但会被推出线程池稍后再试。你可以告诉Play在适当的延迟后或得到一个@Promise@的返回值后再次处理这个请求。

Tip. 你可以看一个实例:@samples-and-tests/chat@.

例如,这个action会处理一个很长时间的job并且等到完成后再返回结果到HTTP response:

public static void generatePDF(Long reportId) {
Promise<InputStream> pdf = new ReportAsPDFJob(report).now();
InputStream pdfStream = await(pdf);
renderBinary(pdfStream);
}

这里我们使用了@await(…)@来让Play延缓处理这个请求,直到返回@Promise@的结果。

Continuations

为了重新获得之前响应其他请求的线程,框架必须暂停执行你的代码。在Play之前的版本使用的是@waitFor(…)@,也就是现在所说的@await(…)@,用它来延缓你的action,稍后再回来执行。

为了更方便地处理异步的代码,我们来介绍一下continuations。Continuations可能让你的代码很自然地暂停后再重新开始。于是你可以象下命令一样来写你的代码,如下:

public static void computeSomething() {
Promise<String> delayedResult = veryLongComputation(…);
String result = await(delayedResult);
render(result);
}

实际上在这儿,你的代码会被分为两步,在两个不同的线程中来执行。但正如你所看到的,在你的代码中是感觉不到的。

你可以使用 await(…) 和 continuations 来写一个循环:

public static void loopWithoutBlocking() {
for(int i=0; i<=10; i++) {
Logger.info(i);
await("1s");
}
renderText("Loop finished");
}

即使是在开发模式,默认只有一个线程来处理请求,Play也可以在同一时间并行地运行这些循环。

HTTP 输出流

现在你可以运行这类循环而不必担心阻塞线程,同时你肯定还想每当取得一部分有效的结果时就把结果发到浏览器客户端。这就是@Content-Type:Chunked@这个HTTP输出类型的作用。它可以使你用多个chunks来分批发送HTTP结果。浏览器则会马上显示出收到的结果。

使用 await(…) 和 continuations, 你现在就可以实现它了:

public static void generateLargeCSV() {
CSVGenerator generator = new CSVGenerator();
response.contentType = "text/csv";
while(generator.hasMoreData()) {
String someCsvData = await(generator.nextDataChunk());
response.writeChunk(someCsvData);
}
}

即使这个CSV的生成要耗费一个小时,Play也可以用一个线程同时处理多次请求,并且每当有最新的结果时就返回给客户端。

使用 WebSockets

WebSockets是一个连接浏览器和应用服务的双向通信通道。在浏览器一边,你可以用“ws://”打开一个socket通道:

new Socket("ws://localhost:9000/helloSocket?name=Guillaume")

而在Play这边,你可以声明一个WS的route:

WS   /helloSocket            MyWebSocket.hello

MyWebSocket 是一个 WebSocketController。 一个 WebSocket 的 controller 和一个标准的 HTTP controller 类似,但也有一些不同的概念:

  • 它有一个请求 request 对象,但却没有 response 返回对象。
  • 它可以访问 session,但是只读的。
  • 它没有 renderArgsrouteArgs 或 flash scope 。
  • 它只能从 route 路径或 QueryyString 路径字串中读取参数。
  • 它有两个通信通道: inbound 输入 和 outbound 输出。

当客户端连接 ws://localhost:9000/helloSocket socket 通道,Play 就会运行 MyWebSocket.hello action 方法。一旦 MyWebSocket.hello action 方法退出,这个 socket 通道就关闭了。

下面是一个非常简单的socket例子:

public class MyWebSocket extends WebSocketController {
public static void hello(String name) {
outbound.send("Hello %s!", name);
}
}

在这个例子中,客户端连接socket,收到“Hello Guillaume”的消息,然后这个 socket 就关闭了。

当然了通常你并不想马上关闭这个 socket。而使用 await(…) 可以很容易地保持连接。

如下是一个非常简单的 Echo 响应服务:

public class MyWebSocket extends WebSocketController {
public static void echo() {
while(inbound.isOpen()) {
WebSocketEvent e = await(inbound.nextEvent());
if(e instanceof WebSocketFrame) {
WebSocketFrame frame = (WebSocketFrame)e;
if(!e.isBinary) {
if(frame.textData.equals("quit")) {
outbound.send("Bye!");
disconnect();
} else {
outbound.send("Echo: %s", frame.textData);
}
}
}
if(e instanceof WebSocketClose) {
Logger.info("Socket closed!");
}
}
} }

在上面的例子中,多层嵌套的“if“ 和 ”cast“ 写起来很烦而且极易出错。在这儿就体现出Java的糟糕之处。即使象这样一个简单的示例都这么难处理,如果碰到更复杂的情况,比如你要组合多个数据流,并且有更多的事件类型,那将会是一场恶梦。

因此我们要介绍一个Java的简单匹配模式,是在 play.libs.F 这个函数式编程的库中。

那么之前的Echo的例子就可以重写为下面这样:

public static void echo() {
while(inbound.isOpen()) {
WebSocketEvent e = await(inbound.nextEvent());
for(String quit: TextFrame.and(Equals("quit")).match(e)) {
outbound.send("Bye!");
disconnect();
}
for(String msg: TextFrame.match(e)) {
outbound.send("Echo: %s", frame.textData);
}
for(WebSocketClose closed: SocketClosed.match(e)) {
Logger.info("Socket closed!");
}
}
}

继续这个话题

接下来, 实现 Ajax 请求.

Play!中使用HTTP异步编程的更多相关文章

  1. JS中的同步异步编程

    首先我们先看看同步与异步的定义,及浏览器的执行机制,方便我们更好地理解同步异步编程. 浏览器是多线程的,JS是单线程的(浏览器只分配一个线程来执行JS)   进程大线程小:一个进程中包含多个线程,例如 ...

  2. c#中的Task异步编程

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/index翻译 1. 引入 Task异步 ...

  3. .NET Web应用中为什么要使用async/await异步编程

    前言 什么是async/await? await和async是.NET Framework4.5框架.C#5.0语法里面出现的技术,目的是用于简化异步编程模型. async和await的关系? asy ...

  4. 让我们再为C#异步编程Async正名

    本文版权归博客园和作者吴双本人共同所有.转载和爬虫必须在显要位置注明出处:http://www.cnblogs.com/tdws 半年前翻译了一系列很糟糕的异步编程文章,用异步的常用语来说:" ...

  5. 异步编程系列第01章 Async异步编程简介

    p { display: block; margin: 3px 0 0 0; } --> 2016.10.11补充 三个月过去了,回头来看,我不得不承认这是一系列失败的翻译.过段时间,我将重新翻 ...

  6. 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单

    一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两 ...

  7. C#:异步编程和线程的使用(.NET 4.5 )

    摘自:http://www.codeproject.com/Articles/996857/Asynchronous-programming-and-Threading-in-Csharp-N(葡萄城 ...

  8. 异步编程和线程的使用(.NET 4.5 )

    C#:异步编程和线程的使用(.NET 4.5 )   异步编程和线程处理是并发或并行编程非常重要的功能特征.为了实现异步编程,可使用线程也可以不用.将异步与线程同时讲,将有助于我们更好的理解它们的特征 ...

  9. .Net 4.5 异步编程初试(async和await)

    .Net 4.5 异步编程初试(async和await) 前言 最近自己在研究Asp.Net Web API.在看到通过客户端来调用Web API的时候,看到了其中的异步编程,由于自己之前没有接触过, ...

随机推荐

  1. Android Studio下载及使用教程(转载)

    (一)下载及相关问题解决: Android Studio 下载地址,目前最新可下载地址,尽量使用下载工具. Android Studio正式发布,给Android开发者带来了不小的惊喜.但是下载地址却 ...

  2. 快速入门系列--WCF--06并发限流、可靠会话和队列服务

    这部分将介绍一些相对深入的知识点,包括通过并发限流来保证服务的可用性,通过可靠会话机制保证会话信息的可靠性,通过队列服务来解耦客户端和服务端,提高系统的可服务数量并可以起到削峰的作用,最后还会对之前的 ...

  3. SQLSERVER中的ALLOCATION SCAN和RANGE SCAN

    SQLSERVER中的ALLOCATION SCAN和RANGE SCAN 写这篇文章的开始,我还不知道ALLOCATION SCAN的工作原理是怎样的,网上资料少得可怜 求助了园子里的某位大侠,他看 ...

  4. 一次意外的X锁不阻塞问题

        最近有一个朋友问我一个关于给查询操作强制上X锁却不阻塞的问题.该查询写在一个存储过程中,代码如代码1所示: 1: create PROC [dbo].[GetCityOrders] 2: @c ...

  5. WEBAPP开发技巧(手机网站开发注意事项)

    以下只是我个人得总结,如果你有更好的建议,请留言,一起共勉进步!!- -! 1.要响应式开发web,也就是页面必须自适应屏幕大小,可以采用流体布局,如之前的文章(自适应宽度布局),其他具体的小问题可以 ...

  6. javascript类型系统——日期Date对象

    × 目录 [1]静态方法 [2]构造函数 [3]实例方法 前面的话 Date对象是javascript语言中内置的数据类型,用于提供日期和时间的操作接口.Date对象是在早期java中的java.ut ...

  7. SQL*Loader之CASE5

    CASE5 1. SQL脚本 [oracle@node3 ulcase]$ cat ulcase5.sql set termout off rem host write sys$output &quo ...

  8. Unity3D 开发之shader教程(浅谈光照之漫反射diffuse)

    在游戏开发过程中,光照应该是必不可少部分,当然,这是指大多数的稍微大型一些的3D游戏会需要,给模型或者山山水水加上光照,会看上去更加的真实,获得更好的体验.一个本身不发光物体显示什么颜色,在于本身反射 ...

  9. Azure REST API (1) 前言

    <Windows Azure Platform 系列文章目录> 一.服务运行时API简介 微软的Windows Azure服务总线提供了一整套REST风格的API,其中包括服务运行时API ...

  10. qt 窗口动画

    窗口动画 编辑删除转载 2015-10-10 14:50:27 标签:qt渐变动画 一个应用程序通常包含多个动画,例如,你可能希望同时移动许多graphic items或者一个个按照串行的方式的移动他 ...