web-dev-qa-db-ja.com

httpのabort / closeをnginxからuwsgiに伝播します/ Django

DjangoアプリケーションWebアプリケーションがあり、nginxにabort/close to uwsgi/Djangoを伝播させることができるかどうか疑問に思っていました。

基本的に、nginxはデフォルトでuwsgi_ignore_client_abortが「オフ」になっているため、早期の中止/終了を認識していることを知っています。応答が送信される前にリクエストが中止/終了されると、nginxログにnginx499エラーが記録されます。 uwsgiがリクエストの処理を終了すると、nginxにレスポンスを返すときに「IOエラー」をスローします。

uwsgi_ignore_client_abortを「オン」にすると、nginxはアボート/クローズを認識せず、uwsgiは引き続きnginxに書き戻すことができるため、uwsgiの「IOエラー」を削除します。

私のユースケースは、ユーザーがいくつかのajaxの結果を非常にすばやくページングするアプリケーションがあるため、スキップしたページに対する保留中のajaxリクエストをすばやくページスルーすると、クライアントがクリーンで効率的になります。ただし、サーバー側(uwsgi/Django)は、応答を待機しているものがない場合でも、すべての要求を処理する必要があるため、これは何もしません。

明らかに、何らかの理由でリクエストが途中で中止されたくない特定のページがあるかもしれません。しかし、私はそのカテゴリに分類される可能性のある長時間実行されるリクエストにセロリを使用します。

それで、これは可能ですか? uwsgi's hariakariの設定は、あるレベルにあると私に思わせます....それを行う方法がわかりません。

19
byoungb

私のユースケースは、ユーザーがいくつかのajaxの結果を非常にすばやくページングするアプリケーションがあるため、スキップしたページに対する保留中のajaxリクエストをすばやくページスルーすると、クライアントがクリーンで効率的になります。

クライアント側でのAJAXリクエストの中止は、 XMLHttpRequest.abort() を介して行われます。 abort()が呼び出されたときにリクエストがまだ送信されていない場合、リクエストは送信されません。ただし、リクエストが送信された場合、サーバーはリクエストが中止されたことを認識しません。接続は閉じられません。サーバーに送信されるメッセージは何でもありません。リクエストが不要になったことをサーバーに認識させたい場合は、基本的に、リクエストを識別する方法を考え出す必要があります。これにより、最初のリクエストを行ったときに、そのIDを取得できます。次に、別のAJAXリクエストを介して、could以前のリクエストをキャンセルする必要があることをサーバーに通知します。 (abort()このように に関する質問を検索して「サーバー」を検索すると、同じことを言っている説明が見つかります。)

uwsgi_ignore_client_abortは接続クロージャを処理するものであることに注意してくださいTCPレベルで。これは、AJAXリクエストを中止することとは異なります。通常、JavaScriptで実行できるアクションはありませんentail TCP接続を閉じます。ブラウザは、ニーズに合わせて接続の作成と破棄を最適化します。ちょうど今、私はこれをしました:

  1. lsofを使用して、プロセスがexample.comに接続しているかどうかを確認しました。誰もいなくなった。 (lsofは開いているファイルを一覧表示できる* nixユーティリティです。ネットワーク接続は* nixの「ファイル」です。)

  2. Chromeでexample.comのページを開きました。 lsofは、接続とそれを開いたプロセスを示しました。

  3. 次に、ページを閉じました。

  4. lsofでポーリングして、以前に特定した接続がまだ開いているかどうかを確認しました。 接続を開いたままにする必要は実際にはありませんでしたが、ページを閉じてから約1分間開いたままでした。

そして、XMLHttpRequest.abort()を介して実行されたアボートを認識させるuswgi設定をいじる量はありません。

あなたが与えたユースケースのシナリオは、ユーザーがいくつかの結果をすばやくページングしているシナリオでした。私は質問で与えられた説明の2つの可能性を見ることができます:

  1. ユーザーは、さらにページングする前に更新を待ちます。たとえば、アリスはユーザー「ゼノ」のアルファベット順にソートされたユーザー名のリストを調べており、新しいページが表示されるたびに、名前がそこになく、ページが下に表示されていることがわかります。この場合、ユーザーのアクションは処理された要求に依存しているため、中止するものは何もありませんfirst。 (ユーザーは、決定を下す前に新しいページを表示する必要があります。)

  2. ユーザーは、更新を待たずにページを下に移動するだけです。アリスは再び「ゼノ」を探していますが、最後のページになると思っているので、クリック、クリック、クリックして行きます。この場合、サーバーに対して行われたリクエストをデバウンスできます。次に、次のページボタンを押して、表示するページの数を増やします。ユーザーに送信しますが、すぐにリクエストを送信しないでください。代わりに、ユーザーがボタンのクリックをやめてから少し遅れて、finalページ番号でリクエストを送信するので、12回ではなく1回のリクエストを行います。 Here は、DataTables検索に対して実行されるデバウンスの例です。

8
Louis

明らかに、何らかの理由でリクエストが途中で中止されたくない特定のページがあるかもしれません。

これはまさに、これをいずれかの方法で行うことの背後にある問題です。

  • 明らかに、コストのかかる検索操作など、その後中止された接続の処理にシステムリソースを費やし続けたくない場合があります。

  • しかし、おそらく接続は十分に重要であり、クライアントが切断された場合でも接続を処理する必要があります。

    • たとえば、まったく同じ高価な検索操作ですが、実際にはクライアント固有ではなく、後続のすべてのクライアントのためにnginxによってキャッシュされます。

    • あるいは、アプリケーションの状態を変更する操作かもしれません—アプリケーションに一貫性のない状態を持たせたくないのは明らかです!

前述のように、問題はNGINXではなくuWSGIにあります。ただし、uWSGIにそのような意図を自分で明らかにしない限り、uWSGIに意図を自動的に決定させることはできません。

そして、コードで意図をどの程度正確に明らかにしますか?多数のプログラミング言語は、マルチスレッドおよび/または非同期プログラミングモデルを実際にはサポートしていないため、操作をキャンセルすることはまったく簡単ではありません。

そのため、ここには魔法の解決策はありません。 Golangのような並行性に適したプログラミング言語でさえ、WithCancelcontextの周りに問題があります —ブロックする可能性のあるすべての関数呼び出しでそれを渡す必要があるかもしれません。コードを非常に醜くします。

Djangoで上記のコンテキスト受け渡しをすでに行っていますか?そうでない場合、解決策は醜いですが非常に単純です—要求を明確に中止できるときはいつでも、クライアントがまだuwsgi.is_connected(uwsgi.connection_fd())に接続されているかどうかを確認してください。

3
cnst