Celery 源码学习(一)架构分析

01 前言:神奇的消息队列

对于后端工程师而言,消息队列并不是一个新奇的概念,也不是什么奇技淫巧。实际上,很多公司都会用到消息队列。基本上任何一家大型公司都有一套自己成熟的消息队列体系。

如果有想学习python的程序员,可来我的python学习扣qun:711944363,免费送python的视频教程噢!我每晚上8点还会在群内直播讲解python知识,欢迎大家前来学习哦

 

在我看来,消息队列主要能解决以下场景的问题:

  1. 非实时数据离线计算
  2. 业务逻辑拆分,降低耦合
  3. 异步响应数据,提高用户体验

其实在知乎,很多行为,比如你点一个赞,写一篇回答,发表一篇文章,启用匿名等等,背后都会有很多消息发送出去。不同的业务方收到后做不同的处理,比如算法会计算回答、文章的分值,做一些标签的判定,后台系统可能会记录回答,文章的 meta 信息等等。

02 什么是 celery?

接触 python 的同学肯定不会陌生,即便没有用过,但应该也会听说过。celery 是 python 世界中最有名的开源消息队列框架。他之所以特别火爆,主要在于它实现了以下几点:

  1. 性能高,吞吐量大
  2. 配置灵活,简单易用
  3. 文档齐全,配套完善

其实除了以上的优点外,celery 的源码也有很大的价值,可以说是软件工程设计模式的典范。不过在这篇文章中我不会涉及源码的解读,更多的会介绍下 celery 架构设计中的一些哲学。

03 高性能:多进程事件驱动的异步模型

说到高性能,尤其是对于消息队列来说,很多人不以为然,认为高性能是没有意义的。其实这也不无道理。主要表现为以下几点:

  1. 离线任务本身不要求实时性,一秒处理 100 和一秒处理 1000 个任务没有本质的区别。
  2. 如果非要提高吞吐量,可以通过扩容等更加灵活的手段。而且资源的开销并不是很大。
  3. 吞吐量过高,反而有可能是坏事。比如把上游业务打崩,把 mysql 打崩,把系统连接打满等等。

虽然对于离线队列来说,性能不重要。但是,这并不妨碍我们从学习的角度看待 celery 的架构原则。celery 的高性能主要靠两个方面来保证,一个是多进程,一个是事件驱动。下面我分别来讲一下他们的设计思想。

  1. 珠玉在前

很多人应该对 nginx 不陌生,提到 nginx,我们首先想到的,就是 nginx 是一个高性能的反向代理服务器。而 celery,可以说相当程度上借鉴了 nginx。

2. 消费模型

celery 的核心架构,分成了调度器(master/main process) 和 工作进程(slaves/worker processes),也就是我们常说的主从。

celery 的消费模型很简单,调度器负责任务的获取,分发,工作进程(slaves/worker processes)的管理(创建,增加,关闭,重启,丢弃等等),其他辅助模块的维护等等。工作进程负责消费从调度器传递过来的任务。

具体流程:调度器首先预生成(prefork)工作进程,做为一个进程池(mutiprocessing-pool),之后通过事件驱动(select/poll/epoll)的方式,监听内核的事件(读、写、异常等等),如果监听到就执行对应的回调,源源不断的从 中间人(broker)那里提取任务,并通过 管道(pipe)作为进程间通讯的方式,运用一系列的路由策略(round-robin、weight 等等)交给工作进程。工作进程消费(ack)任务,再通过管道向调度器进行状态同步(sync),进程间通讯等等行为。

当然,这只是一个很粗粒度的描述,其实 celery 内部还实现了很多有趣的功能,比如 prefetch,集群监控与管理,auto-scaler,容灾恢复等等,这些非核心功能的模块暂时还不会涉及,以后可以单独拆出来看他是怎么实现的。

3. 高效的理由

可以思考一下,为什么这种架构方式性能非常高。

首先,我们分析下调度器。调度器是一个事件驱动模型,什么事事件驱动,其实就是它消灭了阻塞。正常的单线程模型,一次只能拿一条消息,每一次都要走一条来和回的链路,并且需要一个 while True 的循环不断的去检测,这样无疑是非常低效且开销大的。而事件驱动则不这样,他可以同时发送多个检测的信号,然后就直接挂起,等待内核进行提示,有提示再去执行对应的回调。这样既优雅的化解了单线程每次都要检测的 while True,又通过多次请求并发降低了重复链路。

然后,我们看一下工作进程用多进程的优势。业内有经验的工程师,在配置容器的时候,经常会使用 n 核,n*m worker 数的配置。这是因为,多进程可以良好的发挥每个核的计算能力。而且多进程良好的分摊了并发请求的处理压力,同时,多进程内部,还可以使用多线程、异步等方式。这样,可以在充分利用多核计算优势的基础上,再充分利用单个线程非阻塞模型的优势。

好,关于 celery 的设计架构大概就讲到这里,之后会从源码的角度分析下上面的那一系列流程是怎么实现的。加油~

猜你喜欢

转载自blog.csdn.net/w17688977481/article/details/88722929