5

まず第一に、私の英語で申し訳ありませんが、Google 翻訳の助けを借りてこれを書きました。

PHPとAjaxでGoogle Waveのようなアプリを作ろうとしています。ユーザーが何かを入力すると、ページ上のjavascriptが検出されoninput、テキストエリアのコンテンツがサーバーに送信され、サーバーがコンテンツをデータベースに保存するテキストエリアがあります。

私がやっていることは、XHR でコンテンツを送信するたびにXHR.abort()、以前の XHR 要求を常に中断するということです。データベース内のデータは問題ありませんが、以前のバージョンが保存されている場合があります。

クライアントがアボートしたのにPHPが実行を停止せず、前のリクエストが最後のリクエストよりも時間がかかり、最後のリクエストの後に完了したことがあるために発生することがわかっているので、「ignore_user_abort」の関数のマニュアルを読みましたおよび「connection_aborted」ですが、問題は解決しません。

状況をシミュレートするためにこのスクリプトを作成しました。接続を中止したとき (「停止」を押し、タブ/ウィンドウを閉じる)、データベースに新しいデータがないことを期待しましたが、5 秒後にはまだ新しいデータが残っています。 、そのため、ユーザーが接続を中止したときにトランザクションをロールバックするために助けが必要です。

シミュレートするスクリプトは次のとおりです (PDO_DSN、PDO_USER、PDO_PASS が定義されています)。

<?php
ignore_user_abort(true);

ob_start('ob_gzhandler');

$PDO = new PDO(PDO_DSN, PDO_USER, PDO_PASS, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));

$PDO->beginTransaction();
$query = $PDO->query('INSERT INTO `table` (`content`) VALUES (' . $PDO->quote('test') . ')');
sleep(5);
echo ' ';
ob_flush();
flush();
if (connection_aborted()) {
  $PDO->rollBack();
  exit;
}
$PDO->commit();

ob_end_flush();
4

4 に答える 4

2

見つけXHR.abort()connection_aborted()信頼できない場合は、帯域外シグナルを送信して、実行中の PHP リクエストにトランザクションをコミットしないことを通知する別の方法を検討してください。

APCを実行していますか (または可能性がありますか)?

を呼び出す代わりにXHR.abort()、アボートを知らせる別の XHR リクエストを送信できます。この要求の目的は、APC ユーザー キャッシュに特別なキーを記録することです。このキーの存在は、実行中の PHP リクエストに対して、ロールバックする必要があることを示します。

これを機能させるには、各 XHR リクエストが (比較的) 一意のトランザクション識別子 (フォーム変数など) を保持する必要があります。この識別子は、ランダムに、または現在の時刻に基づいて生成され、最初の XHR と「中止」XHR で送信され、中止要求を実行中の要求に関連付けることができます。以下の例では、トランザクション ID は variable の形式になっていますt

「中止」XHR ハンドラの例:

<?php
$uniqueTransactionId = $_REQUEST['t'];
$abortApcKey = 'abortTrans_' . $uniqueTransactionId;

apc_store($uniqueTransactionId, 1, 15);

改訂されたデータベース書き込み XHR ハンドラの例:

<?php
$PDO = new PDO(PDO_DSN, PDO_USER, PDO_PASS,
               array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'));

$PDO->beginTransaction();
$query = $PDO->query('INSERT INTO `table` (`content`) VALUES (' . $PDO->quote('test') . ')');

$uniqueTransactionId = $_REQUEST['t'];
$abortApcKey = 'abortTrans_' . $uniqueTransactionId;
if (apc_exists($abortApcKey)) {
  $PDO->rollBack();
  exit;
}

$PDO->commit();

まだタイミングの問題があるかもしれません。アボートの到着が遅すぎてコミットを停止できない場合があります。これに適切に対処するには、データベース書き込みハンドラーを変更して、トランザクションがコミットされたことを示す APC キーを記録します。アボート ハンドラは、このキーの存在を確認し、意味のある XHR アボート結果を送り返し、クライアントに「申し訳ありませんが、手遅れでした」と通知することができます。

アプリケーションが複数のライブ サーバーでホストされている場合、APC のキャッシュは単一のマシン上のプロセス間でのみ共有されるため、memcachedまたはなどの共有キャッシュを使用する必要があることに注意してください。redis

于 2013-02-20T01:22:41.410 に答える
1

データベースにも保存されているタイムスタンプまたは実行中の番号をブラウザに送り返すようにするのはどうですか。新しいタイムスタンプが新しい場合にのみ書き込むように更新をチェックできます。

于 2013-02-20T01:33:45.940 に答える
0

この問題は、Javascript と Ajax で何度も見てきました。UI の実装に十分注意しないと、ユーザーが 2 回クリックしたり、ブラウザーが ajax 呼び出しを 2 回トリガーしたりして、同じレコードがデータベースに 2 回ヒットする可能性があります。これらは UI からの 2 つの完全に異なる http 要求である可能性があるため、データベースに挿入する前に、サーバー側のコードで重複を除外で​​きることを確認する必要があります。私の解決策は通常、同じユーザーが最近入力したレコードを照会し、これが本当に新しいエントリかどうかを確認することです。この新しいレコードがまだデータベースにない場合は挿入し、そうでない場合は無視します。Oracle では、PL/SQL を使用して MERGE IF MATCH THEN INSERT コマンドを使用できるため、これを 1 つのクエリで処理できます。

于 2013-02-19T21:19:04.220 に答える