【问题标题】:2 minutes latency on worker send back result with celery, redis and rabbitMQ工作人员使用 celery、redis 和 rabbitMQ 发回结果的 2 分钟延迟
【发布时间】:2021-12-15 02:02:13
【问题描述】:

我有什么

我用 rabbitMQ 作为代理运行 celery,redis 作为结果后端。我有一个应用程序发送任务和处理任务的工作人员。

我将其部署如下:

  • 应用程序、redis、rabbitMQ 和一个 worker(我们称他为“local_worker”)正在使用 docker-compose 在 azure VM 上运行,因此我使用的是 rabbitMQ 和 redis (6.2.5) 的 docker 版本。 rabbitMQ 和 redis 端口在 VM 上打开,并且这些容器配置了用户名和密码。
  • 我使用连接到虚拟机上运行的 redis 和 rabbitMQ 的 azure 容器实例添加工作程序。

首先,如果您对此架构有建议,我很乐意获得建议。

问题

一切正常,任务被分派给不同的工人,这些工人发回结果等等......

当任务在 30 分钟后发送且没有任务运行时,我观察到当任务未发送到“local_worker”时,redis 延迟为 2 分钟。

  • 我知道这一定来自redis,因为我可以在发送任务后立即在worker容器实例中看到任务的日志。
  • 我使用花和带有 celery prometheus 导出器的 graphana 监控此架构,因此我可以监控任务的延迟。在开花时,潜在任务保持“处理”状态。
  • 在无任务间隔后的第一个任务上还有 120 秒的时间,并且未由“local_worker”处理。
  • 当任务由运行在与 redis 相同的 VM 上的“local_worker”处理时,不会发生这种情况。

这就像 redis 或虚拟机在发回结果之前休眠了 2 分钟。因为它正好是 120 秒(2 分钟),所以我希望它是 redis、celery 或 azure 想要的东西(确定性的东西)

我不使用 redis conf 文件,只使用默认设置(密码除外)来运行 redis 服务器。

感谢您对我的架构和问题的帮助和反馈。

这是我在花中看到的截图。这三个任务是相同的(删除目录)。

第一个和第三个任务已由本地工人处理。第二个已由外部工作人员处理。在外部工作人员的日志上,我在返回结果之前放了一条打印线,这条线已在 14:14:23 打印。所以从这次打印到任务正式结束已经有120秒了。

编辑:

我发现redis_socket_timeout的默认值是120秒。

我删除了redis_retry_on_timeout = True 行并在我的芹菜配置文件中添加了redis_socket_keepalive = True 行。现在我得到的错误是任务以redis.exceptions.TimeoutError: Timeout reading from socket 失败。 我不知道为什么套接字超时而结果已经准备好。是不是我的容器实例的网络有问题?

这是我的 docker-compose:

