🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

4453閲覧

Server-Sent Events(SSE)でデータがときどき欠落して取得できない

obon_t

総合スコア52

Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/01/15 13:20

編集2021/01/16 05:05

前提・実現したいこと

Python(flask)でブラウザでボタンを押したらサーバー側で処理を行い、結果をSSEでブラウザ側に表示するサイトを作っています。しかし、ブラウザで表示されるログは、一部が欠落してスキップしてデータが表示されてしまい苦戦しております。(例:①〜⑩の処理を表示したいが、②の処理がブラウザ側では取得することができないことがときどき起こる)

処理内容

ajaxでブラウザ側のボタンを押したらサーバー側で処理の結果をQueueモージュールにてデータを逐次入れていき、それをwhileの中でデータを取り出し、event-streamでブラウザ側に結果を表示しようと試みてますが、思ったように動作することができておりません。(サーバーの処理後ブラウザへ値を渡したいです)

エラー画面・取得できない例

サーバー側のログを見てみると、①〜⑩の処理は取得できているのですが、ブラウザ側では②を取得することができてません。ブラウザに上手く表示されないデータは、毎回固定ではなく、クリックするタイミングにもより変化してます。(全て正常に動作することもあります)

【ブラウザ側】
ブラウザ側
【サーバー側】
サーバー側

該当のソースコード

PYTHON

1from flask import Flask, Response, request,render_template 2import queue 3import time 4 5app = Flask(__name__) 6q = queue.Queue() 7 8@app.route('/stream') 9def stream(): 10 def event_stream(): 11 while True: 12 event = "test" 13 data = q.get() 14 print(data) 15 yield "event:{}\ndata:{}\n\n".format(event,data) 16 time.sleep(0.1) 17 return Response(event_stream(), mimetype="text/event-stream") 18 19 20@app.route("/ajax", methods=['POST']) 21def ajax(): 22 if request.method == 'POST': 23 print("キューに追加開始") 24 q.put("処理①") 25 q.put("処理②") 26 q.put("処理③") 27 q.put("処理④") 28 q.put("処理⑤") 29 q.put("処理⑥") 30 q.put("処理⑦") 31 q.put("処理⑧") 32 q.put("処理⑨") 33 q.put("処理⑩") 34 print("キューに追加終了") 35 return "ajax end" 36 37 38@app.route("/") 39def index(): 40 return render_template('index.html') 41 42if __name__ == '__main__': 43 app.run(port=5000, debug=True)

HTML

1<!DOCTYPE html> 2<html lang="ja"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <title>Test</title> 7 <link rel="stylesheet" href="static/style.css" /> 8 <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> 9 <script defer src="static/script.js"></script> 10 </head> 11 <body> 12 <input type="button" id="btn" value="btn"> 13 </body> 14</html>

javascript

1$(function () { 2 3 var evtSource = new EventSource("/stream"); 4 evtSource.addEventListener("test",function(e){ 5 console.log(e.data); 6 }); 7 8 $("#btn").click(function () { 9 $.ajax({ 10 url: "/ajax", 11 type: "POST", 12 }) 13 .done(function (data) {console.log(data);}) 14 .fail(function () {console.log("Error");}); 15 }); 16 17});

試したこと・疑っていること

  1. whileの中でsleepを入れると、若干ではあるが気持ち改善するような気がしますが、それでも同様のエラーは依然と起きており、根本的な解決にはいたっておりません。
  2. ブラウザ側で取得できていない値があるときは、サーバー側のログをみてみると法則として、直前にhttpリクエストが走っていることが多いと気づいたのですが、それを発生させないようなコードの書き方がわからないです。(これが直接的な原因なのかわかりません)

補足情報(FW/ツールのバージョンなど)

イメージ説明
・python3.9
・flask
・参考コード:Flaskで簡易版プログレスバー実装して処理の進捗見れるようにしてやんよ!!!

些細なことでも構いませんので、お力添えのほどよろしくお願いいたします。


<2021/1/16 13:15 追記>

