Asynchronous programming with HTTP in play!

Source: Internet
Author: User

This chapter translator: @Sam Liu (Translator did not leave his homepage, please Sam Liu see this article, join the group 168013302 contact ' bumblebee @ Translation play ')

This chapter explains how to use the Asynchronous Pattern to program a typical long connection (long-polling), flow (streaming), and push mode (Comet-style) to facilitate the response to tens of thousands of concurrent requests.

Delaying (Suspending) HTTP requests

Play is primarily used to handle very short requests. It uses a fixed thread pool to process the user's HTTP requests. For best results, the thread pool is, of course, as small as possible. We generally use the @ processor number [email protected] this more appropriate value as the default pool size.

This means that if a request lasts for a long time (such as waiting for a lengthy calculation) it blocks the thread pool and reduces the responsiveness of the service. Of course you can also add more threads to the pool, but that wastes a lot of resources and the pool size is not always infinite.

Imagine a chat service when each browser terminal makes a request to wait for a new message to be displayed. These requests typically take a long time (usually several seconds), which inevitably blocks the thread pool. If you plan to support 100 people at a time, then you have to prepare at least 100 threads. Well, that's not a thing, but what if it's 1000? 10,000 of them?

In order to solve this kind of always, play allows you to temporarily delay (suspend) a request. This HTTP request will remain connected, but will be pushed to the thread pool to try again later. You can tell play to process the request again after the appropriate delay or after getting the return value of a @[email protected].

Tip. You can see an example: @samples-and-tests/[email protected]

For example, this action will process a job for a long time and then return the result to HTTP response when it is finished:

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

Here we use the @await (...) @ To allow play to defer processing this request until the result of returning @[email protected].

Continuations

In order to regain the thread before responding to other requests, the framework must suspend execution of your code. The version prior to play was used by @waitfor (...). @, which is now called @await (...) @, use it to slow down your action and come back to execute it later.

To make it easier to work with asynchronous code, let's introduce continuations. Continuations may allow your code to pause and start again in a natural way. So you can write your code like a command, as follows:

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

In fact here, your code is divided into two steps, executed in two different threads. But as you can see, it is not felt in your code.

You can use await(…) and continuations to write a loop:

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

Even in development mode, with only one thread to handle requests by default, play can run these loops concurrently at the same time.

HTTP output stream

Now you can run this type of loop without worrying about blocking threads, and you'll want to send the results to the browser client whenever you get some valid results. This is what @content-type:[email protected] does with this HTTP output type. It allows you to send HTTP results in batches using multiple chunks. The browser will immediately display the results received.

Using await(…) and continuations, you can now implement it:

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

Even if the CSV is generated for one hours, play can process multiple requests at the same time with one thread and return to the client whenever there is an up-to-date result.

Using WebSockets

WebSockets is a two-way communication channel that connects browsers and app services. On the browser side, you can use "ws://" to open a socket channel:

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

On the play side, you can declare a WS route:

WS   /helloSocket            MyWebSocket.hello

MyWebSocketis a WebSocketController . A WebSocket controller is similar to a standard HTTP controller, but there are a few different concepts:

    • It has a request requests object, but does not have a response return object.
    • It can access the session, but read-only.
    • It does not renderArgs , routeArgs or flash scope.
    • It can only read parameters from the route path or the queryystring path string.
    • It has two communication channels: inbound input and outbound output.

When the client connects ws://localhost:9000/helloSocket to the socket channel, Play runs the MyWebSocket.hello action method. Once the MyWebSocket.hello action method exits, the socket channel is closed.

Here is an example of a very simple socket:

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

In this example, the client connects to the socket, receives a "Hello Guillaume" message, and then the socket is closed.

Of course, you don't usually want to close the socket right away. And the use await(…) can be easily kept connected.

The following is a very simple Echo response service:

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!");             }        }    } }

In the example above, multiple layers of nested "if" and "cast" are annoying and prone to error. This is where Java is a bad place. Even if a simple example like this is so difficult to handle, it can be a nightmare if you encounter more complex situations, such as combining multiple streams of data and having more event types.

So we're going to introduce a Java simple matching pattern, which is in the play.libs.f of this functional programming library.

Then the previous echo example can be rewritten as follows:

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!");         }    }}

Continue this topic

Next, implement the Ajax request.

Asynchronous programming with HTTP in play!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.