Play!中使用HTTP异步编程
本章译者:@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,但是只读的。
- 它没有
renderArgs,routeArgs或 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异步编程的更多相关文章
- JS中的同步异步编程
首先我们先看看同步与异步的定义,及浏览器的执行机制,方便我们更好地理解同步异步编程. 浏览器是多线程的,JS是单线程的(浏览器只分配一个线程来执行JS) 进程大线程小:一个进程中包含多个线程,例如 ...
- c#中的Task异步编程
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/index翻译 1. 引入 Task异步 ...
- .NET Web应用中为什么要使用async/await异步编程
前言 什么是async/await? await和async是.NET Framework4.5框架.C#5.0语法里面出现的技术,目的是用于简化异步编程模型. async和await的关系? asy ...
- 让我们再为C#异步编程Async正名
本文版权归博客园和作者吴双本人共同所有.转载和爬虫必须在显要位置注明出处:http://www.cnblogs.com/tdws 半年前翻译了一系列很糟糕的异步编程文章,用异步的常用语来说:" ...
- 异步编程系列第01章 Async异步编程简介
p { display: block; margin: 3px 0 0 0; } --> 2016.10.11补充 三个月过去了,回头来看,我不得不承认这是一系列失败的翻译.过段时间,我将重新翻 ...
- 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单
一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两 ...
- C#:异步编程和线程的使用(.NET 4.5 )
摘自:http://www.codeproject.com/Articles/996857/Asynchronous-programming-and-Threading-in-Csharp-N(葡萄城 ...
- 异步编程和线程的使用(.NET 4.5 )
C#:异步编程和线程的使用(.NET 4.5 ) 异步编程和线程处理是并发或并行编程非常重要的功能特征.为了实现异步编程,可使用线程也可以不用.将异步与线程同时讲,将有助于我们更好的理解它们的特征 ...
- .Net 4.5 异步编程初试(async和await)
.Net 4.5 异步编程初试(async和await) 前言 最近自己在研究Asp.Net Web API.在看到通过客户端来调用Web API的时候,看到了其中的异步编程,由于自己之前没有接触过, ...
随机推荐
- 设为首页 和 收藏本站js代码 兼容IE,chrome,ff
设为首页 和 收藏本站js代码 兼容IE,chrome,ff //设为首页 function SetHome(obj,url){ try{ obj.style.behavior='url(#defau ...
- 轻松搭建Unity3D 安卓Android开发环境
1,下载安装Java的JDK: http://www.oracle.com/technetwork/java/javase/downloads/index.html (JDK中,包含JRE) 如果是6 ...
- Spark入门实战系列--9.Spark图计算GraphX介绍及实例
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .GraphX介绍 1.1 GraphX应用背景 Spark GraphX是一个分布式图处理 ...
- windows环境下跑hadoop自带的wordcount遇到的问题
hadoop环境自己之前也接触过,搭建的是一个伪分布的环境,主从节点都在我自己的机子上,即127.0.0.1,当初记得步骤很多很麻烦的样子(可能自己用ubuntu还不够熟练),包括myeclipse. ...
- eclipse推荐的插件
1.Log4j的颜色插件 http://m.blog.csdn.net/blog/JavaWinner/41548259
- Theano入门神经网络(一)
Theano是一个Python库,专门用于定义.优化.求值数学表达式,效率高,适用于多维数组.特别适合做机器学习.一般来说,使用时需要安装python和numpy. 首先回顾一下机器学习的东西,定义一 ...
- Elasticsearch——Date Math在索引中的用法详解
在elasticsearch中,有时会想要通过索引日期来筛选查询的数据,此时就需要用到日期数学表达式. 更多内容参考Elasticsearch翻译汇总 基于日期数学表达式的索引 模式如下: <s ...
- 小技巧找出一个php的cron脚本出问题的代码行
这个小技巧虽然很小,但是很有用. 我写了一个cron脚本,但是隔一天发现,这个昨天的cron脚本还一直在跑着,没有停下来,一定是里面有个程序堵住了. 但是如果我重新跑又需要很多时间.这个怎么办? 现在 ...
- C#多线程技术总结(同步)
二.串行(同步): 1.lock.Monitor--注意锁定的对象必需是引用类型(string类型除外) 示例: private static object syncObject = new obje ...
- 《ASP.NET SignalR系列》第一课 认识SignalR
从现在开始相关文章请到: http://lko2o.com/moon 一.概述 ASP.NET signalr对ASP.NET开发者来说是一个新的程序库,它能让我们更加容易便捷地开发实时通信功能; s ...