version: "3.5"
services:

  rabbitmq:
    image: rabbitmq:3.8-management
    restart: always
    ports:
      - 5672:5672
    labels:
      - traefik.enable=true
      - traefik.http.services.rabbitmq-ui.loadbalancer.server.port=15672
      - traefik.http.routers.rabbitmq-ui-http.entrypoints=http
      - traefik.http.routers.rabbitmq-ui-http.rule=(Host(`rabbitmq.${HOSTNAME?Variable not set}.sowit.app`))
      - traefik.docker.network=traefik-public
      - traefik.http.routers.rabbitmq-ui-https.entrypoints=https
      - traefik.http.routers.rabbitmq-ui-https.rule=Host(`rabbitmq.${HOSTNAME?Variable not set}.sowit.app`)
      - traefik.http.routers.rabbitmq-ui-https.tls=true
      - traefik.http.routers.rabbitmq-ui-https.tls.certresolver=le
      - traefik.http.routers.rabbitmq-ui-http.middlewares=https-redirect
    env_file:
      - .env
    environment:
      - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}
      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}

    networks:
      - traefik-public


  redis:
    image: redis:6.2.5
    restart: always
    command: ["redis-server", "--requirepass", "${RABBITMQ_DEFAULT_PASS:-password}"]
    ports:
      - 6379:6379
    networks:
      - traefik-public

  flower:
    image: mher/flower:0.9.5
    restart: always
    labels:
      - traefik.enable=true
      - traefik.http.services.flower-ui.loadbalancer.server.port=5555
      - traefik.http.routers.flower-ui-http.entrypoints=http
      - traefik.http.routers.flower-ui-http.rule=Host(`flower.${HOSTNAME?Variable not set}.sowit.app`)
      - traefik.docker.network=traefik-public
      - traefik.http.routers.flower-ui-https.entrypoints=https
      - traefik.http.routers.flower-ui-https.rule=Host(`flower.${HOSTNAME?Variable not set}.sowit.app`)
      - traefik.http.routers.flower-ui-https.tls=true
      - traefik.http.routers.flower-ui-https.tls.certresolver=le
      - traefik.http.routers.flower-ui-http.middlewares=https-redirect

      - traefik.http.routers.flower-ui-https.middlewares=traefik-admin-auth

    env_file:
      - .env
    command:
      - "--broker=amqp://${RABBITMQ_DEFAULT_USER:-guest}:${RABBITMQ_DEFAULT_PASS:-guest}@rabbitmq:5672//"
    depends_on:
      - rabbitmq
      - redis

    networks:
      - traefik-public

  local_worker:
    build:
      context: ..
      dockerfile: ./setup/devops/docker/app.dockerfile
    image: swtools:app
    restart: always
    volumes:
      - ${SWTOOLSWORKINGDIR:-/tmp}:${SWTOOLSWORKINGDIR:-/tmp}
    command: ["celery", "--app=app.worker.celery_app:celery_app", "worker", "-n", "local_worker@%h"]
    env_file:
      - .env
    environment:
      - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}
      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}
      - RABBITMQ_HOST=rabbitmq
      - REDIS_HOST=${HOSTNAME?Variable not set}
    depends_on:
      - rabbitmq
      - redis
    networks:
      - traefik-public

  dashboard_app:
    image: swtools:app
    restart: always
    labels:
      - traefik.enable=true
      - traefik.http.services.dash-app.loadbalancer.server.port=${DASH_PORT-8080}
      - traefik.http.routers.dash-app-http.entrypoints=http
      - traefik.http.routers.dash-app-http.rule=Host(`dashboard.${HOSTNAME?Variable not set}.sowit.app`)
      - traefik.docker.network=traefik-public
      - traefik.http.routers.dash-app-https.entrypoints=https
      - traefik.http.routers.dash-app-https.rule=Host(`dashboard.${HOSTNAME?Variable not set}.sowit.app`)
      - traefik.http.routers.dash-app-https.tls=true
      - traefik.http.routers.dash-app-https.tls.certresolver=le
      - traefik.http.routers.dash-app-http.middlewares=https-redirect

      - traefik.http.middlewares.operator-auth.basicauth.users=${OPERATOR_USERNAME?Variable not set}:${HASHED_OPERATOR_PASSWORD?Variable not set}
      - traefik.http.routers.dash-app-https.middlewares=operator-auth

    volumes:
      - ${SWTOOLSWORKINGDIR:-/tmp}:${SWTOOLSWORKINGDIR:-/tmp}

    command: ['waitress-serve', '--port=${DASH_PORT:-8080}', 'app.order_dashboard:app.server']
    env_file:
      - .env
    environment:
      - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}
      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}
      - RABBITMQ_HOST=rabbitmq
      - REDIS_HOST=${HOSTNAME?Variable not set}
    networks:
      - traefik-public
    depends_on:
      - rabbitmq
      - redis
networks:
  traefik-public:
    external: true

还有我的 celery 配置文件:

import os
import warnings
from pathlib import Path

# result backend use redis
result_backend_host = os.getenv('REDIS_HOST', 'localhost')
result_backend_pass = os.getenv('REDIS_PASS', 'password')

