当前位置: 首页 > 科技观察

Celery使用过程中遇到的一些问题

时间:2023-03-11 23:02:39 科技观察

本文转载自微信公众号《新钛云服务》,作者黄平安。转载本文请联系新钛云服务公众号。在做项目时,使用了像Celery这样的工具。前段时间遇到一些问题,解决后没有总结。今天我会花点时间把它们记录下来。用过Celery的程序员都知道,它是一个异步执行程序的工具。里面还有Worker、Task等概念,这里就??不细说了。一、功能需求在使用Celery的过程中,需要知道Task的状态。任务是一个异步任务。如果用户没有执行一个异步任务,就会创建一个新的Task来代表这个异步任务。Task对象包含很多信息,包括状态。在我的项目中,需要根据Task的状态来判断异步任务是否还在执行中。2.有问题。由于我需要Task的状态,所以我需要查看如何获取Task的状态!我检查了芹菜的源代码!我发现Celery的AsyncResult对象中有一个state字段。如下图所示:根据源码中的注释,该字段有几个值。它们是:PENDING、STARTED、RETRY、FAILURE、SUCCESS。然后,我赶紧写了一个demo来验证一下state字段是不是我想要的。demo如下:我在项目中执行Celery异步任务,根据之前查到的任务id。执行演示以查询任务的状态。这时候,问题就出来了。根据demo返回的任务状态,是PENDING。表示Task仍在等待,尚未执行。这是错误的,此时状态应该是STARTED,因为我的Task已经执行了很久,返回的结果是不准确的。3.解决问题。是不是我用的字段不对,然后google了一下。发现Celery官网和网上的反馈大多也是表示Task的status字段为state。那么,为什么我的测量结果与理论结果不同呢?然后,详细查看了Celery的配置,发现了一个参数:CELERY_TRACK_STARTED。这个参数默认是关闭的,也就是说只要Celery开始执行Task,它就会跟踪Task。因此,启用该参数后,Task的状态一直记录在BACKEND中。好的,我在Celery的配置文件中加入了这个参数。然后执行Celery的异步任务,结果就是我想要的。4.延伸思考和问题我的问题解决了,但这又引起了我对Celery的兴趣。当时就考虑如果直接kill正在运行的Task任务。那我就看看这个时候Task的状态,会是什么?STARTED,正在执行的状态。这个时候Task已经关闭了,不应该是这个状态。为此,我猜测这应该是Task在没有改变Task状态的情况下意外结束造成的。但是这样不太好,因为只要是程序,就一定有意外退出的可能。假设,我的项目需要检查任务的状态。当Task被意外杀死时,Task在项目中的状态会不准确。5.延伸解题思路。当时就想:既然任务被kill掉后还能显示为running,说明这个任务的状态一定是保存在某个地方的。如果我清除这个任务的数据,它就结束了。至于Celery的数据存储,只有三个可能的地方:使用RabbitMQ的消息代理(BROKER),使用Redis的任务结果存储(BACKEND),文件存储(当然,这基本不可能,Celery从来没有这样用过.,我主要是死马当活马医)。这三个地方,只有Redis可以存放Task的状态,按照Celery的机制,它是最有可能存放的。但是什么?为了了解Celery的存储机制,想试试Celery会不会把数据存储在RabbitMQ中?然后,我执行了以下命令来清除RabbitMQ队列。此时,RabbitMQ队列中的数据为空。然后我检查了任务的状态,它仍然是开始的。说明它不存储Task的状态。然后,我进入redis。使用keys*命令,可以找到许多带有celery-task-meta前缀的记录。后来发现这些记录的后缀是Celery中Task的id。根据我的Task的id,找到了如下内容:我们可以清楚的看到内容中Task的状态是STARTED。这说明它确实存储在Redis中。然后,我删除这条记录,再执行demo,Task的状态不再是STARTED,在项目中显示的状态是正确的。但是,这又引出了另一个问题,如何删除这条记录,或者什么时候删除这条记录。当然我们删除也很容易,编程语言的redis模块或者Celery本身提供的代码都可以删除。Celery根据任务id删除后台的数据。那么,这条记录应该在什么时候被删除呢?默认情况下,Celery将此数据保留24小时。我想了想,还是决定不删除这条记录。让我们用另一种方式解决这个问题!6、问题的扩展解决方案其次,你要知道Celery的Task是运行在Worker上的。只要判断这时候Worker程序是否还在正常运行,是否可以判断出Task的状态是否还在运行呢?我们开始做吧。我们可以通过ps命令查看Celery运行的程序。然后,我们在执行任务的时候,记录下它运行的程序的进程pid,发现正好是Worker的进程pid。这个很简单,我们只需要结合Celery提供的查看Task状态的接口,以及Python提供的Psutil模块查看进程即可。终于可以判断Task是否真的在运行了。只有当Task的状态为STARTED,并且Task所在的Worker进程正在运行时,Task才真正在运行。Psutil检查进程是否正在运行。代码如下:7.总结今天只是记录一下前段时间遇到的问题和解决方法,并没有写Celery的内部机制等等,这些东西网上一大堆,我不是很好他们的必要性。做了几年程序员,最感触的就是解决问题的思路。一旦遇到某个问题,一种思维方式解决不了,可以用另一种思维方式解决,另一种思维方式不一定能完美解决,但可以加深对问题的理解。但是如何去想另一种思维方式,需要更多的积累和自己认知范围的提升,还是比较难的。