【问题标题】:Best way to re/use redis connections for prometheus django exporter为prometheus django exporter重新/使用redis连接的最佳方法
【发布时间】:2021-06-16 19:22:12
【问题描述】:

我遇到了一个错误

redis.exceptions.ConnectionError: Error 24 connecting to redis-service:6379. Too many open files.
...
OSError: [Errno 24] Too many open files

我知道这可以通过增加ulimit 来解决,但我认为这不是问题所在,而且这是在容器上运行的服务。 应用程序正确启动 48 小时后正常工作,然后出现上述错误。 这意味着连接随着时间呈指数增长。

我的应用程序基本上在做什么

  • background_task(使用 celery 运行)-> 从 postgres 收集数据并将其设置在 redis 上
  • prometheus 在 '/metrics' 处到达应用程序,这是一个 django 视图 -> 从 redis 收集数据并使用 django prometheus exporter 提供数据

代码看起来像这样

views.py

from prometheus_client.core import GaugeMetricFamily, REGISTRY
from my_awesome_app.taskbroker.celery import app


class SomeMetricCollector:

    def get_sample_metrics(self):
        with app.connection_or_acquire() as conn:
            client = conn.channel().client
            result = client.get('some_metric_key')
            return {'some_metric_key': result}

    def collect(self):
        sample_metrics = self.get_sample_metrics()
        for key, value in sample_metrics.items():
            yield GaugeMetricFamily(key, 'This is a custom metric', value=value)


REGISTRY.register(SomeMetricCollector())

tasks.py

# This is my boilerplate taskbroker app
from my_awesome_app.taskbroker.celery import app
# How it's collecting data from postgres is trivial to this issue.
from my_awesome_app.utility_app.utility import some_value_calculated_from_query


@app.task()
def app_metrics_sync_periodic():
    with app.connection_or_acquire() as conn:
        client = conn.channel().client
        client.set('some_metric_key', some_value_calculated_from_query(), ex=21600)
        return True

我认为 tasks.py 中的后台数据收集不会导致 Redis 连接呈指数增长,但这是导致 views.py 中的 Django 视图 '/metrics' 的原因。

你能告诉我我在这里做错了什么吗? 如果有更好的方法从 Django 视图中读取 Redis。 Prometheus 实例每隔5s 抓取一次 Django 应用程序。

【问题讨论】:

    标签: python django redis celery prometheus


    【解决方案1】:

    这个答案是根据我的用例和研究得出的。

    据我说,这里的问题是,对/metrics 的每个请求都会启动一个新线程,views.py 在Celery 代理的连接池中创建新连接。

    这可以通过让Django 管理自己的Redis 连接池通过cache backend 和Celery 管理自己的Redis 连接池而不使用各自线程中的彼此的连接池来轻松处理。

    Django 端

    config.py

    # CACHES
    # ------------------------------------------------------------------------------
    # For more details on options for your cache backend please refer
    # https://docs.djangoproject.com/en/3.1/ref/settings/#backend
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://localhost:6379/0",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            },
        }
    }
    
    

    views.py

    from prometheus_client.core import GaugeMetricFamily, REGISTRY
    # *: Replacing celery app with Django cache backend
    from django.core.cache import cache
    
    
    class SomeMetricCollector:
    
        def get_sample_metrics(self):
            # *: This is how you will get the new client, which is still context managed.
            with cache.client.get_client() as client:
                result = client.get('some_metric_key')
                return {'some_metric_key': result}
    
        def collect(self):
            sample_metrics = self.get_sample_metrics()
            for key, value in sample_metrics.items():
                yield GaugeMetricFamily(key, 'This is a custom metric', value=value)
    
    
    REGISTRY.register(SomeMetricCollector())
    
    

    这将确保Django 将保持其Redis 连接池,并且不会导致不必要地启动新连接。

    芹菜面

    tasks.py

    # This is my boilerplate taskbroker app
    from my_awesome_app.taskbroker.celery import app
    # How it's collecting data from postgres is trivial to this issue.
    from my_awesome_app.utility_app.utility import some_value_calculated_from_query
    
    
    @app.task()
    def app_metrics_sync_periodic():
        with app.connection_or_acquire() as conn:
            # *: This will force celery to always look into the existing connection pool for connection.
            client = conn.default_channel.client
            client.set('some_metric_key', some_value_calculated_from_query(), ex=21600)
            return True
    
    

    如何监控连接?

    • 有一个很好的 prometheus celery exporter 可以帮助您监控 celery 任务活动,但不确定如何添加连接池和连接监控。
    • 每次在 Web 应用程序上点击 /metrics 时手动验证连接是否在增长的最简单方法是:
      $ redis-cli
      127.0.0.1:6379> CLIENT LIST
      ...
      
    • client list 命令将帮助您查看连接数是否在增长。
    • 遗憾的是,我不使用队列,但我建议使用队列。这就是我的工作人员的运行方式:
      $ celery -A  my_awesome_app.taskbroker worker --concurrency=20 -l ERROR -E
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-06
      • 2020-06-18
      • 2021-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多