11

私はjqueryで次のクエリを持っています。これは、Nginx のロング ポーリング モジュールを使用して設定された Nginx サブスクライブ/パブリッシュ ペアの「パブリッシュ」アドレスを読み取ります。

function requestNextBroadcast() {
        // never stops - every reply triggers next. 
        // and silent errors restart via long timeout. 
        getxhr = $.ajax({
            url: "/activity",
            // dataType: 'json',
            data: "id="+channel,
            timeout: 46000, // must be longer than max heartbeat to only trigger after silent error. 
            error: function(jqXHR, textStatus, errorThrown) {
                alert("Background failed "+textStatus);  // should never happen 
                getxhr.abort(); 
                requestNextBroadcast();  // try again
            },
            success: function(reply, textStatus, jqXHR) {
                handleRequest(reply);   // this is the normal result. 
                requestNextBroadcast(); 
            }
        });
    }

コードはチャット ルームの一部です。送信されたすべてのメッセージは、null rply (200/OK) で返信されますが、データは公開されます。これは、データが戻ってきたときにサブスクライブ アドレスを読み取るためのコードです。

タイムアウトを使用すると、チャットルームのすべての人が 30 秒から 40 秒ごとに単純なメッセージを送信します。何も入力しなくても、このコードが読み取るデータは十分にあります。40 秒あたり少なくとも 2 件、場合によってはそれ以上のメッセージです。 .

コードは EI と Firefox で 100% 堅実です。しかし、Chrome では約 5 回の読み込みで 1 回失敗します。

Chrome が失敗すると、46 秒のタイムアウトが発生します。

ログには、一度に 1 つの /activity ネットワーク リクエストが未処理であることが示されます。

私はこのコードを 3 日間クロールして、さまざまなアイデアを試しています。そして毎回、IE と Firefox は問題なく動作し、Chrome は失敗します。

私が見た 1 つの提案は、呼び出しを同期させることですが、ユーザー インターフェイスが長時間ロックされるため、明らかに不可能です。

編集 - 部分的な解決策があります:コードは次のとおりです

function requestNextBroadcast() {
    // never stops - every reply triggers next. 
    // and silent errors restart via long timeout. 
    getxhr = jQuery.ajax({
        url: "/activity",
        // dataType: 'json',
        data: "id="+channel,
        timeout: <?php echo $delay; ?>,
        error: function(jqXHR, textStatus, errorThrown) {
            window.status="GET error "+textStatus;
            setTimeout(requestNextBroadcast,20);  // try again
        },
        success: function(reply, textStatus, jqXHR) {
            handleRequest(reply);   // this is the normal result. 
            setTimeout(requestNextBroadcast,20); 
        }
    });
}

その結果、$delay (15000) が発生するまで応答が遅延することがあります。その後、キューに入れられたメッセージがすぐに到着し、追跡できません。この新しい配置では、メッセージをドロップさせることができませんでした (ネットワークの最適化をオフにしてのみテストしました)。

遅延がネットワークの問題によるものであるとはとても思えません。すべてのマシンは 1 台の実マシン内の VM であり、ローカル LAN の他のユーザーはいません。

編集 2 (金曜日 2:30 BST) - promise を使用するようにコードを変更しました - アクションの POST は同じ症状を示し始めましたが、受信側は正常に動作し始めました! (????????????)。これは POST ルーチンです。一連のリクエストを処理して、一度に 1 つのみが未解決であることを確認します。

function issuePostNow() {
    // reset heartbeat to dropout to send setTyping(false) in 30 to 40 seconds. 
    clearTimeout(dropoutat);
    dropoutat = setTimeout(function() {sendTyping(false);},  
                           30000 + 10000*Math.random()); 
    // and do send 
    var url = "handlechat.php?";
    if (postQueue.length > 0) {
        postData = postQueue[0];
        var postxhr = jQuery.ajax({ 
            type: 'POST',
            url: url,
            data: postData,
            timeout: 5000
        })
        postxhr.done(function(txt){
            postQueue.shift();  // remove this task
            if ((txt != null) && (txt.length > 0)) {
                alert("Error: unexpected post reply of: "+txt)
            }
            issuePostNow();
        });
        postxhr.fail(function(){
            alert(window.status="POST error "+postxhr.statusText);
            issuePostNow();
        });
    }
}

約 8 回に 1 回のアクションで、handlechat.php の呼び出しがタイムアウトになり、アラートが表示されます。アラートが OK になると、キューに入れられたすべてのメッセージが到着します。

また、handlechat の呼び出しが、他のユーザーに表示されるメッセージを書き込む前に停止していることにも気付きました。PHPによるセッションデータの扱いがおかしいのではないかと思っています。セッション データが破損しないように呼び出しを慎重にキューに入れることを知っているので、別のブラウザまたは別のマシンを使用するように注意してきました。php ワーカー スレッドは 2 つしかありませんが、php は /activity の処理や静的コンテンツの提供には使用されません。