result_backend = 'redis://:{password}@{host}:6379/0'.format(password=result_backend_pass, host=result_backend_host)


# redis_retry_on_timeout = True
redis_socket_keepalive = True

# broker use rabbitmq
rabbitmq_user = os.getenv('RABBITMQ_DEFAULT_USER', 'guest')
rabbitmq_pass = os.getenv('RABBITMQ_DEFAULT_PASS', 'guest')
rabbitmq_host = os.getenv('RABBITMQ_HOST', 'localhost')




broker_url = 'amqp://{user}:{password}@{host}:5672//'.format(user=rabbitmq_user, password=rabbitmq_pass, host=rabbitmq_host)


include = ['app.worker.tasks', 'app.dashboard.example1', 'app.dashboard.example2']


#task events
worker_send_task_events = True
task_send_sent_event = True

所有 env 变量都已定义,除了我的套接字超时问题外,它运行良好!当我在容器实例上部署新的工作人员时,我设置了环境变量,以便它连接到在 docker-compose 上运行的 rabbitmq 和 redis。

这是我定义 celery 应用程序的 celery 文件:

from celery import Celery
from app.worker import celery_config

celery_app = Celery()
celery_app.config_from_object(celery_config)

【问题讨论】:

  • 你是如何测量这 2 分钟的?在花?试图在代码中检索结果?
  • 是的,有花!它比没有这种延迟的任务运行的持续时间正好多 120 秒
  • flower 偷看事件并且不使用后端 (Redis) - 您可以编辑问题并添加显示这 2 分钟花的屏幕截图吗?
  • 我更新了描述并放了截图。感谢您的关心!
  • 为了确保它不是你做的,我建议添加一些信号 (docs.celeryproject.org/en/stable/userguide/…) - 只需添加日志以确保它卡在哪里

标签: azure docker redis rabbitmq celery


【解决方案1】:

我猜你的 Redis 实例和你的工作人员之间有一些防火墙。 你能登录那个SandboxHost...并确保你可以连接你的redis吗?

您可以使用telnet 执行此操作,例如:

telnet <your_redis_hostname> <your_redis_port>

或使用 redis-cli:

redis-cli -h <your_redis_hostname> -p <your_redis_port>

编辑

好像你不见了result_backend

result_backend = f"redis://username:{result_backend_pass}@{result_backend_host}:6379/0"

并确保您的REDIS_HOST=${HOSTNAME?Variable not set} 有效...

EDIT2

您能否将bind 添加到您的 Redis 命令中:

["redis-server", "--bind", "0.0.0.0", "--requirepass", "${RABBITMQ_DEFAULT_PASS:-password}"]

请注意其安全隐患!

【讨论】:

  • 因为它是一个容器,我无法连接到它并使用此命令。我尝试以工人身份连接自己的机器,我会看看我是否也有问题。如果我有,我将能够运行这些命令。
  • 您的机器可能配置不同,您不能执行到您的容器中吗?不熟悉 azure 容器,但似乎可以:docs.microsoft.com/en-us/azure/container-instances/…
  • 这只是为了隔离问题。我想知道您是如何访问rabbitmq 而不是redis,它们是否也在容器上运行?
  • 感谢您的资源,我认为我无法从正在运行的容器运行命令。我会试试看。 Rabbitmq 和 redis 都在我的虚拟机中的 docker-compose 内的容器上运行。
  • 你能分享你的 docker compose 文件 + 你的 celery redis 配置(Celery 的实例化)
【解决方案2】:

最终将后端更改为 rpc 解决了问题。我用 redis 尝试了不同的方法,但没有成功。一种挖掘方法是使用 tcp-dump 检查套接字以查看它阻塞的位置,但我没有尝试使用 rpc 后端解决了我的问题。

【讨论】:

    猜你喜欢
    • 2013-03-23
    • 1970-01-01
    • 2013-03-01
    • 2012-06-26
    • 2019-02-22
    • 2016-11-11
    • 2011-08-29
    • 2012-04-10
    • 1970-01-01
    相关资源
    最近更新 更多