在 Celery 任务中调用 API 永远不会返回

Posted

技术标签:

【中文标题】在 Celery 任务中调用 API 永远不会返回【英文标题】:Calling API within Celery task never returns 【发布时间】:2020-04-01 01:44:47 【问题描述】:

我想从web3.eth.getTransactionCount 获取一个值。它只是挂起。此功能在其他地方运行良好(普通应用程序、控制台)。

要重新创建此行为,只需创建一个新文件夹,将这 3 个文件添加到文件夹中,然后在该文件夹中运行 docker-compose up。 *请注意,infura 凭证可以安全使用。

码头文件

FROM python:3.7
WORKDIR /usr/src/app
RUN pip install flask celery[redis] web3

docker-compose.yml

version: "3"
services:
  redis:
    image: redis:5.0.7
    container_name: redis
    ports:
      - "6379:6379"
  myapp:
    build: .
    container_name: myapp
    ports:
      - "5000:5000"
    volumes:
      - .:/usr/src/app
    environment:
      - FLASK_ENV=development
      - WEB3_INFURA_PROJECT_ID=1cc71ab02b99475b8a3172b6a790c2f8
      - WEB3_INFURA_API_SECRET=6a343124ed8e4a6f9b36d28c50ad65ca
    entrypoint: |
      bash -c "python /usr/src/app/app.py"
  celery:
    build: .
    container_name: celery
    volumes:
      - .:/usr/src/app
    environment:
      - WEB3_INFURA_PROJECT_ID=1cc71ab02b99475b8a3172b6a790c2f8
      - WEB3_INFURA_API_SECRET=6a343124ed8e4a6f9b36d28c50ad65ca
    command: celery worker -A app.client -l info

app.py

from flask import Flask
from web3.auto.infura.rinkeby import w3 as web3
from celery import Celery

app = Flask(__name__)
client = Celery(app.name, broker='redis://redis:6379', backend='redis://redis:6379')

@client.task
def never_return():
    print('start')  # this is printed
    nonce = web3.eth.getTransactionCount('0x51cDD4A883144F01Bf0753b6189f3A034866465f')
    print('nonce', nonce)  # this is never printed

@app.route('/')
def index():
    never_return.apply_async()
    return "hello celery"

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

我在这里只找到了 1 个类似的未解决帖子:Call to Google Cloud API in Celery task never returns

在 Celery 任务中由其他库发出请求调用时似乎有些奇怪。当我尝试使用request 发出帖子请求时,一切正常。不幸的是,我不知道如何使用这个 request 库来解决这个问题。

非常感谢任何形式的建议。

【问题讨论】:

启动 celery 时是否识别为 celery 任务?你怎么称呼它?你在使用 celery beat 和 schedule 吗?它是否在 docker 容器中运行? 是的,celery 控制台识别该方法并已成功记录“开始”。不使用芹菜节拍和时间表。是的,有 3 个容器:app、redis 和 celery。 您不要在 Celery 任务中调用 print()... 请改用 Celery 记录器。 【参考方案1】:

在我看来,这个问题与 websockets 有关。所以我尝试将其切换到 HTTP。并且有效。

这里是修改后的app.py

from flask import Flask
from web3 import Web3
from celery import Celery
from web3.middleware import geth_poa_middleware
import os

app = Flask(__name__)
client = Celery(app.name, broker='redis://redis:6379', backend='redis://redis:6379')

@client.task
def never_return():
    w3 = Web3(Web3.HTTPProvider(f"https://rinkeby.infura.io/v3/os.getenv('WEB3_INFURA_PROJECT_ID')", request_kwargs='timeout': 60))
    w3.middleware_onion.inject(geth_poa_middleware, layer=0)
    print('started')
    l = w3.eth.getBlock('latest')
    print(f'block number: l')
    print('finished ok')

@app.route('/')
def index():
    never_return.apply_async()
    return f"hello celery"

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

【讨论】:

以上是关于在 Celery 任务中调用 API 永远不会返回的主要内容,如果未能解决你的问题,请参考以下文章

RuntimeError:永远不要在任务 Celery 中调用 result.get()

Django ElasticSearch Celery 任务模型调用返回“str”对象不可调用

为第三方 HTTP 调用优化 Celery

某些 Celery 任务启动但挂起并且从不执​​行

从 Celery 任务向 Channels 发送消息

基于celery及redis封装sanic的api