AWS Redis + UWSGI за NGINX - высокая нагрузка

Я запускаю приложение на python (flask + redis-py) с помощью uwsgi + nginx и использую aws эластичную боль (redis 2.8.24).

пытаясь улучшить время отклика приложения, я заметил, что при высокой нагрузке (500 запросов в секунду / в течение 30 секунд с использованием loader.io) я теряю запросы (для этого теста я использую только один сервер без нагрузки балансировщик, 1 экземпляр uwsgi, 4 процесса, специально для тестирования). стресс тест

Я покопался немного глубже и обнаружил, что при такой нагрузке некоторые запросы к ElastiCache выполняются медленно. например:

  • нормальная загрузка: cache_set time 0.000654935836792
  • большая нагрузка: время кэширования 0,0122258663177это не происходит для всех запросов, просто происходит случайно.

Мой AWS ElastiCache основан на 2 узлах на cache.m4.xlarge (настройки конфигурации AWS по умолчанию). Просмотреть текущих клиентов, подключенных за последние 3 часа: клиенты упругой боли

Я думаю, что это не имеет смысла, так как в настоящее время 14 серверов (8 из них с высоким трафиком XX RPS используют этот кластер), я ожидаю увидеть гораздо более высокую скорость клиентов.

Конфигурация uWSGI (версия 2.0.5.1)

processes = 4
enable-threads = true
threads = 20
vacuum = true
die-on-term = true
harakiri = 10
max-requests = 5000
thread-stacksize = 2048
thunder-lock = true
max-fd = 150000
# currently disabled for testing
#cheaper-algo = spare2
#cheaper = 2
#cheaper-initial = 2
#workers = 4
#cheaper-step = 1

Nginx - это просто веб-прокси для uWSGI, использующий сокет unix.

Вот как я открываю соединение с Redis:

rdb = [
    redis.StrictRedis(host='server-endpoint', port=6379, db=0),
    redis.StrictRedis(host='server-endpoint', port=6379, db=1)
]

Вот как я устанавливаю значение, например:

def cache_set(key, subkey, val, db, cache_timeout=DEFAULT_TIMEOUT):
    t = time.time()
    merged_key = key + ':' + subkey
    res = rdb[db].set(merged_key, val, cache_timeout)
    print 'cache_set time ' + str(time.time() - t)
    return res

cache_set('prefix', 'key_name', 'my glorious value', 0, 20)

Вот как я получаю значение:

def cache_get(key, subkey, db, _eval=False):
    t = time.time()
    merged_key = key + ':' + subkey
    val = rdb[db].get(merged_key)
    if _eval:
        if val:
            val = eval(val)
        else:  # None
            val = 0
    print 'cache_get time ' + str(time.time() - t)
    return val

cache_get('prefix', 'key_name', 0)

Версия:

  • UWSGI: 2.0.5.1
  • Колба: 0.11.1
  • redis-py: 2.10.5
  • Redis: 2.8.24

Итак, вывод:

  1. Почему количество клиентов AWS низкое, если подключено 14 серверов, каждый с 4 процессами, и каждый из них открывает соединение с 8 различными базами данных в кластере redis
  2. Что заставляет время отклика запросов подниматься?
  3. Буду признателен за любые рекомендации относительно производительности ElastiCache и / или uWSGI при большой нагрузке.

1 ответ

Короткий ответ

Так что, если я правильно понял, в моем случае проблема была не в запросах Elasticache, а в использовании памяти uWSGI.

Длинный ответ

Я установил uwsgitop с этой настройкой:

### Stats
### ---
### disabled by default
### To see stats run: uwsgitop /tmp/uwsgi_stats.socket
### uwsgitop must be install (pip install uwsgitop)
stats = /tmp/uwsgi_stats.socket

это выставит uwsgi статистику uwsgitop.

Затем я использовал http://loader.io/ для стресс-тестирования приложения со скоростью 350-500 запросов в секунду.

Что я обнаружил в своей предыдущей конфигурации, так это то, что работники uWSGI продолжали увеличивать объем используемой памяти, пока память не захлебнулась, а затем процессор не заработал. новые работники, которым нужно было повторно порождать, также требовали использования процессора, который вызывал некоторую перегрузку на серверах, что приводило к тому, что nginx блокировал время ожидания и закрывал эти соединения.

Поэтому я провел некоторое исследование и модификацию конфигурации, пока мне не удалось получить приведенную ниже настройку, которая в настоящее время управляет ~650rps для каждого экземпляра с временем отклика ~13 мс, что очень хорошо для меня.

* Мое приложение использовало (по-прежнему использует некоторые) файлы с маринованными дисками, некоторые из них были тяжелыми для загрузки - я уменьшил зависимость от диска до минимума *

Для тех, кто может увидеть это в будущем - если вам нужны быстрые ответы - асинхронизируйте все, что можете. например, используйте celery+rabbitmq для любых запросов к базам данных, если это возможно

Конфигурация uWSGI:

listen = 128
processes = 8
threads = 2
max-requests = 10000
reload-on-as = 4095
reload-mercy = 5
#reload-on-rss = 1024
limit-as = 8192
cpu-affinity = 3
thread-stacksize = 1024
max-fd = 250000
buffer-size = 30000
thunder-lock = true
vacuum = true
enable-threads = true
no-orphans = true
die-on-term = true

NGINX соответствующие части:

user nginx;
worker_processes 4;
worker_rlimit_nofile 20000;
thread_pool my_threads threads=16;
pid /run/nginx.pid;

events {
    accept_mutex off;
    # determines how much clients will be served per worker
    # max clients = worker_connections * worker_processes
    # max clients is also limited by the number of socket connections available on the system (~64k)
    worker_connections 19000;

    # optmized to serve many clients with each thread, essential for linux -- for testing environment
    use epoll;

    # accept as many connections as possible, may flood worker connections if set too low -- for testing environment
    multi_accept on;
}

http {
    ...
    aio                     threads;
    sendfile                on;
    sendfile_max_chunk      512k;
    tcp_nopush              on;
    tcp_nodelay             on;
    keepalive_timeout       5 5;
    keepalive_requests      0;
    types_hash_max_size     2048;
    send_timeout            15;
    ...
}

Надеюсь, поможет!

Другие вопросы по тегам