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

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

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

Gunicorn (Green Unicorn)は、Rubyのunicornをベースに開発されたUNIX向けのPython製HTTPサーバです。他のライブラリとの依存関係がないため、容易にインストールして使用できます。

Webサーバー

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

サーバ

サーバは、 クライアントサーバモデルにおいてクライアントからの要求に対し 何らかのサービスを提供するプログラムを指す言葉です。 また、サーバーソフトウェアを稼動させているコンピュータ機器そのもののことも、 サーバーと呼ぶ場合もあります。

Python

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

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

2回答

4792閲覧

FastAPIでスレッドエラーとなってしまう

p19ljk

総合スコア146

Gunicorn

Gunicorn (Green Unicorn)は、Rubyのunicornをベースに開発されたUNIX向けのPython製HTTPサーバです。他のライブラリとの依存関係がないため、容易にインストールして使用できます。

Webサーバー

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

サーバ

サーバは、 クライアントサーバモデルにおいてクライアントからの要求に対し 何らかのサービスを提供するプログラムを指す言葉です。 また、サーバーソフトウェアを稼動させているコンピュータ機器そのもののことも、 サーバーと呼ぶ場合もあります。

Python

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

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

0クリップ

投稿2021/06/10 07:13

編集2021/06/11 02:08

前提・実現したいこと

FastAPIにてREST APIを作成し、サーバ(Conoha Wing)に乗せてlocalhostで動かし、
.htaccessにてリバースプロキシさせて稼働しています。(フロントはReact)
FastAPIの起動には下記設定でgunicornを利用しています。
■ gunicorn設定ファイル

python

1name = 'gunicorn' 2 3worker_class = 'uvicorn.workers.UvicornWorker' 4workers = 5 5threads = 5 6keepalive = 2

起動後しばらくは良いのですが、
何度かアクセスを行うとスレッド作成ができないエラーが発生してしまいます。

実際にps -fLu [ユーザID]でプロセスを確認してみると、
起動直後はプロセスは6つ(親 + worker 5個)ですが、
アクセス毎にこのプロセスが増えていきどこかのタイミングで上限に達してしまっているようです。

このプロセスは本来はアクセス毎に生成され、通信が終了した時点で消えるものなのではないのでしょうか?
また、このエラーを解決する方法はありますでしょうか?

発生している問題・エラーメッセージ

File ".../api/venv/lib64/python3.6/site-packages/fastapi/dependencies/utils.py", line 550, in solve_dependencies solved = await run_in_threadpool(call, **sub_values) File ".../api/venv/lib64/python3.6/site-packages/starlette/concurrency.py", line 40, in run_in_threadpool return await loop.run_in_executor(None, func, *args) File "uvloop/loop.pyx", line 2658, in uvloop.loop.Loop.run_in_executor File "/opt/alt/python36/lib64/python3.6/concurrent/futures/thread.py", line 123, in submit self._adjust_thread_count() File "/opt/alt/python36/lib64/python3.6/concurrent/futures/thread.py", line 142, in _adjust_thread_count t.start() File "/opt/alt/python36/lib64/python3.6/threading.py", line 846, in start _start_new_thread(self._bootstrap, ()) RuntimeError: can't start new thread

試したこと

通信を受けるメソッドをasyncで定義すると解決するとあったのですがそれでもだめでした。

python

1@router.get("/", response_model=List[schemas.User]) 2async def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): 3 users = crud_user.get_users(db, skip=skip, limit=limit) 4 return users

下記のように構成を小さくして再度試してみました。
結果的にはやはりプロセスが残っており、上限?の70プロセスで毎回エラーとなります。
ただし、DBアクセスを行わない'/'へのアクセスではプロセスの残りはなかったので、
DB周りが怪しいということが分かりました。closeはちゃんとしているんですが。。。
■ FastAPI側

python

1# main.py 2from fastapi import Depends, FastAPI, Request, Response 3from fastapi.middleware.cors import CORSMiddleware 4from sqlalchemy.orm import Session 5from typing import List 6 7from database import models, schemas 8from database.database import SessionLocal, engine 9from database.cruds import user as crud_user 10from dependencies import get_db 11 12models.Base.metadata.create_all(bind=engine) 13 14app = FastAPI() 15 16app.add_middleware( 17 CORSMiddleware, 18 allow_origins=["*"], 19 allow_credentials=True, 20 allow_methods=["*"], 21 allow_headers=["*"], 22) 23 24 25@app.middleware('http') 26async def db_session_middleware(request: Request, call_next): 27 response = Response('Internal server error', status_code=500) 28 try: 29 request.state.db = SessionLocal() 30 response = await call_next(request) 31 finally: 32 request.state.db.close() 33 34 return response 35 36 37@app.get("/") 38async def root(): 39 return {"message": "Hello World!"} 40 41 42@app.get("/users", response_model=List[schemas.User]) 43async def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): 44 users = crud_user.get_users(db, skip=skip, limit=limit) 45 return users 46

python

1# dependencies.py 2from fastapi import Request 3 4 5def get_db(request: Request): 6 return request.state.db 7

python

1# database/database.py 2from sqlalchemy import create_engine 3from sqlalchemy.ext.declarative import declarative_base 4from sqlalchemy.orm import sessionmaker 5 6import json 7 8with open('config.json', mode='r') as f: 9 config = json.load(f) 10 database_info = config['database'] 11 DATABASE_URL = "mysql+pymysql://%s:%s@%s/%s?charset=utf8mb4" % ( 12 database_info['user'], 13 database_info['password'], 14 database_info['host'], 15 database_info['db'], 16 ) 17 18engine = create_engine(DATABASE_URL, echo=False, pool_recycle=60) 19 20SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 21 22Base = declarative_base()

python

1# database/cruds/user.py 2from sqlalchemy.orm import Session 3 4from database import models 5 6from typing import List 7 8 9def get_users(db: Session, skip: int = 0, limit: int = 100) -> List[models.User]: 10 return db.query(models.User).offset(skip).limit(limit).all()

■ API実行用
テスト用に作ったもので実際はReactからaxiosで実行しています。

python

1import subprocess 2 3 4def main(): 5 for i in range(0, 100): 6 print(f'{i+1}回目') 7 subprocess.call('curl http://localhost:8001/users', shell=True) 8 9 10if __name__ == '__main__': 11 main() 12

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

・python => 3.6
・FastAPI => 0.65.1
・uvicorn => 0.13.4
・gunicorn => 20.1.0

サーバ
・Conoha Wing

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

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

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

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

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

guest

回答2

0

自己解決

自己解決しました。
やはりDB周りだったようです。

python

1# dependencies.py 2from fastapi import Request 3 4# def get_db(request: Request): <- old 5async def get_db(request: Request): #<- new 6 return request.state.db

FastAPIのチュートリアルにはoldで書かれているので注意が必要そうですね。

投稿2021/06/11 08:15

p19ljk

総合スコア146

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

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

m2l

2021/06/11 09:57

解決できて良かったです!
guest

0

fastapi側の処理としてどのような形になっていますでしょうか?
レスポンスを返さず保持した状態ですとthreadがどんどん増えていきまして、スレッドエラーになるかと思います。
お手数でなければ該当のurlに対しての処理が書かれたコードをご提示頂くことは可能でしょうか?

投稿2021/06/10 23:18

m2l

総合スコア318

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

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

p19ljk

2021/06/11 02:10

コメントありがとうございます。 構成を小さくして再度テストしてみたものを追記いたしました。 urlに対しての処理はmain.pyのread_usersになります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問