Gunicornでコードを一回だけ実行する

Gunicorn を起動すると、マスタープロセスと指定された数のワーカープロセスが起動される。

複数のワーカープロセスが起動され、あるコードを一回だけ実行したい場合は、server hook を使用する。

例えば on_starting フックを利用し、マスタープロセスが起動される前に呼び出されるため、コードの実行は一回だけで済む。

on_starting 以外、pre_forkpost_fork も利用できる。

  • pre_fork はワーカープロセスがフォークされる前に呼び出される。
  • post_fork はワーカープロセスがフォークされた後に呼び出される。

フォークされたワーカーの数を記録し、そして post_fork でワーカーの数を確認し、1 であればコードを実行する。

import gunicorn.app.base
from flask import Flask


def create_app():
    app = Flask(__name__)
    ...
    return app


class StandaloneApplication(gunicorn.app.base.BaseApplication):
    worker_num = 0

    @classmethod
    def pre_fork(cls, server, worker):
        server.log.info('Ready to fork a new worker...')
        cls.worker_num += 1

    @classmethod
    def post_fork(cls, server, worker):
        server.log.info(f'{cls.worker_num} workers have been forked.')
        if cls.worker_num == 1:
            ...

    def __init__(self, app, options=None):
        self.options = options or {}
        self.application = app
        self.options['pre_fork'] = self.pre_fork
        self.options['post_fork'] = self.post_fork
        super().__init__()

    def load_config(self):
        config = {key: value for key, value in self.options.items()
                  if key in self.cfg.settings and value is not None}
        for key, value in config.items():
            self.cfg.set(key.lower(), value)

    def load(self):
        return self.application


if __name__ == '__main__':
    app = create_app()
    options = {
        'bind': '127.0.0.1:5000',
        'workers': '4',
        'log-level': 'debug',
    }
    StandaloneApplication(app, options).run()