また、nginx ワーカーまたは php プロセッサが不足しているのではないかと考えたので、それらを引き上げました。物事を失敗させることはより困難になりましたが、それでも可能です。私の推測では、/activity 呼び出しは 30 回に 1 回失敗し、メッセージはまったくドロップされません。

そして、ご意見をお寄せいただきありがとうございます。


所見のまとめ。

1) しばらくの間コードに含まれていた Chrome のバグです。
2) 運が良ければ、バグは送信されない POST として表示され、タイムアウトになると、Chrome は POST の繰り返しが成功するような状態になります。
3) $.ajax() からの戻り値を格納するために使用される変数は、ローカルまたはグローバルにすることができます。新しい (promise) と古い形式の呼び出しの両方がバグを引き起こします。
4) 回避策やバグを回避する方法が見つかりません。

イアン

4

5 に答える 5

4

Chromeで非常によく似た問題がありました。サーバーから毎秒時間を取得するために Ajax 呼び出しを行っています。Ajax 呼び出しは非同期でなければならないのは明らかです。しかし、Ajax 呼び出しの 1 つが失敗すると、後続の呼び出しも同様に失敗します。最初にタイムアウトを 100ms に設定してみましたが、IE と FF ではうまくいきましたが、Chrome ではうまくいきませんでした。私の最善の解決策は、タイプを POST に設定することでした。これにより、クロムのバグが解決されました。

   setInterval(function(){ 
      $.ajax({
         url: 'getTime.php',
         type: 'POST',
         async: true,
         timeout: 100,
         success: function() { console.log("success"); },
         error: function() { console.log("error"); }
       });
   }, 1000);

更新: ここでの実際の根本的な問題は、Chrome のキャッシュ方法にあると思います。1 つのリクエストが失敗すると、その失敗がキャッシュされるようです。したがって、Chrome は後続のリクエストを開始する前にキャッシュされた失敗を取得するため、後続のリクエストは行われません。これは、Chrome の開発者ツールに移動し、[ネットワーク] タブに移動して、行われている各リクエストを調べると確認できます。失敗する前は、getTime.php への ajax リクエストが毎秒行われますが、1 回失敗すると、後続のリクエストは開始されません。したがって、次の解決策がうまくいきました。

   setInterval(function(){ 
      $.ajax({
         url: 'getTime.php',
         cache: false,
         async: true,
         timeout: 100,
         success: function() { console.log("success"); },
         error: function() { console.log("error"); }
       });
   }, 1000);

ここでの変更点は、この Ajax クエリのキャッシュを無効にしていることですが、そうするには type オプションを GET または HEAD にする必要があるため、' type: 'POST'' を削除しました (GET がデフォルトです)。

于 2013-08-06T21:05:08.870 に答える
2

Chrome でのフリーズを防ぐために、ポーリング機能を Webworker に移動してみてください。それ以外の場合は、jquery オブジェクトの ajax .done() を使用してみてください。それは常にChromeで機能します。

于 2012-07-04T21:19:17.293 に答える
0

おそらく、プッシュモジュールの設定を変更することで回避できます(いくつかあります)-これらを投稿していただけますか?

頭のてっぺんから:

  • インターバルポーリングに設定すると、ちょっと醜く解決します
  • 同時実行設定は何らかの影響を与える可能性があります
  • メッセージストレージは、データの欠落を回避するために使用される場合があります

また、 Charlesのようなものを使用して、ネットワーク/アプリケーション層で何が起こっているかを正確に確認します

于 2012-07-11T16:23:06.757 に答える
0

コメントはコードをフォーマットしないので、2番目の回答として再投稿します。

Michael Dibbetsは、$。ajax.doneを使用して何かに取り組んでいると思います。遅延パターンは、処理をイベントループの次のターンにプッシュします。これは、ここで必要な動作だと思います。参照: http: //www.bitstorm.org/weblog/2012-1/Deferred_and_promise_in_jQuery.htmlまたはhttp://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/

私は次のようなことを試してみます:

function requestNextBroadcast() { 
  // never stops - every reply triggers next.
  // and silent errors restart via long timeout.

  getxhr = jQuery.ajax({
    url: "/activity",
    // dataType: 'json',
    data: "id="+channel,
    timeout: <?php echo $delay; ?> 
  });

  getxhr.done(function(reply){
    handleRequest(reply);
  });

  getxhr.fail(function(e){
    window.status="GET error " + e;
  });

  getxhr.always(function(){
    requestNextBroadcast();
  });

注:Promise.doneとPromise.failのコールバック引数に関するドキュメントを見つけるのに苦労しています:(

于 2012-07-05T20:56:49.110 に答える
0

getxhr の前に「var」を付けるべきだと思います。成功/失敗処理の途中で古いリクエストを上書きするのではなく、毎回完全に別の新しいリクエストを作成したくありませんか? setTimeout を追加すると、動作が「改善」される理由を説明できます。私も何かが足りないかもしれません;)

于 2012-07-04T22:48:54.063 に答える