はじめに

こちらの案件で永続的にファイルの変更を監視し、変更があった場合にS3のファイルを更新するというシステムを構築することになりました。

djangoと組み合わせることができそうなwatchdogを採用することにしました。
djangoのコマンドにwatchdogの処理を書き、
ファイルの変更検知した際にdjango内にあるAPIを使って処理を書きました。

watchdogで永続的に処理を実行する為にsupervisordを使っています。
※この記事ではdjangoの設定は割愛します。

djangoのコマンドの記述例
project/management/commands

import argparse

from django.core.management.base import BaseCommand
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
import os
import time
from ...views import IndexViews ←viewに処理を渡します
import logging
import requests
import json
import glob
import datetime
env = environ.Env()
env.read_env('.env')

logger = logging.getLogger(__name__)


class Command(BaseCommand):
    help = 'これはテスト用のコマンドバッチです'

    def add_arguments(self, parser):
        parser.add_argument('request', nargs='+', type=str)

    def handle(self, *args, **options):
        # 起動ログ
        print('フォルダ・ファイル監視スクリプトを起動します。')
        logger.info('フォルダ・ファイル監視スクリプトを起動します。')

        # 現在のフォルダパスを取得する(プログラムが実行されているフォルダパス)
        current_directory = os.path.dirname(os.path.abspath(__file__))
        print(current_directory)

        # インスタンス作成
        event_handler = ChangeHandler()
        observer = Observer()

        # 監視の開始
        observer.start()

        try:
            # 無限ループ
            while True:
                # 待機
                time.sleep(0.05)

        except KeyboardInterrupt:

            # 監視の終了
            observer.stop()

            # スレッド停止を待つ
            observer.join()

            # 終了ログ
            print('フォルダ・ファイル監視スクリプトを終了します。')
            logger.info('フォルダ・ファイル監視スクリプトを終了します。')


class ChangeHandler(FileSystemEventHandler):

    # ファイルやフォルダが作成された場合
    def on_created(self, event):
        filepath = event.src_path
        filename = os.path.basename(filepath)

        request = [filepath.replace('~', ''), filename.replace('~', '')]

        try:
            response = IndexViews.createFile(request)
        except Exception as e:
            logger.info(e)
            logger.info('create FAILURE' + filepath)

        logger.info('create SUCCESS' + filepath)

    # ファイルやフォルダが更新された場合
    def on_modified(self, event):
        filepath = event.src_path
        filename = os.path.basename(filepath)

        logger.info('update START' + filepath)

        try:
            request = [filepath.replace('~', ''), filename.replace('~', '')]
            response = IndexViews.updateFile(request)

        except Exception as e:
            logger.info(e)
            logger.info('update FAILURE' + filepath)

        logger.info('update SUCCESS' + filepath)

    # ファイルやフォルダが移動された場合
    def on_moved(self, event):
        # 移動された場合に呼ばれる今回は使っていない

    # ファイルやフォルダが削除された場合
    def on_deleted(self, event):
        filepath = event.src_path
        filename = os.path.basename(filepath)

        request = [filepath.replace('~', ''), filename.replace('~', '')]

        try:
            response = IndexViews.deleteFile(request)
        except Exception as e:
            logger.info(e)
            logger.info('delete FAILURE' + filepath)

        logger.info('delete SUCCESS' + filepath)

ファイルの変更を検知した場合にファイルパスとファイル名を受け取ってViewに流しています。

supervisorの設定例
まずはsupervisorをインストール

pip install supervisor

設定ファイルを書き出す

echo_supervisord_conf > /etc/supervisord.conf

設定ファイル内のコメントファイルを外しiniファイルを読むようにする

vim /etc/supervisord.conf
コメントアウトを外す
[include]
files = /etc/supervisord.d/*.ini

iniファイルを作成

touch /etc/supervisord.d/django-worker.ini

以下をiniファイルに記述

[program:django-worker-sample]
process_name=%(program_name)s_%(process_num)02d
command=python manage.py {コマンド名}
autostart=true
autorestart=true
user=root
numprocs=1
directory={アプリのパス}
redirect_stderr=true
stdout_logfile=/var/log/supervisor/myprog.log
stderr_logfile=/var/log/supervisor/myprog-error.log

supervisordを起動

systemctl enable supervisord
systemctl start supervisord

監視しているパスでファイルを作成し、動いているか確認

まとめ

この技術を使った事例がこちら:
https://www.iret.co.jp/works/106.html