在Flask中在后台运行进程

1 投票
1 回答
2781 浏览
提问于 2025-04-18 09:06

我正在用Python和Flask制作一个网页应用,当网站出现故障时,它会给网站管理员发邮件和推特通知。我想要一个无限循环,每隔10分钟就发送一次请求去检查网站,看看返回的响应是否是200(也就是网站正常)。我的问题是,这个循环应该放在脚本的哪个地方?有没有什么建议可以让我实现这个功能?

1 个回答

3

士兵,告诉我你什么时候死了

从一个崩溃的应用程序报告问题并不是最可靠的方法。

使用外部程序来监控应用

你需要一个独立的外部应用来监控你的应用。

如果你在纯Python环境中,可以写一个脚本,检查访问某个应用的链接是否成功。如果失败了,就可以通知某个人。通知方面,你可以尝试使用 logbook,通过电子邮件发送日志记录(可以查看 MailHandlerGMailHandler)。

在生产环境中,最好运行一些监控应用,比如 Nagios,并简单地使用 check_http 来检查。

使用Logbook的示例检查脚本

为了更好地阅读,内容被分成了多个部分,实际的脚本在一个叫 monitor_url.py 的文件里。

monitor_url.py: 文档字符串、导入和邮件模板

文档字符串解释了使用方法,最后由命令行解析器 docopt 使用。

导入的内容主要与 logbook 相关。

"""monitor_url.py - check GET access to a url, notify by GMail about problems
Usage:
    monitor_url.py   [options] <url> <from> <pswd> <to>...
    monitor_url.py -h

Options:
  -L, --logfile <logfile>    Name of logfile to write to [default: monitor_url.log].
  -N, --archives <archives>  Number of daily logs to keep, use 0 for unlimited [default: 0]

The check is performed once a minute and does HTTP GET request to <url>.
If there is a problem, it sends an e-mail using GMail account.
There is a limit of 6 e-mails, which can be sent per hour.
"""
import time
from logbook import Logger, GMailHandler, StderrHandler, NestedSetup, TimedRotatingFileHandler, Processor
from logbook.more import JinjaFormatter
from datetime import timedelta
import requests
from requests.exceptions import ConnectionError

MAIL_TEMPL = """Subject: {{ record.level_name }} on {{ record.extra.url }}

{{ record.message }}

Url: {{ record.extra.url }}
{% if record.exc_info %}
Exception: {{ record.formatted_exception }}
{% else %}
Status: {{ record.extra.req.status_code }}
Reason: {{ record.extra.req.reason }}
{% endif %}
"""

monitor_url.py: main 函数执行检查

这个脚本在 while 循环中,每分钟执行一次检查。如果发现问题,或者状态码发生变化,GMailHandler 会被配置来发送电子邮件。

def main(url, e_from, pswd, e_to, logfile, archives):
    log = Logger("httpWatcher")
    def inject_req(record):
        record.extra.url = url
        record.extra.req = req
    processor = Processor(inject_req)
    gmail_handler = GMailHandler(e_from, pswd, e_to,
        level="WARNING", record_limit=6, record_delta=timedelta(hours=1), bubble=True)
    gmail_handler.formatter = JinjaFormatter(MAIL_TEMPL)

    setup = NestedSetup([StderrHandler(),
        TimedRotatingFileHandler(logfile, bubble=True),
        gmail_handler,
        processor])

    with setup.applicationbound():
        last_status = 200
        while True:
            try:
                req = requests.get(url)
                if req.status_code != last_status:
                    log.warn("url was reached, status has changed")
                if req.ok:
                    log.info("url was reached, status OK")
                else:
                    log.error("Problem to reach the url")
                last_status = req.status_code
            except ConnectionError:
                req = None
                log.exception("Unable to connect")
                last_status = 0
            time.sleep(6)

monitor_url.py 最后的 if __name__ ...

这一部分实际上解析命令行参数并调用 main 函数。

if __name__ == "__main__":
    from docopt import docopt
    args = docopt(__doc__)
    print args
    main(args["<url>"], args["<from>"], args["<pswd>"], args["<to>"], args["--logfile"],
            int(args["--archives"]))

调用它

尝试不带参数调用它:

$ python monitor_url.py 

用法: monitor_url.py [选项] ... monitor_url.py -h

要获取完整帮助,请使用 -h

$ python monitor_url.py -h
...the help is the same as script docstring...

用于实际监控,这里用于监控 http://localhost:8000

$ python monitor_url.py -L mylog.log -N 2 http://localhost:8000 <yourmail>@gmail.com xxxpasswordxxx notified.person@example.com
{'--archives': '2',
 '--logfile': 'mylog.log',
 '-h': False,
 '<from>': 'your.mail@gmail.com',
 '<pswd>': 'xxxxxxx',
 '<to>': ['notified.person@example.com'],
 '<url>': 'http://localhost:8000'}
[2014-06-09 19:41] INFO: httpWatcher: url was reached, status OK
[2014-06-09 19:41] ERROR: httpWatcher: Unable to connect
Traceback (most recent call last):
....(shortened)...
    raise ConnectionError(e, request=request)
ConnectionError: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 111] Connection refused)
[2014-06-09 19:41] WARNING: httpWatcher: url was reached, status has changed
[2014-06-09 19:41] INFO: httpWatcher: url was reached, status OK

检查在本地文件夹中创建的日志文件。

查看你的Gmail收件箱(如果没有邮件,你可能需要调整密码)。

总结

使用 logbook 也可以发送Twitter通知,但这里没有展示。

运行这个脚本需要Python 2.7,并且你需要安装一些包:

$ pip install logbook jinja2 requests

管理这样的脚本通知并不简单。在这方面可以把这个脚本视为测试版。

使用像 Nagios 这样的解决方案似乎更适合这个目的。

撰写回答