Play! 的 Comet 支持如何运作?

发布于 2024-10-08 23:18:17 字数 293 浏览 5 评论 0原文

我看到 Akka 模块的描述说 Play 有很好的 Comet 支持,但我从未使用过 Comet 之前,我在 Play 的文档中找不到任何提及它的内容。它在 Play 中如何运作?


我花了两天多的时间来解决这个问题,所以我想与其他 Play 初学者分享这些信息。

I saw the Akka module's description says that Play has great Comet support, but I've never used Comet before and I can't find any mention of it in Play's documentation. How does it work in Play?


I spent a few hours over two days figuring this out so I wanted to share this info for other Play beginners.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

青芜 2024-10-15 23:18:17

Play 包含一个示例聊天应用程序,演示如何使用 Comet。这个例子并没有解释发生了什么,所以这就是我所想到的。

模型

为了让其他人找到您发送的新更新,它们需要存储在某个地方。可以想象,这可能位于缓存中,甚至位于控制器本身中,但数据库将是最安全的选择,因此您需要一个模型。他们还需要一种方法来确定哪些更新对他们来说是新的,这意味着您可能需要一个日期字段(另请参阅:上次使用 JPA 更新时间戳)。 Chat 示例使用一个简单的模型:

@Entity
public class Message extends Model {     
    public String user;
    public Date date;
    public String text;

    public Message(String user, String text) {
        this.user = user;
        this.text = text;
        this.date = new Date();
    }       
}

控制器

控制器需要两种方法来方便 Comet。一种用于发布新数据,不会执行任何特殊操作:

public static void postMessage(String message) {
    new Message(session.get("nick"), message).save();
}

另一种用于检索更新:

public static void newMessages() {
    List<Message> messages = Message.find("date > ?", request.date).fetch();
    if (messages.isEmpty()) {
        suspend("1s");
    }
    renderJSON(messages);
}

这里的关键位是 suspend("1s"),它使 HTTP 请求保持打开状态,检查每秒一次新数据。

视图

视图具有三个职责——发送新数据、获取更新然后呈现这些更新。

与相应的控制器操作一样,发送不会执行任何特殊操作:

$('#send').click(function(e) {
    var message = $('#message').val();
    $('#message').val('');
    $.post('@{postMessage()}', {message: message}); 
});

获取更新是神奇的一点:调用

// Retrieve new messages
var getMessages = function() {
    $.ajax({
        url: '@{newMessages()}',
        success: function(messages) {
            $(messages).each(function() {
                display(this);
            });
        },
        complete: function() {
            getMessages();
        },
        dataType: 'json'
    });
}
getMessages();

一次 getMessages() 即可开始工作,然后在每次成功请求后递归地调用自身。它获取 newMessages() 操作来查找新消息,如果没有任何消息,它会保持请求打开,直到有需要报告的内容为止。找到新消息时,JSON 数据将传递给 display 函数:

var display = function(message) {
    $('#thread').append(tmpl('message_tmpl', {message: message}));
}

display 函数应用 JavaScript 微模板到 JSON 数据以呈现新消息。使用微模板不是必需的,但它确实工作得很好。它们直接包含在将要使用它们的页面模板中:

<script type="text/html" id="message_tmpl">
    <div class="message <%= message.user == '${session.nick}' ? 'you' : '' %> <%= message.user == 'notice' ? 'notice' : '' %>">
        <h2><%= message.user %></h2>
        <p><%= message.text.replace('\n', '<br/>') %></p>
    </div>
</script>

type="text/html" 导致浏览器、搜索引擎和屏幕阅读器忽略整个脚本块。结果比使用 jQuery 构建节点或连接字符串更容易阅读和维护。总的来说,一旦您知道哪些部分是相关的,事情就非常简单了。

Play includes a sample Chat application which demonstrates how to use Comet. The example doesn't explain what's going on though, so here's what I've figured out.

Model

In order for others to find new updates you send, they'll need to be stored somewhere. Conceivably this could be in a cache or even in the controller itself, but the database is going to be the safest bet, so you'll want a model. They'll also need a way to determine which updates are new to them, which means you'll probably want a date field (see also: Last update timestamp with JPA). The Chat example uses a simple model:

@Entity
public class Message extends Model {     
    public String user;
    public Date date;
    public String text;

    public Message(String user, String text) {
        this.user = user;
        this.text = text;
        this.date = new Date();
    }       
}

Controller

The controller needs two methods to facilitate Comet. One where new data is posted, which doesn't do anything special:

public static void postMessage(String message) {
    new Message(session.get("nick"), message).save();
}

and one for retrieving updates:

public static void newMessages() {
    List<Message> messages = Message.find("date > ?", request.date).fetch();
    if (messages.isEmpty()) {
        suspend("1s");
    }
    renderJSON(messages);
}

