如何使用Celery队列对不稳定的任务提高稳定性

44 阅读2分钟

我们可以使用以下几个方法来增强,下载的稳定性

  1. 代理 一些收费的代理提高下载的稳定性。包括一些很难被采集的数据,通过代理使用个人IP,可以极大的简化下载的这个过程。这是很重要的一点。

  2. 队列 采集是一个比较耗时的过程,所以在这个时候需要使用队列,比如python里的Celery。使用队列的话可以对采集任务进行排队,已实现异步采集。

  3. 重试 我们需要使用重试。重试可以在任务没有达到预期的目标的时候,Celery里有自动重试的机制,我们也可以自己手动的增加某些重试逻辑。这样的话,内容可能第一次采集不下来,但是三次之内它的被采集下来的概率可能很大,或者我们可以放大到五次,十次,这个可以极大的提高获取内容的概率。

  4. 通知 我们需要为每一次采集增加通知,这样我们就可以知道哪些成功了,哪些没有成功。我们可以对于没有成功的采集进行重试。

  5. 自动重试 我们可以同时增加另外一个脚本,这个脚本每隔一段时间重新去检查所有的采集任务的状态,它就会自动触发所有失败的采集。这样的话可以避免在某个时间点采集不下来,换个时间点就行了。

以下是Celery的主要逻辑,包括通知和自动重试

import requests
import json
import logging
import pymysql
import logging

from DB import DB
from review_crawler import ReviewCrawler
from datetime import datetime
from typing import List
from celery import Celery
from review_crawler import ReviewCrawler
from pymysql.converters import escape_string

logging.basicConfig(filename='crawler.log', level=logging.INFO)

app = Celery('tasks', broker='redis://localhost:6379/0')
db = DB()

@app.task(bind=True, max_retries=2)
def run_tasks(self, data):
    try:

        q_url = data['q_url']
        prompts = data['prompts']
        session_id = data['session_id']

        logging.info(f"Crawler started for URL: {q_url}, Session ID: {session_id}")
        reviewCrawler = ReviewCrawler()
        reviews,product_title = reviewCrawler.getReviews(q_url)
        logging.info(reviews)
        if reviews:
            saveReviews(data, reviews)
        else:
            updateQuestionState(session_id, 3)
            message = f"Task succeeded for URL: {q_url}, Session ID: {session_id}. No Review."
            send_slack_notification(message)               
            return {"error": "no Review"}

        question = mergeQuestion(reviews, prompts)

        if question:
            updateQuestion(session_id, question,product_title)
        else:
            return {"error": "updateQuestion error"}

        logging.info(f"Task succeeded for URL: {q_url}, Session ID: {session_id}")
        message = f"Task succeeded for URL: {q_url}, Session ID: {session_id}"
        send_slack_notification(message)        
        return getQuestion(session_id)

    except Exception as exc:
        logging.error("Task failed: %s", exc)

        if self.request.retries == 2:  # Check if it's the third retry
            message = f"Task failed for URL: {q_url}, Session ID: {session_id} after 3 retries. Click here to retry: https://xxxxx={session_id}"
            send_slack_notification(message)            
        else:
            message = f"Task failed for URL: {q_url}, Session ID: {session_id}. Retrying... {self.request.retries+1}"
            send_slack_notification(message)
        raise self.retry(exc=exc, countdown=10)    

def send_slack_notification(message):
    try:
        webhook_url = 'xxx'
        data = {
        'text': message,
        'username': 'research_api',
        'icon_emoji': ':frog:'
        }
        requests.post(webhook_url, data=json.dumps(data), headers={'Content-Type': 'application/json'})
        
    except Exception as e:
        logging.error(f"Error sending Slack notification: {e}")