怎么实现 Django 在启动完成之后调起线程去监听数据源?困扰太久,求戳 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
toono
V2EX    Python

怎么实现 Django 在启动完成之后调起线程去监听数据源?困扰太久,求戳

  •  2
     
  •   toono
    ToonoW 2017-11-07 08:56:16 +08:00 8006 次点击
    这是一个创建于 2924 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求 :在写的一个服务基于 Django 框架,有 http 请求的处理和监听 Kafka 的消息队列。为了解决题目提出的问题还集成了 celery,但是还没找到合适的用法。我是希望 Django 程序启动结束之后就调用我写好的创建线程或者进程的函数( Django 未启动完成运行 Kafka 监听的代码会提示未加载完成的错误)。

    我现有的方案是在 celery 的 tasks 编写一个 sharetask 去执行启动监听线程的方法。然后 task 的调用写在 urls.py 。项目启动完成之后就会运行目标代码。

    但是上面的方案会导致我启动线程的代码被执行两次,影响到了我监听的处理逻辑。

    看到的其他方法:

    1. 查到有人提到可以用 middleware,但是觉得这不是 http 请求生命周期里面的工作,觉得不适用。
    2. 也看到用 AppConfig 的,但是重载这个的 ready 方法,去执行我的函数,会提示未加载完成错误,也不适用。

    所以请教各位大大有什么更好的方法?

    第 1 条附言    2017-11-07 10:44:32 +08:00
    因为使用 celery 不熟练,在 urls.py 下面我直接运行 share_task 装饰的 task 函数了,而不是使用 delay 之类的 task 启动方法,导致了两次运行 task 中的代码。抱歉。

    但是像我的写法我觉得还是不应该的,将非路由逻辑的代码放在 urls.py ,希望 dalao 给出给好的建议。谢谢
    22 条回复    2017-11-07 13:35:47 +08:00
    janxin
        1
    janxin  
       2017-11-07 09:02:55 +08:00
    你想把 celery 的异步动作变成同步监听?
    toono
        2
    toono  
    OP
       2017-11-07 09:10:05 +08:00
    @janxin 不是,我的想法是用 celery 启动线程去监听。如果造成了 celery 同步阻塞就不是我希望的效果了。

    我的想法就是监听 Kafka 的消息,然后对消息进行处理。

    集成上了 celery 可能是多余的,但是对处理这种情景没有经验,比较无头绪。
    Tinet
        3
    Tinet  
       2017-11-07 10:15:12 +08:00
    http 请求的处理和监听 Kafka 的消息队列,不应该分成两个系统来完成么?
    wcsjtu
        4
    wcsjtu  
       2017-11-07 10:19:11 +08:00
    WSGIServer 和 uwsgi 启动顺序是不一样的,middleware 在 master 中 load, 业务代码在 worker 中 load。按照你说的, 你的启动代码只能写在 middleware 所在的文件里了。
    这样,就是在 master 进程里开了个线程, 你确定这是你想要的?
    推荐用 celery 解决
    最后一点建议, 不要在 django 全局位置开线程,django 一般都是工作在多进程模式下的。
    walleL
        5
    walleL  
       2017-11-07 10:23:04 +08:00
    消息处理独立成一个进程不好吗?
    提示未加载完成错误 这是指 Django 的环境未初始化的报错?
    manzhiyong
        6
    manzhiyong  
       2017-11-07 10:24:28 +08:00
    你如果只是想用到 django 的 orm,那么建一个 command,按照后台进程启动就行了。
    lxguidu
        7
    lxguidu  
       2017-11-07 10:26:44 +08:00
    放在 urls.py 里肯定不行的,你试下看看 execute_from_command_line(sys.argv)的代码
    toono
        8
    toono  
    OP
       2017-11-07 10:35:55 +08:00
    @Tinet 本来处理的业务就不多了,再分的话整个系统太散了,而且我觉得这个需求是可以实现的。
    toono
        9
    toono  
    OP
       2017-11-07 10:37:12 +08:00
    @wcsjtu 我使用上了 celery 了,但是还不确定在哪个地方对 task 执行 delay 方法好。
    toono
        10
    toono  
    OP
       2017-11-07 10:38:55 +08:00
    @walleL 独立成进程的话会跟随 Django 关闭吗?这一点我不太确定。

    是的,为初始化的报错。
    toono
        11
    toono  
    OP
       2017-11-07 10:41:00 +08:00
    @manzhiyong Django 的 http 服务我还是要的。
    pixstone
        12
    pixstone  
       2017-11-07 10:42:28 +08:00
    我们一般是开两个应用来处理。
    从 kafka 收到消息后,再丢给 celery 处理,或者直接处理。
    toono
        13
    toono  
    OP
       2017-11-07 10:47:18 +08:00
    @pixstone 我现在就相当于混淆起来了。

    因为使用到了 Django 的 orm 和 http 服务,所以才没有分开来。都是小的功能,如果都分开来就太零散了,打算有了瓶颈的时候才重新设计。
    wcsjtu
        14
    wcsjtu  
       2017-11-07 10:47:44 +08:00
    @toono celery 会 load 你的代码,写在哪里都可以。views,models 都可以。
    chocho
        15
    chocho  
       2017-11-07 10:49:45 +08:00
    supervisor 另起一个进程
    laoyur
        16
    laoyur  
       2017-11-07 10:51:16 +08:00
    @toono #10 未初始化的报错
    我是这么绕过的,import 各种模块,放到 ready 中,而不是放到文件顶部
    toono
        17
    toono  
    OP
       2017-11-07 10:54:06 +08:00
    @wcsjtu 感谢!突然发现新大陆
    pixstone
        18
    pixstone  
       2017-11-07 11:04:46 +08:00
    Python GIL 就是你的瓶颈(笑,所以不开线程处理,还会影响 HTTP 部分的服务质量 。 其实可以考虑用 async 来处理。 不过我自己一直没调通。或者 stackless ( pypy 版集成了)

    所以 supervisor 另起一个进程是一个最简单,性能不会有风险的解决方案。 毕竟 Python 是 Python,不是 Java 后台开一堆线程池处理的,大家都在一个 JVM 干活的模式。
    toono
        19
    toono  
    OP
       2017-11-07 11:10:54 +08:00
    @pixstone 嗯,所以现在打算 supervisor + celery 来解决这个需求。多谢了~
    toono
        20
    toono  
    OP
       2017-11-07 11:12:23 +08:00
    @laoyur 我继续用 celery 去解决这个问题了,用 AppConfig 我觉得逻辑不是很清晰。
    wizardoz
        21
    wizardoz  
       2017-11-07 13:24:20 +08:00
    如果是需要同步监听的话还是建议单独开一个进程去监听,有数据以后 POST 到 django 或者通过消息队列。
    celery 的定时任务我觉得不适合这种场景。
    PS:在 wsgi.py 中可以直接开线程或者进程。但是如果是我的话还是会单独另起一个程序。
    clino
        22
    clino  
       2017-11-07 13:35:47 +08:00
    建议用 jenkins 之类的另外的工具去做
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2117 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 00:59 PVG 08:59 LAX 16:59 JFK 19:59
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86