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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Flask

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

Heroku

HerokuはHeroku社が開発と運営を行っているPaaSの名称です。RubyやNode.js、Python、そしてJVMベース(Java、Scala、Clojureなど)の複数のプログラミング言語をサポートしている。

Webサーバー

Webサーバーとは、HTTPリクエストに応じて、クライアントに情報を提供するシステムです。

JavaScript

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

Python

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

Q&A

解決済

1回答

2331閲覧

flaskアプリをherokuでデプロイした時のpythonのグローバル変数の状態について教えてください

JunkiM

総合スコア17

Flask

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

Heroku

HerokuはHeroku社が開発と運営を行っているPaaSの名称です。RubyやNode.js、Python、そしてJVMベース(Java、Scala、Clojureなど)の複数のプログラミング言語をサポートしている。

Webサーバー

Webサーバーとは、HTTPリクエストに応じて、クライアントに情報を提供するシステムです。

JavaScript

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

Python

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

0グッド

0クリップ

投稿2020/10/02 01:40

編集2020/10/02 02:25

やりたいこと

flaskを使った画像処理のwebアプリを作っています。
画像をアップロードされたらその画像をpython側のグローバル変数imgs(辞書)に自動生成したuniqueIDをキーとして格納しておき、その後、webページの更新があっても画像を参照できる状態にしたいです。

何度も参照したい理由は、
すでに用意されている背景画像に、アップロード画像をペーストしたあとで
位置調整や二値化パラメータなどの微調整をしていくために
何度かページを更新する必要があると考えたためです。

ユーザーによる操作の流れ

画像をアップロード

二値化や位置調整のパラメータをスライダーで決定

変換ボタンをクリック(この後にpython側でimgsを参照しています)

画像処理とユーザーによる微調整がされた画像が表示される

プログラムの流れ

webページで画像をアップロード

javascriptでuniqueIDを生成

python側のimgs(辞書)にkeyをuniqueID、valueをアップロードされた画像として格納

webページの変換ボタンをクリック

webページからGETでURLに画像処理のパラメータとuniqueIDのパラメータを載せて送信

python側でimgs[uniqueID]で画像を参照し(←ここの参照ができるときとできないときがあります)
URLから得られたパラメータに従って画像処理を施して、webページに表示

問題

実際にデプロイして、画像アップロード後にimgsにuniqueIDで参照して画像処理を行い、webページに表示する形を取っているのですが、imgsにその「キーは存在しません」となります。

しかし、変換ボタンを何度かクリックして、「キーが存在しません」が数回出たあとでも、
何度も参照をしていると参照できる時があり、
なぜ参照できるときとできない時があるのかがわかりません。

試したこと

画像のアップロードに時間がかかっているのかと考え、アップロード後に数分待ってから参照を試みましたが、できるときとできないときがあり、よくわかりませんでした。

知りたいこと

herokuにおいてpythonのグローバル変数はどんな状態になっているのでしょうか。

グローバル変数の中身を参照できるときがあるので、変数は保持されているのだと思いますが、なぜ参照できないという状態があるのでしょうか。

プログラムのバグというよりは、herokuのシステムの特徴だと感じました。
よろしくお願いいたします。

該当するプログラム

python

1app = Flask(__name__, static_url_path="") 2 3# 該当のグローバル変数 4imgs = {} 5 6 7@app.route('/upload', methods=['POST', 'GET']) 8def upload(): 9 # グローバル変数imgsを使用する 10 global imgs 11 # webページ側で生成されたuniqueIDをURLから取得 12 if request.method == 'POST': 13 image_id = request.form.get( 14 'imageUniqueID', 'サンプル画像ID', type=str) 15 else: 16 image_id = request.args.get( 17 'imageUniqueID', 'サンプル画像ID', type=str) 18 try: 19 if request.files['image']: 20 # POSTで受け取った画像をPILの画像に変換 21 stream = request.files['image'].stream 22 img_array = np.asarray(bytearray(stream.read()), dtype=np.uint8) 23 img = cv2.imdecode(img_array, cv2.IMREAD_UNCHANGED) 24 img = Image.fromarray(img) 25 26 # グローバル変数imgsにuniqueIDをキーとして画像を格納 27 imgs[image_id] = img 28 except: 29 pass 30 31 # グローバル変数imgsに格納された画像を参照する 32 try: 33 img = imgs[image_id] # ←ここで参照できるときとできないときがあります。 34 except: 35 # キーが存在しないなどのエラーが出たらサンプル画像を取得する(エラー画面を出さないため) 36 img = Image.open('サンプル画像パス') 37 38 # 画像処理を施したアップロード画像を用意された背景画像にペーストする 39 dst = webページに表示する画像 40 dst = dstをwebページ表示用データに変換 41 42 return render_template('index.html', dst=dst) 43 44 45if __name__ == '__main__': 46 app.debug = True 47 app.run(host='0.0.0.0', port=int(os.environ.get("PORT", 5000)))

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

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

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

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

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

