質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

88.35%

uWSGI+Flaskで、gevent.sleepが並行動作しない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 410

hkcomori

score 19

uWSGI+Flaskで、gevent.sleepが並行動作しない

uWSGI+FlaskのWebアプリをgeventを使って並行動作させようとしています。
具体的には時間がかかる外部IO処理をロングポーリング方式で実装しようとしており、
そのIO待ちをgevent.sleepで待機したいと考えています。

その前段階として、以下のコードでgevent.sleep動作確認したところ、期待と異なる結果となりました。

ソースコード

# 必要なモジュールのimport, Flaskの設定等

@app.route('/sleep1/<int:seconds>')
def sleep1_response(seconds: int):
    gevent.sleep(seconds)
    return jsonify(sleep=seconds)


def func(seconds, data):
    gevent.sleep(seconds)
    print(data)


@app.route('/sleep2/<int:seconds>')
def sleep2_response(seconds: int):
    gevent.spawn(func, seconds, 'test')
    return jsonify(sleep=seconds)

結果

  • /sleep1/10 を同時に2つ開く -> 10秒後にひとつめ、20秒後にふたつめのページが表示される
  • /sleep2/10 を同時に2つ開く -> ページはすぐに表示され、10秒後にuWSGIのログにtestが2つ表示される

解決したいこと

以上の結果から、 /sleep1/10 はsleep動作が並行動作しておらず、
/sleep2/10 はsleepが並行動作していると考えられます。

geventはgevent.sleepを呼ぶとコンテキストスイッチが起こるそうなので、
/sleep1/10 も並行動作することを期待したのですが、
なぜこのような動作となるのでしょうか?

また、Flaskのレスポンスをgevent.sleepで遅延させたい場合、
どのようにすれば良いでしょうか?

uWSGIの設定ファイル

[uwsgi]
base = /path/to/foo
chdir = %(base)
module = webapp.view:app
master = true
processes = 2
gevent = 1024
socket = /tmp/uwsgi_foo.sock
chmod-socket = 666
vacuum = true
die-on-term = true

# ロギング
logto = /var/log/uwsgi/foo.log
log-maxsize = 10485760
log-backupname = %(logto).1
log-encoder = format ${strftime:%%Y/%%m/%%d %%H:%%M:%%S} - ${msgnl}
ignore-sigpipe = true
ignore-write-errors = true
disable-write-exception = true

harakiri = 90   # 処理のタイムアウト秒

# 更新
touch-reload = .uwsgi_touch

その他

  • WebアプリはMySQLへの読み書き処理を含む
  • MySQLの読み書きはSQLAlchemy+PyMySQLを使用している
  • uWSGIのgevent-monkey-patchオプションを有効に有効にすると、SQLAlchemyで例外が発生するようになったため、モンキーパッチは使用していない (DBアクセス中にコネクションが切断される。scoped_sessionをgevent経由で並行アクセスするようになるのが原因?)
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

自己解決いたしました。
テスト方法に問題があったようです。

一本のコネクションでリクエストを出していたため、
応答が返ってくるまで次のリクエストが送られず、
同時にリクエストを送信するという状況を作れていなかったようです。

複数のコネクションでリクエストを出すことで、
/sleep1/10 も、並行して gevent.sleep していることが確認できました。

誤ったテスト方法

Google Chromeで同じURLのタブを複数開く

改善後のテスト方法

Siegeというツールで当該URLにアクセスする

  • siege --concurrent=3 --reps=1 --benchmark http://foo.com/sleep1/10
    ⇒ 10秒後に全ての応答が得られる

  • siege --concurrent=1 --reps=3 --benchmark http://foo.com/sleep1/10
    ⇒ 30秒後に全ての応答が得られる

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る