【问题标题】:AJAX Polling : Why when two messages are sent at the same time by two different clients, only one of the client is updated?AJAX 轮询:为什么当两个不同的客户端同时发送两条消息时,只有一个客户端被更新?
【发布时间】:2014-06-24 04:41:04
【问题描述】:

我正在尝试创建一个使用普通轮询的 Laravel 4 聊天应用程序。到目前为止它工作正常但是当我在两个不同帐户的两个浏览器中测试它时,我发现当我尝试同时从每个客户端(浏览器)发送消息时,客户端 B 收到客户端 A 的消息但客户端 A 确实没有收到客户 B 的消息。刷新当然会在客户端 A 中加载客户端 B 的消息。我该如何解决?

我使用普通轮询而不是长轮询的原因是因为并发连接问题,即一些旧版浏览器仅限制为 2 个并发连接。此聊天应用程序计划在单个聊天会话中容纳最多 25 个用户。由于遗留浏览器问题,我也无法使用网络套接字。如果我错了,请纠正我。

附言更好的方法来实现这一点的奖励积分!谢谢!

这是我更新消息的客户端(Javascript)代码。

updatemessages(){
...
 $.ajax({
            type: "POST",
            url: 'create/populate',
            data: {
                'from': lastMessages,
                'conv_id':conv_id
            },
            success: function(messages) {
                $.each(messages, function() {
                    appendMessage(this);
                });
            },
            error: function(xhr,textStatus,errorThrown) {
                console.log(xhr.responseText);
                console.log('something went wrong!');
                $(this).abort();
            },
            complete: function() {
                window.setTimeout(updateMessages, 2000);
            },
            dataType: 'json'
        });
}
updatemessages();

这是我的服务器代码(控制器),它接收消息,将其与我的数据库中的最新消息进行比较,如果找到最新消息,则检索它。

public function index()
    {
        $timestamps = Input::get('from'); //get timestamp of AJAX sent message
        $conv_id = Input::get('conv_id'); //get conversation id of AJAX sent conv_id
        $allMessages = Messages::with('User')->whereIn('conv_id',$conv_id);

        if(is_null($timestamps)){
           $messages = Messages::with('User')->whereIn('conv_id',$conv_id)->orderBy('created_at','desc');
        }else{
           asort($timestamps);
           $messages = $allMessages->where('created_at','>',end($timestamps));
        }

        return $messages->get()->reverse();
    }

这是另一个将新消息存储到数据库中的代码。

public function store()
    {
        $body = Input::get('message');
        $user_id = Input::get('user_id');
        $conversation = null;

            $conv_id = Input::get('conv_id');
            return Messages::create(array('body'=>$body,'conv_id'=>$conv_id,'user_id'=>$user_id));

    }

【问题讨论】:

    标签: php ajax laravel polling


    【解决方案1】:

    您所描述的似乎是预期的行为。

    如果用户 A 和 B 都“同时”发送一条消息,那么其中一个将首先进入 DB 记录。因此,第一个插入的用户在下一次轮询响应之前不会看到其他用户的新消息。

    我建议您查看 Memcache 层来存储最后一条消息的时间戳。您可以将“上次轮询”时间戳存储在 Session 中,这样您就不必向数据库发出额外的请求,只是为了了解自登录用户上次轮询以来是否有任何新消息。您当然会在任何时候创建新消息以及任何时候 Memcache 返回空值(如缓存 TTL 已过期)时写入该 Memcache 记录。

    此外,如果不是每个请求都写入会话,Memcache 可以加快会话性能。

    【讨论】:

    • 所以如果 B 向 A 发送消息,服务器会将消息及其时间戳存储到缓存和数据库中,对吗?为了让 A 接收 B 的消息,A 将使用当前时间戳 (At) 轮询服务器,并且服务器使用以前的 Session 时间作为参考点检查 memcache 中是否有任何新消息。最后,它使用 At 更新会话时间,对吗?请原谅我,我不明白这将如何解决我的问题。介意进一步解释吗?谢谢!
    【解决方案2】:

    Carbon's answer 确实解决了一点问题,但核心问题是我使用的时间戳值精度高达仅秒,而不是精度高达 毫秒强>。这在

    中很明显
     $messages = $allMessages->where('created_at','>',end($timestamps));
    

    如您所见,这就是A和B同时发送消息时没有收到消息的主要原因,因为A的消息时间戳与B的消息时间戳相同。所以我创建了另一个名为毫秒的列,每次创建新消息时,我都会运行

    $micro = round(microtime(true) * 1000);
    

    并像这样保存新消息:

    Messages::create(array(...other message datas...,'microseconds'=>$micro));
    

    然后在控制器的加载消息函数中,我没有与视图中出现的最后一条消息的时间戳进行比较,而是使用 Carbon 的答案与上次轮询的时间进行比较。

    $currentPoll = round(microtime(true) * 1000); //get time in microseconds of time of current poll
    $lastPoll = Session::get('lastPoll');//get time of previous poll
    $messages = $allMessages->where('microseconds','>',$lastPoll)->orderBy('microseconds','desc');
    Session::put('lastPoll',$currentPoll);// update the lastPoll variable to be used in the next request
    

    最后,在我的 javascript 中,我删除了任何将消息附加到视图的函数,除了 updateMessages 函数中的函数,因为只允许 updateMessages 附加消息。这是为了防止我的消息显示两次。确实,当我发送消息时,我的消息不会立即出现在我的视野中,但这比根本没有收到任何消息要好得多。

    瞧~!我的问题解决了……

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-31
      • 1970-01-01
      • 2017-08-07
      • 1970-01-01
      • 1970-01-01
      • 2019-08-05
      • 2016-04-25
      相关资源
      最近更新 更多