5

python异步任务队列、消息队列

 2 years ago
source link: https://www.hi-roy.com/posts/python%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E9%98%9F%E5%88%97%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

python异步任务队列、消息队列

2013-11-07

提起gevent,就不得不提起greenlet。按照官方解释greenlet是轻量级的并行编程,而gevent呢,就是利用greenlet实现的基于协程的python的网络library。

官方文档:http://www.gevent.org/contents.html

说说协程,进程和线程大家平时了解的都比较多,而协程算是一种轻量级进程,但又不能叫进程,因为操作系统并不知道它的存在。什么意思呢,就是说,协程像是一种在程序级别来模拟系统级别的进程,由于是单进程,并且少了上下文切换,于是相对来说系统消耗很少,而且网上的各种测试也表明,协程确实拥有惊人的速度。并且在实现过程中,协程可以用以前同步思路的写法,而运行起来确是异步的,也确实很有意思。话说有一种说法就是说进化历程是多进程->多线程->异步->协程,暂且不论说的对不对,单从诸多赞誉来看,协程还是有必要理解一下的。

在学习的过程中,结果却发现了不少其他的相关技术:

异步任务队列:

celery http://www.oschina.net/p/celery

celery(芹菜)是一个异步任务队列/基于分布式消息传递的作业队列。它侧重于实时操作,但对调度支持也很好。

celery用于生产系统每天处理数以百万计的任务。

celery是用Python编写的,但该协议可以在任何语言实现。它也可以与其他语言通过webhooks实现。

建议的消息代理RabbitMQ的,但提供有限支持Redis, Beanstalk, MongoDB, CouchDB, ,和数据库(使用SQLAlchemy的或Django的 ORM) 。

celery是易于集成Django, Pylons and Flask,使用 django-celery, celery-pylons and Flask-Celery 附加包即可。

Gearman http://www.oschina.net/p/gearman

一个分发任务的程序框架,可以用在各种场合,与Hadoop相比,Gearman更偏向于任务分发功能。它的 任务分布非常 简单,简单得可以只需要用脚本即可完成。Gearman最初用于LiveJournal的图片resize功能,由于图片resize需要消耗大量计算资 源,因此需要调度到后端多台服务器执行,完成任务之后返回前端再呈现到界面。

消息队列(MessageQueue):

rabbitMQ http://www.rabbitmq.com/getstarted.html

beanstalk http://www.oschina.net/p/beanstalkd

关于某些概念性问题,limodou给出解答如下:

gevent是基于greenlet实现的异步框架。它提供了对io, socket, time等的一些异步的实现,如gevent.sleep(),用greenlet还可以模拟线程式的东西。同时提供monkey_patch机制,可以对python标准库的一些阻塞方法替換为非阻塞,这样原来的程序基本不用修改即支持异步。同时基于gevent还有象gevent-websocket, gevent-socketio等库用来处理web socket和sockio.js的支持。gevent也提供wsgi server的支持。这一点和tornado,不过tornado还可以进行web应用开发,而gevent只用来提供运行的server。 MessageQueue 只是一个消息队列,用于消息的分发。往往异步处理之间协同会考虑使用队列来传递消息。所以单纯的消息队列没什么作用,要和应用处理相结合。

jie chen:

我这边gevent用的比较多, gevent可以当作一个线程库来理解,但有几点要注意一下。 Python自带的线程库是由Python自己来作线程切换(如果我没记错是每运行100行+字节码就进行一次切换),所以它对每个线程的调度时间比较公平。 而gevent只有在当前协程遇到gevent支持的IO阻塞时,才会切换到其它协程运行。所以每个协程的调度时间一般来说不是很公平。 (这种非抢占式的调度策略也有好处,像Python的List,tuple, dict这些复合类型以及其它一些外部操作(sqlite操作)等等,你可以直接在多个协程里当作共享资源使用而不用加锁(那当然你确保在操作这些类型时没有gevent支持的IO阻塞)。 缺点除了上面所说的不公平外,还有就是你要知道所有gevent支持的IO阻塞。) 如果你的应用场景里,gevent支持的IO阻塞比较多的话,可以考虑用gevent, 当然,你对gevent足够了解的话,你也可以根据它的API扩展它所支持的IO阻塞。

有一个使用 django+celery+RabbitMQ 实现异步执行的例子:http://www.oschina.net/question/25940_24780

引用部分内容介绍应用场合:

言归正传,先介绍一下这篇文章的应用场景吧。我们知道大型网站的性能非常重要,然而有时不得不做一些相当耗时的操作。 比如SNS网站的“新鲜事儿”系统,我发帖之后,会给所有关注我的人推送一条通知。乍一看没什么难的,发帖之后找出关注我的人, 然后生成相应的消息记录就行了。但问题是,100个人关注我,就要执行100条INSERT查询,更要命的是,Web服务器是同步的, 这100条查询执行完成之前,用户是看不到结果的。 怎么办呢,这时就轮到消息队列上场了。发帖之后只需给队列发送一条消息, 告诉队列“我发帖子了”,然后把发帖的结果返回给用户。 这时另一个叫做worker的进程会取出这条消息并执行那100条INSERT查询。这样,推送通知的操作在后台异步执行, 用户就能立即看到发帖结果。更精彩的是,可以运行多个worker实现分布式,多繁重的任务都不在话下了。

“我其实还一直有个疑问: celery 跑 job 是可以,但是它是不是一定要通过 broker 的形式?有时候别人只是想跑个 job,而不是发异步消息跑一个 worker” worker是指进程,broker是指容器(即你的任务存放在哪,比如mongodb、redis),那job是不是工作列表?(消息队列) 有一篇文章写出了流程(全文,写的很好。http://www.dongwm.com/archives/shi-yong-celeryzhi-liao-jie-celery/):

  1. 使用django-celery或者直接操作数据库(settings.py里面指定)添加任务,设置的相关属性(包含定时任务的间隔)存入数据库.
  2. celerybeat通过djcelery.schedulers.DatabaseScheduler获取django内你设置的任务周期 性的检查(默认5s),发现需要执行某任务讲其丢入你设置的broker(我这里是rabbitmq),他会更具settings.py的设置放到对应的 队列
  3. 在你启动了celery worker(以前是celeryd)的服务器上,根据worker也会定期(默认5s)去broker里面查找需要它执行的队列里面是否有任务
  4. 当发现队列有要执行的任务,worker将它取出来执行,执行完的结果通过celerycam(默认30s,所以这个进程也要启动)写入django设置的数据库,更新了这个任务的状态.比如花费的时间

我自已内部就直接使用redis的list,它支持阻塞方式读取,和队列功能一样。 —limodou

mongodb、redis居然还能用来作消息队列? 涨知识了!

http://www.oschina.net/question/12_29127


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK