Play!的Comet如何支持工作?

时间:2010-12-22 17:27:58

标签: comet playframework

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


我花了几个小时两天来搞清楚这一点,所以我想与其他Play初学者分享这些信息。

3 个答案:

答案 0 :(得分:20)

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

模型

为了让其他人找到您发送的新更新,他们需要存储在某个地方。可以想象,这可能是在缓存中,甚至在控制器本身,但数据库将是最安全的赌注,所以你需要一个模型。他们还需要一种方法来确定哪些更新对他们来说是新的,这意味着您可能需要一个日期字段(另请参阅:Last update timestamp with 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 Micro-Template应用于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"会导致浏览器,搜索引擎和屏幕阅读器忽略整个script块。与使用jQuery构建节点或连接字符串相比,结果更容易阅读和维护。总的来说,一旦你知道哪些位是相关的,它就非常简单。

答案 1 :(得分:7)

有很多方法可以在网络应用程序中实现服务器推送或Comet,其中最常见的是Long Polling,因为它在大多数现代浏览器中都得到了很好的支持。

播放主要通过suspend(time);功能实现长轮询。这个函数做了两件非常重要的事情

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

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

  3. 当播放处于DEV模式时,它只能在单个线程上运行,但您可以(我已经尝试)在示例聊天应用程序上运行数十个用户而不会挂起服务器,或者用户被阻止请求。

    暂停方法是唯一可以帮助Comet的方法。如果您运行示例聊天应用程序并打开客户端,您会注意到120秒后,请求将超时。示例应用程序不会重试该请求。您必须自己构建的长轮询技术的客户端。

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

答案 2 :(得分:1)

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