flask에서 multi-threading 세팅하기

2 분 소요

flask 서버에 multi-threading 세팅하기

  • flask를 서버로 사용하면서, html 페이지에 여러 개의 matplotlib의 figure를 embed하고 싶었습니다.
  • 다음과 같은 html template가 있을 경우 mean, var를 다르게 한 다양한 그림을 한 웹페이지 안에 보여주게 하고 싶은 것이죠.

<html>
  <head>
    <title>random normal - {{mean}}, {{var}} </title>
  </head>
  <body>
    <h2>This is random normal image</h2>
    <h3>this is h3 </h3>
    <p>this is the paragraph</p>
      <img src="{{ url_for('fig', mean=5, var=3) }}" alt="Image Placeholder" 
      width={{width}}, height={{height}}>
    <p>this is the paragraph</p>
      <img src="{{ url_for('fig', mean=100, var=5) }}" alt="Image Placeholder" 
      width={{width}}, height={{height}}>
  </body>
</html>

그러나.

  • 이상하게 저게 잘 안됩니다. 그림이 여러 개 있다면 단지 하나만 출력되고 나머지 하나는 출력되지 않아요.
  • 서로 다른 img 태그 내에서 콜하는 컴포넌트 코드는 대략 다음과 같습니다. 이 코드 상의 문제는 없을 것 같고요.
  • 안되는 이유는 해당 component에서 multiple-threading이 지원되지 않기 때문인 것이 아닐까? 싶었습니다.
@app.route('/fig/<int:mean>_<int:var>')
@nocache
def fig(mean, var):
  plt.figure(figsize=(4, 3))
  xs = np.random.normal(mean, var, 100)
  ys = np.random.normal(mean, var, 100)
  plt.scatter(xs, ys, s=100, marker='h', color='red', alpha=0.3)
  """
  file로 저장하는 것이 아니라 binary object에 저장해서 그대로 file을 넘겨준다고 생각하면 됨
  """
  img = BytesIO()
  plt.savefig(img, format='png', dpi=300)
  img.seek(0)## object를 읽었기 때문에 처음으로 돌아가줌
  return send_file(img, mimetype='image/png')

그래서

  • 단순하게 맨 밑에 아래 코드를 넣어주니까 분명해졌습니다.
  • 단, 아직 이유는 모르겠지만 ‘새로고침’으로는 여전히 특정 이미지 파일에서 잘 읽히지 않네요.
if __name__ == '__main__':
    ## threaded=True 로 넘기면 multiple plot이 가능해짐
    ## host='0.0.0.0', port=5000 을 함께 넘기면 서버 내부의 0.0.0.0의 주소에 5000포트에서 프로그램이 실행됨
  app.run(debug=True, threaded=True)

reference

  • https://medium.com/@dkhd/handling-multiple-requests-on-flask-60208eacc154
  • https://stackoverflow.com/questions/14672753/handling-multiple-requests-in-flask

raw code

hello3.py

from flask import Flask, send_file, render_template, make_response

from io import BytesIO
import numpy as np 

## macOS의 경우 아래 순서에 따라서 library를 import해줘야 에러없이 잘 됩니다. 
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
#################

## remove cache 
from functools import wraps, update_wrapper
from datetime import datetime

def nocache(view):
  @wraps(view)
  def no_cache(*args, **kwargs):
    response = make_response(view(*args, **kwargs))
    response.headers['Last-Modified'] = datetime.now()
    response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
    response.headers['Pragma'] = 'no-cache'
    response.headers['Expires'] = '-1'
    return response      
  return update_wrapper(no_cache, view)
###############

app = Flask(__name__, static_url_path='/static', )

@app.route('/normal/<m_v>')
@nocache
def normal(m_v):
  m, v = m_v.split("_")
  m, v = int(m), int(v)
  return render_template("random_gen.html", mean=m, var=v, width=400, height=300)

@app.route('/fig/<int:mean>_<int:var>')
@nocache
def fig(mean, var):
  plt.figure(figsize=(4, 3))
  xs = np.random.normal(mean, var, 100)
  ys = np.random.normal(mean, var, 100)
  plt.scatter(xs, ys, s=100, marker='h', color='red', alpha=0.3)
  """
  file로 저장하는 것이 아니라 binary object에 저장해서 그대로 file을 넘겨준다고 생각하면 됨
  """
  img = BytesIO()
  plt.savefig(img, format='png', dpi=300)
  img.seek(0)## object를 읽었기 때문에 처음으로 돌아가줌
  return send_file(img, mimetype='image/png')
  # plt.savefig(img, format='svg')
  # return send_file(img, mimetype='image/svg')

#################
if __name__ == '__main__':
    # threaded=True 로 넘기면 multiple plot이 가능해짐
  app.run(debug=True, threaded=True)

random_gen.html


<html>
  <head>
    <title>random normal - {{mean}}, {{var}} </title>
  </head>
  <body>
    <h2>This is random normal image</h2>
    <h3>this is h3 </h3>
    <p>this is the paragraph</p>
      <img src="{{ url_for('fig', mean=5, var=3) }}" alt="Image Placeholder" 
      width={{width}}, height={{height}}>
    <p>this is the paragraph</p>
      <img src="{{ url_for('fig', mean=100, var=5) }}" alt="Image Placeholder" 
      width={{width}}, height={{height}}>
    <p>this is the paragraph</p>
      <img src="{{ url_for('fig', mean=100, var=5) }}" alt="Image Placeholder" 
      width={{width}}, height={{height}}>
  </body>
</html>

댓글남기기