app.run()の直前でWSGIRequestHandlerでhttp1.1を指定した結果は下記のようになりました。【参考コード】

python

1WSGIRequestHandler.protocol_version = "HTTP/1.1"

レスポンスヘッダーを見ると、http/1.0からhttp1.1にはなったのですが、Connectionはcloseのままでした。
http1.1を指定
そして、データの欠落についてですが、まだ改善は見られておりません。


<2021/1/16 14:03 追記>

WSGIRequestHandlerでhttp1.1の指定に加えて、Responseヘッダにkeep-aliveを追加。

python

1resp = Response(event_stream(), mimetype="text/event-stream") 2resp.headers['Connection'] = 'keep-alive' 3return resp

Connectionにkeep-aliveが追加されるようにはなったのですが、Connection:closeは残ったまま。
データの欠落も、まだ改善は見られておりません。

イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Flaskの開発サーバーは HTTP/1.0なので、keep-aliveが有効ではないようです。

Chromeの開発者ツールでRequest Headerのソースを見ると、
HTTP1.1でConnection: keep-aliveを要求しても、Response HeaderのソースはHTTP1.0でConnection: close が返ってくることが確認できるかと思います。

EventSourceがコネクションを張りなおしている最中も、サーバーのevent-streamはクライアントにデータをプッシュし続けているんだろうし、その間のデータが欠落している、という状況な気がします。

※HTTP/1.1に変更する方法の情報もありましたが、試してみても変化が無く、開発サーバーはHTTP/1.1に対応していない、という情報もあったのでスタンドアロンで立ち上げてる場合はムリかもしれません。

python

1WSGIRequestHandler.protocol_version = "HTTP/1.1"

ちなみにQueueをグローバルな位置に配置してますが、Webアプリなので複数人でアクセスすると中身の取り合いになって、結局全部届かない事になります(それが意図した動きなら構わないですが、queue.Queueではなく、multiprocessing.Queueを使うべきかもしれません)。

対応するなら、別途何らかのMQサーバーを上げる必要があると思います(RabbitMQ、redisのPub/Sub等)。

投稿2021/01/16 02:17

umau

総合スコア831

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

obon_t

2021/01/16 04:17

ご回答有り難うございます。keep-aliveというものが関係してくるのですね! 教えていただいた、HTTP1.1の指定をしてみたのですが、Connectionはcloseのままででした。(キャプチャ画面を追記) 開発サーバーのため、改善が見られなかったということなのでしょうか? もう少し、keep-aliveを勉強してみようと思います! また、Queueモジュールについてのご指摘ありがとうございます。 こちらについいても盲点でしたので、調べてみようと思います。
umau

2021/01/16 04:38

HTTP/1.1 指定できたんですね。それでもConnection: close を返してくるということは、開発サーバーが対応していない可能性もありますね。何かFlask自体にkeep-aliveをonにする方法があればいいんですが、Document検索してみても見つからず、です。 Responseヘッダに自分で投入は試してみたんですが、私の環境ではうまくいきませんでした。そちら1.1指定できたという事は、試してみてもいいかもしれないですね。 ``` resp = Response(event_stream(), mimetype="text/event-stream") resp.headers['Connection'] = 'keep-alive' return resp ```
obon_t

2021/01/16 05:13

ご回答ありがとうございます! さらに教えて頂いたResponseヘッダにkeep-aliveを追加してみたのですが、Connection:closeは残ったままになってしまいました。(keep-aliveは無事追加されています)データの欠落の改善も反応としては変わらずでした。 もう少し試行錯誤してみようと思います! 引き続き何かわかりましたらご教授いただけると幸いです。
obon_t

2021/01/19 07:03

2021/01/19(追記) 追記:根本的な問題は何だったのかまだ調べ中ですが、FlaskからFastAPIに以降したことより解決いたしました。
umau

2021/01/19 08:46

サーバー変更されたんですね。こちらも、もし本番がGunicornとか使って開発サーバーとして実行しない構成にするなら、そちらで検証して問題なければFlask開発時は諦めたらどうか、と言いかけてました。解決して良かったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問