本章译者:@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. KnockoutJS 3.X API 第四章(13) template绑定

    目的 template绑定(模板绑定)使用渲染模板的结果填充关联的DOM元素. 模板是一种简单方便的方式来构建复杂的UI结构 . 下面介绍两种使用模板绑定的方法: 本地模板是支持foreach,if, ...

  2. 原生JS实现分页效果1.0

    不太完整,写的太急,等等加上完整注释,写起来还是有些难度的,写的有点水,后面再改进改进. <!DOCTYPE html><html lang="en">&l ...

  3. hdu3339 In Action(Dijkstra+01背包)

    /* 题意:有 n 个站点(编号1...n),每一个站点都有一个能量值,为了不让这些能量值连接起来,要用 坦克占领这个站点!已知站点的 之间的距离,每个坦克从0点出发到某一个站点,1 unit dis ...

  4. java中对象多态时成员变量,普通成员函数及静态成员函数的调用情况

    /* 样例1: class Parent{ int num = 3; } class Child extends Parent{ int num = 4; } */ /* 样例2: class Par ...

  5. 再读GFS论文

    http://loopjump.com/gfs_paper_note/ 再读GFS的一些笔记.主要涉及GFS架构.Chunk大小选择的一些折中考量.元数据管理及锁.写数据流程.GFS一致性模型的理解. ...

  6. Yii的学习(5)--Active Record的关联

    官网原文:http://www.yiiframework.com/doc/guide/1.1/zh_cn/database.arr 官网中后半段为英文,而且中文的内容比英文少一些,先放到这里,之后有时 ...

  7. 数据可视化(8)--D3数据的更新及动画

    最近项目组加班比较严重,D3的博客就一拖再拖,今天终于不用加班了,赶紧抽点时间写完~~ 今天就将D3数据的更新及动画写一写~~ 接着之前的博客写~~ 之前写了一个散点图的例子,下面可以自己写一个柱状图 ...

  8. [OpenCV] Samples 03: cout_mat

    操作Mat元素时:I.at<double>(1,1) = CV_PI; /* * * cvout_sample just demonstrates the serial out capab ...

  9. Azure Redis Cache (3) 在Windows 环境下使用Redis Benchmark

    <Windows Azure Platform 系列文章目录> 熟悉Redis环境的读者都知道,我们可以在Linux环境里,使用Redis Benchmark,测试Redis的性能. ht ...

  10. Asp.net 高性能 Sqlite ORM 框架之 sqliteSugar

    一.介简 easyliter框架的升级版本,并且正式命名为SqliteSugar框架,另外Sugar系列还有 MySql和MsSql版本,Oracle版本待开发中(因为客户端太大一直在忧郁当中) 用S ...