ローリングコンバットピッチなう!

AIとか仮想化とかペーパークラフトとか

【python】Flaskで作ったWeb/APIサーバーをkeep-alive対応に設定する&requestsで作るクライアントのkeep-alive設定

[technology][python]flaskのHTTP/1.1(keep-alive)対応設定

ちょっと仕事で簡単なWebUIを作る必要があって、数日前からflaskを弄っています。
flaskはpythonでwebアプリケーションをお手軽に書けるフレームワークです。

palletsprojects.com


node.js + expressとかでやっている様な

  • サーバーサイドでのwebページの動的生成(そのためのHTMLテンプレートエンジン)
  • GET,POST等のリクエストのルーティング
  • リクエストからパラメーターやヘッダの抽出
  • json等の送受信

を少ないコード量で書ける様にしてくれるありがたい仕組みです。
Javascript使いならnode.js + expressとかで良いのですが、自分はWebのフロントエンド周りは苦手で、特にJavascriptを使い慣れておらず、なるべくpythonで全部書きたいためflaskを弄り始めた次第。(ブラウザサイドでは最低限Javascript使う必要はあるのですが)


flaskの使い方のチュートリアル的なものはqiita等に色々載っていて、REST APIとか作るだけならこういった記事を参考に真似するだけで超カンタンに出来るのですが、そういえばこれ、HTTP keep-alive対応させるにはどうするんだろう?と疑問が生じたので調べてみました。ちょっと自分が作ろうと思っているものがクライアント側から結構高頻度でREST API叩きそうなので、その度にTCPセッション張り直しは無駄だなと思ったので。

結論から言うと以下の様な感じです。
ソースはStackoverflow
stackoverflow.com

WSGIRequestHandlerをimportして、アプリをrun()する前にHTTP protocol versionとしてHTTP/1.1を設定する。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# flaskをimport
from flask import Flask, render_template, request, redirect, url_for,jsonify
# WSGIRequestHandlerをimport、これにHTTP/1,1対応を設定する
from werkzeug.serving import WSGIRequestHandler

app = Flask(__name__)

〜中略〜

if __name__ == '__main__':
    WSGIRequestHandler.protocol_version = "HTTP/1.1" # HTTP/1.1対応の宣言
    app.run(host='0.0.0.0')

ちなみにpythonでクライアント側を書く場合、httpクライアントとしてはurllib.requestだったり、http.clientだったり、いくつか選択肢があります。requestsが割とシンプルなので、requestsを使う場合だと、Session()というメソッドでHTTPセッションのインスタンスを作り、そこにgetやputを投げれば良いみたいです。

import requests
s = requests.Session()

response1 = s.get('http://localhost:5000/api1')
response2 = s.get('http://localhost:5000/api2')

こんな感じにすると、localhost:5000に対する/api1と/api2のGETが同じHTTPセッション上で実行されます。
Wiresharkでパケット拾いながら確認した限りでは上手く行っている様に見えます。

あとflask側でkeep-alive timeoutを設定する方法が良く判らない。
WSGIServerのattributeにtimeoutがあるみたいだけど、これクラス変数なのかな?
よく判らないので、公式サイトからソースコード落として確認する。
f.gallai.re


ソースコードはwsgiserver.pyに全部入っているみたい。

class HTTPServer(object):

    """An HTTP server."""
〜中略〜
    timeout = 10
    """The timeout in seconds for accepted connections (default 10)."""

〜中略〜
class WSGIServer(HTTPServer):

    """A subclass of HTTPServer which calls a WSGI application."""

〜中略〜
    def __init__(self, wsgi_app, host='0.0.0.0', port=8080, numthreads=10,
                 server_name=None, max=-1, request_queue_size=5, timeout=10,
                 shutdown_timeout=5, accepted_queue_size=-1,
                 accepted_queue_timeout=10, certfile=None, keyfile=None,
                 ca_certs=None):
〜中略〜
        self.timeout = timeout

WSGIServerのインスタンスを初期化する際にクラス変数のtimeoutの値をインスタンス変数のtimeoutに引き継いでいるみたいですね。
ということはインスタンス生成前にこれを書き換えればtimeout値を自由に設定できそうです。
10秒なら自分の目的には問題無いので触らないですけど。

[technology][python]追記: Flaskのログを抑制する

Flaskを使うとAPIサーバーをサクサクっと書けて便利なのですが、巷に載っているサンプルコードの通りに動かすと、HTTPアクセスがある度にログが標準出力に吐き出されます。
これを止めたいなと思って、調べたら、これまたStackoverFlowに答えがありました。


stackoverflow.com


簡単に言えばFlaskはloggingモジュール使っているので、loggingをimportして、ログの出力レベルを設定するなり、出力を止めるなりしなさいという話。

import logging
〜中略〜
app = Flask(__name__)
log = logging.getLogger('werkzeug')
log.disabled = True

ログを止めるのはこんな感じ。実際試してみてうまく行きました。
ログの出力レベルを変えるならlog.disabled = Trueのところを

log.setLevel(logging.ERROR)

等にすると、出力レベルを変えられる。
setHandler()で出力先のファイルを設定して、出力先毎にログレベルを変える様な事も出来ます。