The key bit here is suspend("1s") which is what holds the HTTP request open, checking for new data once per second.

View

The view has three responsibilities -- sending new data, fetching updates and then rendering those updates.

Sending, like the corresponding controller action, doesn't do anything special:

$('#send').click(function(e) {
    var message = $('#message').val();
    $('#message').val('');
    $.post('@{postMessage()}', {message: message}); 
});

Fetching updates is the magic bit:

// Retrieve new messages
var getMessages = function() {
    $.ajax({
        url: '@{newMessages()}',
        success: function(messages) {
            $(messages).each(function() {
                display(this);
            });
        },
        complete: function() {
            getMessages();
        },
        dataType: 'json'
    });
}
getMessages();

getMessages() is called once to get things started, and afterwards it calls itself recursively after each successful request. It GETs the newMessages() action which looks for new messages, and if there aren't any it holds the request open until it has something to report. When new messages are found, the JSON data is passed to a display function:

var display = function(message) {
    $('#thread').append(tmpl('message_tmpl', {message: message}));
}

The display function applies a JavaScript Micro-Template to the JSON data to render new messages. Use of micro templates isn't necessary, but it does work pretty well. They're included right in the template of the page that's going to use them:

<script type="text/html" id="message_tmpl">
    <div class="message <%= message.user == '${session.nick}' ? 'you' : '' %> <%= message.user == 'notice' ? 'notice' : '' %>">
        <h2><%= message.user %></h2>
        <p><%= message.text.replace('\n', '<br/>') %></p>
    </div>
</script>

The type="text/html" causes browsers, search engines and screen readers to ignore the whole script block. The result is much easier to read and maintain than using jQuery to build nodes or concatenating strings. Overall it's pretty simple once you know which bits are relevant.

何必那么矫情 2024-10-15 23:18:17

在 Web 应用程序中实现服务器推送或 Comet 的方法有很多,最常见的方法之一是长轮询,因为它在大多数现代浏览器中得到了很好的支持。

Play主要通过suspend(time);函数实现长轮询。该函数做了两件非常重要的事情

  1. 它保持 HTTP 请求打开并在指定的时间内重试该操作。这允许您保留 HTTP 请求并继续重试该操作,直到发生您想要通知浏览器的事情

  2. 的事情,并且非常重要的是,当请求挂起时,线程将被释放。这意味着 http 请求不会为每个挂起的请求保留一个打开的线程。

当游戏处于 DEV 模式时,它仅在单个线程上运行,但您可以(并且我已经尝试过)在示例聊天应用程序上运行数十个用户,而不会导致服务器挂起或用户请求被阻止。

然而,挂起方法是 play 真正对 Comet 提供帮助的唯一方法。如果您运行示例聊天应用程序并使客户端保持打开状态,您会注意到 120 秒后,请求将超时。示例应用程序不会重试该请求。您必须自己构建长轮询技术的客户端。

Guillaume 在论坛中提到,示例聊天应用程序只是轮询可以使用多长时间的演示。所以我不认为 Play 可以证明拥有强大的 Comet 支持,这只是朝着正确方向迈出的一步。

There are many ways to achieve server-push or Comet in web applications, and one of the most common is Long Polling due to it being well supported in most modern browsers.

Play achieves long polling mainly through the suspend(time); function. This function does two very important things

  1. it keeps the HTTP request open and retries the action in the time specified. This allows you to hold the HTTP request and keep retrying the action until something has happened that you want to inform the browser about

  2. and very importantly, the thread is released when the request is suspended. This means that the http request does not hold an open thread for each suspended request.

When play is in DEV mode, it only runs on a single thread, yet you can (and I have tried) run dozens of users on the sample chat application without the server hanging, or users getting blocked requests.

The suspend method however is the only thing that play really does to help with Comet. If you run the sample chat application and leave a client open, you will notice that after 120 seconds, the request will timeout. The sample application does not re-try the request. The client side of the long-polling technology you have to build yourself.

Guillaume has mentioned in the forums that the sample chat application is just a demo of how long polling can be used. So I don't think Play can attest to having great Comet support, it is just a step in the right direction.

月亮坠入山谷 2024-10-15 23:18:17

您可以在 Akka 中使用 Mist 进行由 Servlet 3.00 或 Jetty7 Continuations 支持的服务器推送 (Comet): http://doc.akkasource.org/http#Mist%20-%20Lightweight%20Asynchronous%20HTTP

You can use Mist in Akka for server-push (Comet) backed with Servlet 3.00 or Jetty7 Continuations: http://doc.akkasource.org/http#Mist%20-%20Lightweight%20Asynchronous%20HTTP

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文