FiroProchainezo

2020/10/02 01:52

現象が出ていて、その現象を解決したいということでしたら、現象が再現する最小限のコードを提供ください。 Webアプリでグローバル変数とは何のことを言っていますか? セッションでしょうか? また、herokuの特徴と感じたということは、ローカルでは再現しないということでしょうか?
JunkiM

2020/10/02 02:24

該当するコードを追加させていただきました。 また、ローカルでは再現しない状態でした。 ローカルでは問題なく実行できることを確認しております。 よろしくお願いいたします。
quickquip

2020/10/02 08:28

Procfile はどういう指定でしょう。gunicornあたりを実行していますか?
guest

回答1

0

ベストアンサー

python

1from datetime import datetime 2from flask import Flask 3 4app = Flask(__name__) 5 6d = datetime.now() 7print(str(d)) # ☆ 8 9@app.route('/') 10def index(): 11 return str(d)

Procfile

web: gunicorn app:app --log-file=-

みたいな感じで作ってみましたが、web dynoが1つであるにも関わらず、再読み込みを繰り返すと2種類の結果が観測できます。
heroku logsを確認すると、workerは2つ作成されていますし、☆のログが2つ出ています。
以上からappのインスタンス化が2つされていることが確信できます。

herokuではgunicornの-wオプションのデフォルト値が2なのだと推測されます。(ローカルでは1にも関わらず)

なんでだろうとgunicornのソースを調べてみると、
https://github.com/benoitc/gunicorn/blob/20.0.4/gunicorn/config.py#L611

python

1 default = int(os.environ.get("WEB_CONCURRENCY", 1))

とあって環境変数WEB_CONCURRENCYの影響を受けるようです。

アプリに

python

1import os 2print(int(os.environ.get("WEB_CONCURRENCY", 1)))

を入れてみたところ、結果2を得ました。

ドキュメントからも、
https://devcenter.heroku.com/articles/python-concurrency-and-database-connections#calculating-required-connections

The WEB_CONCURRENCY environment variable is automatically set by Heroku, based on the processes’ Dyno size.

とあり、これで不思議なことは何もなくなりました。

上記ドキュメントにあるとおり、heroku CLIから

heroku config:set WEB_CONCURRENCY=1

を実行するか、Procfileで-wオプションを明に

web: gunicorn app:app -w1 --log-file=-

と指定することで、ワーカーが1つになります。

こちらの方が質問者さんが期待する動作でしょう。


それはそれとして。

https://jp.heroku.com/dynos/lifecycle

dyno は永続的なものではなく、アプリとシステム全体の健全性を維持するため1日に1回以上再起動されます

というようなものですから、グローバル変数にデータを保管するのはHerokuでやることではないように思います。
運が悪い人がエラーを踏む可能性があってもよいのなら構いませんが。

投稿2020/10/02 16:54

quickquip

総合スコア11235

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

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

JunkiM

2020/10/02 21:53

Procfileの方を編集してデプロイし直したら、参照ができない状態がなくなり、問題が解決しました! ワーカーの数の問題だということを全く考えられなかっため、 教えていただき本当にありがとうございます!! dynoが永続的ではないものということについては 画像処理による微調整が完了するまでグローバル変数に格納できていれば良いと考えていて、 数分間状態を保っていられるならば問題ないと考えておりました。 しかしherokuにおいてグローバル変数を使用するのはあまり良い方法ではないということを いただいた回答から知ることができましたので、今後気をつけさせていただきます。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問