APSchedule

from functools import wraps

def singleton(cls,*args,**kwargs):
    isinstance={}

    @wraps(cls)
    def _singleton():
        if cls not in isinstance:
            isinstance[cls]=cls(*args,**kwargs)
        return isinstance[cls]
    return _singleton


 

但是在本地直接runserver 测试的时候,会发现定时任务每次执行了两次,后面发现,runserver 默认情况会起一个进程来监控代码的变更,而多进程下,单例模式是无法起作用的,所以需要在runserver 后面加上:

python3 manage.py runserver --noreload  # 这样就不会启动autoreload这个进程

    1

本地测试没有问题,开始部署项目,采用的也是比较通用的Nginx + uwsgi + Django 这一套
部署完成之后发现,定时任务没有启动,排查原因,由于我们之前是通过manage.py来启动项目,而当用uwsgi来启动时,与django进行通信的是wsgi.py。所以将启动定时任务命令放在wsgi.py里面。
重新部署,发现定时任务被执行了多次,我们的uwsgi.ini配置如下:

socket = xxx.socket
chdir = xxx
wsgi-file=xxx/wsgi.py
#home=xxx
#主进程
master = true
# 开发模式
py-autoreload=1
#子进程数
workers = 4
stats = xxxx:xxx
reload-mercy = 10
#退出、重启时清理文件
vacuum = true
max-requests = 1000
##通过使用POSIX/UNIX的setrlimit()函数来限制每个uWSGI进程的虚拟内存使用数
limit-as = 2048
#设置用于uwsgi包解析的内部缓存区大小为64k。默认是4k。
buffer-size = 32768
# 请求超时时间
harakiri = 60
pidfile =xxx/uwsgi9000.pid
#pid文件,用于下面的脚本启动、停止该进程
daemonize = xxx/uwsgi9000.log

 

**一开始以为是woker = 4引起的,改为1之后也还是一样执行多次,后面百度了很多,借鉴了 一个大神的 方法,就是启用文件锁,**具体参考:https://blog.csdn.net/raptor/article/details/69218271

启用文件锁之后,定时任务执行多次的任务以解决,但此时又出现了新的问题,页面发起一个请求后,一直处于pending状态,需要过个10s左右才能正常,后面还是不断复现这个问题。查看uwsgi 的log日志,没有什么值得参考的信息。怀疑是进程被异常挂起或者阻塞住了,面向百度疯狂搜索,找到以下几个方案:

    1,X uwsgi 默认是one thread  one process,需要在配置文件里面加上一条 enable-thread = true,也就是允许程序内部启动多线程。试过之后,没有效果;
    2,X 配置文件加上lazy=true,由于uwsgi默认情况是先加载一遍程序,然后再根据woker的数量不断fork自己,所以在lazy=true的情况下,会每一次请求都去加载整个程序,可惜启用后还是没效果;
    3,√  将定时任务启动命令放在 配置文件的mule参数下,单独起一个进程来执行整个定时任务,顺利解决。可以参考:https://stackoverflow.com/questions/24352634/would-starting-apscheduler-in-a-uwsgi-app-end-up-with-one-scheduler-for-each-wor

 

最终的uwsgi.ini配置文件如下:

socket = xxx.socket
chdir = xxx
wsgi-file=xxx/wsgi.py
#home=xxx
#主进程
master = true
# 开发模式
py-autoreload=1
#子进程数
workers = 4
stats = xxxx:xxx
reload-mercy = 10
#退出、重启时清理文件
vacuum = true
max-requests = 1000
##通过使用POSIX/UNIX的setrlimit()函数来限制每个uWSGI进程的虚拟内存使用数
limit-as = 2048
#设置用于uwsgi包解析的内部缓存区大小为64k。默认是4k。
buffer-size = 32768
# 请求超时时间
harakiri = 60
pidfile =xxx/uwsgi9000.pid
#pid文件,用于下面的脚本启动、停止该进程
daemonize = xxx/uwsgi9000.log
enable-threads=true
preload=True
# 单独的进程,request请求不会转发进来
mule=xxx/start_timer.py

start_timer.py代码如下:

import RunTimer
import atexit
import fcntl
def pre_run():
    t = RunTimer()
    t.start()

def start_timer():
    # 定义锁文件
    f = open(" /xxx/automation.lock", "wb")
    try:
        # 开启排斥(劝告锁),非阻塞锁,锁文件存在则不创建新的对象
        fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
        # 开启定时任务
        pre_run()

        import uwsgi
        # 使用main_loop阻塞进程,防止进程挂起
        while True:
            sig = uwsgi.signal_wait()
    except Exception as e:
        pass
    # 给文件解锁
    def unlock():
        fcntl.flock(f, fcntl.LOCK_UN)
        f.close()
    # 注册程序退出时的回调函数
    atexit.register(unlock)

start_timer()

---------------------
作者:marin_1995
来源:CSDN
原文:https://blog.csdn.net/qq_41606405/article/details/88053840
版权声明:本文为博主原创文章,转载请附上博文链接!

我添加了--enable-threads = true后并没有用.
后来按照stackoverflow的这个回答Would starting APScheduler in a uwsgi app end up with one scheduler for each worker?找到了uWSGI的mule模块.
mule与普通worker进程一样, 但是request请求不会转发到mule进程, 可以通过这个进程运行定时任务.

最终我的uwsgi.ini为

[uwsgi]
socket = /usr/local/uwsgi/var/run/saltops.sock
chdir = /var/www/html/saltops

virtualenv = /opt/saltops_env
uid = nginx
gid = nginx

logto = /usr/local/uwsgi/var/log/saltops.log
wsgi-file = saltops/wsgi.py
enable-threads = true
mule = cronjob/__init__.py

cronjob/__init__.py的内容为

# -*- coding: utf-8 -*-

from apscheduler.scheduler import Scheduler
from saltstack.cron_jobs import healthCheck

sched = Scheduler()
sched.add_interval_job(healthCheck, seconds = 30)

sched.start()

## 下面这句加在定时任务模块的末尾...判断是否运行在uwsgi模式下, 然后阻塞mule主线程(猜测).
try:
    import uwsgi
    while True:
        sig = uwsgi.signal_wait()
        print(sig)
except Exception as err:
    pass

...mule可以独立实现定时器的功能, 也可以通过uwsgi提供的模块实现cron类型的任务, 但是这样就与uwsgi服务紧耦合了, 我很不喜欢, 所以还是选择APScheduler的实现.

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值