Плохая работа Django/uwsgi - программирование
Подтвердить что ты не робот

Плохая работа Django/uwsgi

Я запускаю приложение django с nginx и uwsgi. Вот как я запускаю uwsgi:

sudo uwsgi -b 25000 --chdir=/www/python/apps/pyapp --module=wsgi:application --env DJANGO_SETTINGS_MODULE=settings --socket=/tmp/pyapp.socket --cheaper=8 --processes=16  --harakiri=10  --max-requests=5000  --vacuum --master --pidfile=/tmp/pyapp-master.pid --uid=220 --gid=499

& Конфигурации nginx:

server {
    listen 80;
    server_name test.com

    root /www/python/apps/pyapp/;

    access_log /var/log/nginx/test.com.access.log;
    error_log /var/log/nginx/test.com.error.log;

    # https://docs.djangoproject.com/en/dev/howto/static-files/#serving-static-files-in-production
    location /static/ {
        alias /www/python/apps/pyapp/static/;
        expires 30d;
    }

    location /media/ {
        alias /www/python/apps/pyapp/media/;
        expires 30d;
    }

    location / {
        uwsgi_pass unix:///tmp/pyapp.socket;
        include uwsgi_params;
        proxy_read_timeout 120;
    }

    # what to serve if upstream is not available or crashes
    #error_page 500 502 503 504 /media/50x.html;
}

Вот и проблема. Когда вы делаете "ab" (ApacheBenchmark) на сервере, я получаю следующие результаты:

nginx версия: nginx версия: nginx/1.2.6

версия uwsgi: 1.4.5

Server Software:        nginx/1.0.15
Server Hostname:        pycms.com
Server Port:            80

Document Path:          /api/nodes/mostviewed/8/?format=json
Document Length:        8696 bytes

Concurrency Level:      100
Time taken for tests:   41.232 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      8866000 bytes
HTML transferred:       8696000 bytes
Requests per second:    24.25 [#/sec] (mean)
Time per request:       4123.216 [ms] (mean)
Time per request:       41.232 [ms] (mean, across all concurrent requests)
Transfer rate:          209.99 [Kbytes/sec] received

При работе на уровне 500 concurrency

oncurrency Level:      500
Time taken for tests:   2.175 seconds
Complete requests:      1000
Failed requests:        50
   (Connect: 0, Receive: 0, Length: 50, Exceptions: 0)
Write errors:           0
Non-2xx responses:      950
Total transferred:      629200 bytes
HTML transferred:       476300 bytes
Requests per second:    459.81 [#/sec] (mean)
Time per request:       1087.416 [ms] (mean)
Time per request:       2.175 [ms] (mean, across all concurrent requests)
Transfer rate:          282.53 [Kbytes/sec] received

Как вы можете видеть... все запросы на сервере терпят неудачу с ошибками таймаута или "Клиент преждевременно отключен" или:

writev(): Broken pipe [proto/uwsgi.c line 124] during GET /api/nodes/mostviewed/9/?format=json

Вот немного больше о моем приложении: В принципе, это набор моделей, которые отражают таблицы MySQL, которые содержат весь контент. В интерфейсе у меня есть django-rest-framework, который обслуживает клиентский контент json.

Я установил django-profiling и django debug toolbar, чтобы узнать, что происходит. О django-профилировании здесь, что я получаю при запуске одного запроса:

Instance wide RAM usage

Partition of a set of 147315 objects. Total size = 20779408 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  63960  43  5726288  28   5726288  28 str
     1  36887  25  3131112  15   8857400  43 tuple
     2   2495   2  1500392   7  10357792  50 dict (no owner)
     3    615   0  1397160   7  11754952  57 dict of module
     4   1371   1  1236432   6  12991384  63 type
     5   9974   7  1196880   6  14188264  68 function
     6   8974   6  1076880   5  15265144  73 types.CodeType
     7   1371   1  1014408   5  16279552  78 dict of type
     8   2684   2   340640   2  16620192  80 list
     9    382   0   328912   2  16949104  82 dict of class
<607 more rows. Type e.g. '_.more' to view.>



CPU Time for this request

         11068 function calls (10158 primitive calls) in 0.064 CPU seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.064    0.064 /usr/lib/python2.6/site-packages/django/views/generic/base.py:44(view)
        1    0.000    0.000    0.064    0.064 /usr/lib/python2.6/site-packages/django/views/decorators/csrf.py:76(wrapped_view)
        1    0.000    0.000    0.064    0.064 /usr/lib/python2.6/site-packages/rest_framework/views.py:359(dispatch)
        1    0.000    0.000    0.064    0.064 /usr/lib/python2.6/site-packages/rest_framework/generics.py:144(get)
        1    0.000    0.000    0.064    0.064 /usr/lib/python2.6/site-packages/rest_framework/mixins.py:46(list)
        1    0.000    0.000    0.038    0.038 /usr/lib/python2.6/site-packages/rest_framework/serializers.py:348(data)
     21/1    0.000    0.000    0.038    0.038 /usr/lib/python2.6/site-packages/rest_framework/serializers.py:273(to_native)
     21/1    0.000    0.000    0.038    0.038 /usr/lib/python2.6/site-packages/rest_framework/serializers.py:190(convert_object)
     11/1    0.000    0.000    0.036    0.036 /usr/lib/python2.6/site-packages/rest_framework/serializers.py:303(field_to_native)
    13/11    0.000    0.000    0.033    0.003 /usr/lib/python2.6/site-packages/django/db/models/query.py:92(__iter__)
      3/1    0.000    0.000    0.033    0.033 /usr/lib/python2.6/site-packages/django/db/models/query.py:77(__len__)
        4    0.000    0.000    0.030    0.008 /usr/lib/python2.6/site-packages/django/db/models/sql/compiler.py:794(execute_sql)
        1    0.000    0.000    0.021    0.021 /usr/lib/python2.6/site-packages/django/views/generic/list.py:33(paginate_queryset)
        1    0.000    0.000    0.021    0.021 /usr/lib/python2.6/site-packages/django/core/paginator.py:35(page)
        1    0.000    0.000    0.020    0.020 /usr/lib/python2.6/site-packages/django/core/paginator.py:20(validate_number)
        3    0.000    0.000    0.020    0.007 /usr/lib/python2.6/site-packages/django/core/paginator.py:57(_get_num_pages)
        4    0.000    0.000    0.020    0.005 /usr/lib/python2.6/site-packages/django/core/paginator.py:44(_get_count)
        1    0.000    0.000    0.020    0.020 /usr/lib/python2.6/site-packages/django/db/models/query.py:340(count)
        1    0.000    0.000    0.020    0.020 /usr/lib/python2.6/site-packages/django/db/models/sql/query.py:394(get_count)
        1    0.000    0.000    0.020    0.020 /usr/lib/python2.6/site-packages/django/db/models/query.py:568(_prefetch_related_objects)
        1    0.000    0.000    0.020    0.020 /usr/lib/python2.6/site-packages/django/db/models/query.py:1596(prefetch_related_objects)
        4    0.000    0.000    0.020    0.005 /usr/lib/python2.6/site-packages/django/db/backends/util.py:36(execute)
        1    0.000    0.000    0.020    0.020 /usr/lib/python2.6/site-packages/django/db/models/sql/query.py:340(get_aggregation)
        5    0.000    0.000    0.020    0.004 /usr/lib64/python2.6/site-packages/MySQLdb/cursors.py:136(execute)
        2    0.000    0.000    0.020    0.010 /usr/lib/python2.6/site-packages/django/db/models/query.py:1748(prefetch_one_level)
        4    0.000    0.000    0.020    0.005 /usr/lib/python2.6/site-packages/django/db/backends/mysql/base.py:112(execute)
        5    0.000    0.000    0.019    0.004 /usr/lib64/python2.6/site-packages/MySQLdb/cursors.py:316(_query)
       60    0.000    0.000    0.018    0.000 /usr/lib/python2.6/site-packages/django/db/models/query.py:231(iterator)
        5    0.012    0.002    0.015    0.003 /usr/lib64/python2.6/site-packages/MySQLdb/cursors.py:278(_do_query)
       60    0.000    0.000    0.013    0.000 /usr/lib/python2.6/site-packages/django/db/models/sql/compiler.py:751(results_iter)
       30    0.000    0.000    0.010    0.000 /usr/lib/python2.6/site-packages/django/db/models/manager.py:115(all)
       50    0.000    0.000    0.009    0.000 /usr/lib/python2.6/site-packages/django/db/models/query.py:870(_clone)
       51    0.001    0.000    0.009    0.000 /usr/lib/python2.6/site-packages/django/db/models/sql/query.py:235(clone)
        4    0.000    0.000    0.009    0.002 /usr/lib/python2.6/site-packages/django/db/backends/__init__.py:302(cursor)
        4    0.000    0.000    0.008    0.002 /usr/lib/python2.6/site-packages/django/db/backends/mysql/base.py:361(_cursor)
        1    0.000    0.000    0.008    0.008 /usr/lib64/python2.6/site-packages/MySQLdb/__init__.py:78(Connect)
  910/208    0.003    0.000    0.008    0.000 /usr/lib64/python2.6/copy.py:144(deepcopy)
       22    0.000    0.000    0.007    0.000 /usr/lib/python2.6/site-packages/django/db/models/query.py:619(filter)
       22    0.000    0.000    0.007    0.000 /usr/lib/python2.6/site-packages/django/db/models/query.py:633(_filter_or_exclude)
       20    0.000    0.000    0.005    0.000 /usr/lib/python2.6/site-packages/django/db/models/fields/related.py:560(get_query_set)
        1    0.000    0.000    0.005    0.005 /usr/lib64/python2.6/site-packages/MySQLdb/connections.py:8()

.. и т.д.

Однако панель инструментов django-debug показывает следующее:

Resource Usage
Resource    Value
User CPU time   149.977 msec
System CPU time 119.982 msec
Total CPU time  269.959 msec
Elapsed time    326.291 msec
Context switches    11 voluntary, 40 involuntary

and 5 queries in 27.1 ms

Проблема заключается в том, что "верхний" показывает, что среднее значение загрузки растет быстро, а тест apache, который я запускал как на локальном сервере, так и на удаленной машине в сети, показывает, что я не обслуживаю много запросов/секунду. В чем проблема? это насколько я мог достигнуть при профилировании кода, поэтому было бы полезно, если кто-то может указать, что я здесь делаю.

Изменить (23/02/2013): добавив больше деталей на основе ответа Эндрю Алькока: Вопросы, требующие моего внимания/ответа, (3) (3) Я выполнил "показать глобальные переменные" в MySQL и выяснил, что для конфигураций MySQL было 151 для параметра max_connections, которого более чем достаточно для обслуживания рабочих, которые я запускаю для uwsgi.

(3) (4) (2) Единственный запрос, который я профилирую, является самым тяжелым. Он выполняет 4 запроса в соответствии с панелью django-debug-toolbar. Случается, что все запросы выполняются в: 3,71, 2,83, 0,88, 4,84 мс соответственно.

(4) Здесь вы имеете в виду пейджинг памяти? если да, то как я мог сказать?

(5) На 16 рабочих, 100 concurrency ставка, 1000 запросов средняя загрузка нагрузки до ~ 12 Я провел тесты на разных числах работников (уровень concurrency равен 100):

  • 1 рабочий, средняя загрузка ~ 1.85, 19 reqs/second, Время в заказе: 5229.520, 0 не-2xx
  • 2 рабочий, средняя загрузка ~ 1.5, 19 reqs/second, Время за запрос: 516.520, 0 не-2xx
  • 4 работника, средняя загрузка ~ 3, 16 reqs/second, Время за запрос: 5929.921, 0 не-2xx
  • 8 рабочих, средняя загрузка ~ 5, 18 reqs/second, Время за запрос: 5301.458, 0 не-2xx
  • 16 рабочих, средняя загрузка ~ 19, 15 reqs/second, Время в заказе: 6384.720, 0 не-2xx

Как вы можете видеть, чем больше у нас рабочих, тем больше нагрузки у нас на системе. Я вижу в журнале dawon uwsgi время отклика в миллисекундах увеличивается, когда я увеличиваю число рабочих.

На 16 рабочих, выполняющих 500 запросов concurrency, uwsgi запускает ошибки:

 writev(): Broken pipe [proto/uwsgi.c line 124] 

Нагрузка увеличивается до ~ 10. и тесты не занимают много времени, потому что не-2xx ответы 923 из 1000, поэтому ответ здесь довольно быстрый, поскольку он почти пуст. Это также ответ на ваш вопрос № 4 в сводке.

Предполагая, что то, с чем я столкнулся, - это латентность ОС на основе ввода-вывода и сетей, каково рекомендуемое действие для ее масштабирования? новое оборудование? более крупный сервер?

Спасибо

4b9b3361

Ответ 1

РЕДАКТИРОВАТЬ 1. Посмотрите, что у вас есть 1 виртуальное ядро, добавив комментарий во всех релевантных точках.

РЕДАКТИРОВАТЬ 2 Дополнительная информация от Maverick, поэтому я устраняю идеи, исключающие и разрабатывающие подтвержденные проблемы.

РЕДАКТИРОВАТЬ 3 Заполняется более подробная информация о параметрах очереди uwsgi и параметрах масштабирования. Улучшенная грамматика.

EDIT 4 Обновления от Maverick и незначительные улучшения

Комментарии слишком малы, вот некоторые мысли:

  • Среднее значение нагрузки - это, в основном, сколько процессов работает или ждет внимания ЦП. Для совершенно загруженной системы с 1 центральным процессором среднее значение нагрузки должно быть 1,0; для четырехъядерной системы это должно быть 4.0. В тот момент, когда вы запускаете веб-тест, потоковые ракеты, и у вас много процессов, ожидающих процессор. Если средняя загрузка не превышает количество ядер ЦП с существенным запасом, это не проблема
  • Первое значение "Время на запрос" 4s коррелирует с длиной очереди запросов - 1000 запросов, сбрасываемых на Django, почти мгновенно и заняли в среднем 4 секунды для обслуживания, около 3.4 из которых ожидали в очереди. Это связано с очень сильным несоответствием между количеством запросов (100) и количеством процессоров (16), в результате чего 84 из запросов ожидали процессор в любой момент.
  • Запуск при concurrency из 100, тесты занимают 41 секунду при 24 запросах в секунду. У вас есть 16 процессов (потоков), поэтому каждый запрос обрабатывается около 700 мс. Учитывая ваш тип транзакции, это длительное время для каждого запроса. Это может быть связано с тем, что:

    • Стоимость процессора для каждого запроса высока в Django (что маловероятно, учитывая низкое значение ЦП с панели инструментов отладки)
    • ОС - это многозадачная задача (особенно, если средняя нагрузка выше 4-8), а латентность сводится к слишком большому количеству процессов.
    • Недостаточно соединений DB, обслуживающих 16 процессов, поэтому процессы ждут, когда они будут доступны. У вас есть хотя бы одно соединение для каждого процесса?
    • Существует значительная латентность вокруг БД, либо:

      • Десятки небольших запросов, каждый из которых принимает, скажем, 10 мс, большинство из которых являются сетевыми накладными расходами. Если да, можете ли вы ввести кэширование или уменьшить SQL-запросы на меньшее число. ИлиЗабастовкa >
      • Одна или несколько запросов принимают 100 мс. Чтобы проверить это, выполните профилирование в БД. Если это так, вам нужно оптимизировать этот запрос.
  • Распределение между ценой центрального процессора и пользователя в системе необычно велико, хотя общий процессор низкий. Это означает, что большая часть работы в Django связана с ядром, например, с сетью или диском. В этом случае это могут быть сетевые затраты (например, получение и отправка HTTP-запросов, а также получение и отправка запросов в БД). Иногда это будет высоким из-за пейджинга. Если нет пейджинга, то вам, вероятно, не придется об этом беспокоиться.

  • Вы настроили процессы на 16, но имеете средний уровень нагрузки (насколько высоко вы не указали). В идеале вы всегда должны иметь хотя бы один процесс, ожидающий процессор (так что процессоры не вращаются без движения). Процессы здесь не связаны с CPU, но имеют значительную задержку, поэтому вам нужно больше процессов, чем ядер. На сколько больше? Попробуйте запустить uwsgi с различным количеством процессоров (1, 2, 4, 8, 12, 16, 24 и т.д.), Пока у вас не будет лучшей пропускной способности. Если вы измените латентность среднего процесса, вам нужно будет снова настроить его.
  • Уровень 500 concurrency определенно является проблемой , но является ли это клиентом или сервером? В отчете говорится, что 50 (из 100) имели неправильную длину контента, что подразумевает проблему с сервером. Кажется, что не-2xx указывает там. Возможно ли захватить ответы не-2xx для отладки - трассировки стека или конкретное сообщение об ошибке было бы невероятно полезно (EDIT) и вызвано очередью запросов uwsgi, для которой установлено значение по умолчанию 100.

Итак, вкратце:

enter image description here

  • Django кажется прекрасным
  • Несоответствие между concurrency теста нагрузки (100 или 500) по сравнению с процессами (16): вы вызываете слишком много параллельных запросов в систему для количества обрабатываемых процессов. Когда вы превысите количество процессов, все, что произойдет, это то, что вы удлиняете очередь HTTP-запросов на веб-сервере.
  • Существует большая задержка, поэтому либо

    • Несоответствие между процессами (16) и ядрами ЦП (1): если среднее значение нагрузки равно > 3, то, вероятно, слишком много процессов. Повторите попытку с меньшим количеством процессов.

      • Среднее значение нагрузки > 2 → попробуйте 8 процессов
      • Среднее значение нагрузки > 4 → попробуйте 4 процесса
      • Среднее значение нагрузки > 8 → попробуйте 2 процесса
    • Если среднее значение нагрузки < 3, оно может быть в БД, поэтому необходимо профилировать БД, чтобы увидеть, есть ли множество небольших запросов (аддитивно вызывающих задержку) или один или два оператора SQL проблема

  • Не записывая неудавшийся ответ, я не могу сказать о неудачах при 500 concurrency

Разработка идей

Средние значения нагрузки > 10 на одной порошковой машине действительно неприятны и (как вы заметили) приводят к большому переключению задач и обычному медленному поведению. Я лично не помню, как машина со средним значением нагрузки 19 (что у вас есть для 16 процессов) - поздравления за то, что она так высока;)

Производительность базы данных велика, поэтому я бы сразу дал понять, что сейчас.

Пейджинг. Чтобы ответить на вопрос о том, как увидеть пейджинг, вы можете обнаружить подкачку ОС несколькими способами. Например, в верхней части заголовок имеет вставки и выходы (см. Последнюю строку):

Processes: 170 total, 3 running, 4 stuck, 163 sleeping, 927 threads                                                                                                        15:06:31
Load Avg: 0.90, 1.19, 1.94  CPU usage: 1.37% user, 2.97% sys, 95.65% idle  SharedLibs: 144M resident, 0B data, 24M linkedit.
MemRegions: 31726 total, 2541M resident, 120M private, 817M shared. PhysMem: 1420M wired, 3548M active, 1703M inactive, 6671M used, 1514M free.
VM: 392G vsize, 1286M framework vsize, 1534241(0) pageins, 0(0) pageouts. Networks: packets: 789684/288M in, 912863/482M out. Disks: 739807/15G read, 996745/24G written.

Количество процессов. В вашей текущей конфигурации количество процессов слишком велико. Масштабировать количество процессов до 2. Мы можем добавить это значение позже, в зависимости от изменения дополнительной нагрузки с этого сервера.

Расположение теста Apache Benchmark. Среднее значение нагрузки 1,85 для одного процесса подсказывает мне, что вы используете генератор нагрузки на том же компьютере, что и uwsgi, - это правильно?

Если это так, вам действительно нужно запустить это с другой машины, иначе тестовые прогоны не будут представлять фактическую нагрузку - вы берете память и процессор из веб-процессов для использования в генераторе нагрузки. Кроме того, генераторы 100 или 500 генератора нагрузки обычно усиливают ваш сервер таким образом, который не происходит в реальной жизни. На самом деле это может быть причиной того, что весь тест не удался.

Расположение базы данных. Среднее значение нагрузки для одного процесса также предполагает, что вы используете БД на той же машине, что и веб-процессы, - это правильно?

Если я правильно отношусь к БД, то первым и лучшим способом начать масштабирование является перемещение БД на другую машину. Мы делаем это по двум причинам:

  • Серверу БД требуется другой профиль оборудования из обработки node:

    • Диск: БД требует много быстрого, резервного, резервного диска, а для обработки node нужен только базовый диск
    • Процессор: для обработки node нужен самый быстрый процессор, который вы можете себе позволить, в то время как машина БД может обойтись без (часто ее производительность защищена на диске и ОЗУ).
    • ОЗУ: машине БД обычно требуется как можно больше ОЗУ (и самый быстрый БД имеет все свои данные в ОЗУ), тогда как для многих узлов обработки требуется намного меньше (для вас требуется около 20 МБ на процесс - очень мало
    • Масштабирование: Атомные БД наилучшим образом масштабируются благодаря наличию машин-монстров со многими процессорами, тогда как веб-уровень (не имеющий состояния) может масштабироваться путем подключения многих идентичных маленьких коробок.
  • Сродство к процессору: лучше, чтобы процессор имел среднюю нагрузку 1,0 и процессы имели сродство к одному ядру. Это максимизирует использование кэша процессора и минимизирует накладные расходы на переключение задач. Разделив DB и обрабатывающие узлы, вы выполняете эту привязку в HW.

500 concurrency с исключениями. Очередь запросов на диаграмме выше не более 100 - если uwsgi получает запрос, когда очередь заполнена, запрос отклоняется с ошибкой 5xx. Я думаю, что это происходило в вашем тестовом эксперименте 500 concurrency - в основном очередь заполнялась первыми 100 или около того потоками, затем остальные 400 потоков выдавали оставшиеся 900 запросов и получали немедленные ошибки 5xx.

Чтобы обрабатывать 500 запросов в секунду, вам необходимо обеспечить две вещи:

  • Размер очереди запросов настроен для обработки пакета: используйте аргумент --listen для uwsgi
  • Система может обрабатывать пропускную способность более 500 запросов в секунду, если 500 является нормальным условием, или бит ниже, если 500 является пиком. См. Примечания масштабирования ниже.

Я предполагаю, что uwsgi имеет очередь, установленную на меньшее число, чтобы лучше справляться с DDoS-атаками; если они находятся под огромной нагрузкой, большинство запросов немедленно терпят неудачу с почти никакой обработкой, позволяющей блоку в целом по-прежнему реагировать на администраторов.

Общие рекомендации по масштабированию системы

Самое важное для вас, вероятно, для максимизировать пропускную способность. Еще одна возможная необходимость минимизировать время отклика, но я не буду обсуждать это здесь. При максимальной пропускной способности вы пытаетесь максимизировать систему, а не отдельные компоненты; некоторые локальные сокращения могут улучшить общую пропускную способность системы (например, изменение, которое происходит для добавления задержки в веб-уровне, чтобы повысить производительность БД, является чистым коэффициентом усиления).

В зависимости от специфики:

  • Переместить БД на отдельный компьютер. После этого профилируйте БД во время теста нагрузки, запустив top и ваш любимый инструмент мониторинга MySQL. У вас должен быть профиль. Перемещение БД на отдельный компьютер приведет к некоторой дополнительной задержке (несколько мс) для каждого запроса, поэтому ожидайте немного увеличить количество процессов на уровне веб-страниц, чтобы поддерживать такую ​​же пропускную способность.
  • Убедитесь, что очередь запросов uswgi достаточно велика, чтобы обрабатывать пакет трафика с использованием аргумента --listen. Это должно быть в несколько раз максимальным запросом в секунду, который может обрабатывать ваша система.
  • На уровне web/app: Баланс количества процессов с количеством ядер процессора и присущей латентности в этом процессе. Слишком много процессов замедляет производительность, слишком мало означает, что вы никогда не будете полностью использовать системные ресурсы. Не существует фиксированной точки балансировки, так как каждое приложение и шаблон использования различны, поэтому сравнивайте и настраивайте. В качестве руководства используйте задержку процессов, если каждая задача имеет:

    • 0% латентности, тогда вам нужно 1 процесс на ядро ​​
    • 50% латентность (т.е. время процессора составляет половину фактического времени), тогда вам нужно 2 процесса на ядро ​​
    • Задержка 67%, тогда вам нужно 3 процесса на ядро.
  • Проверяйте top во время теста, чтобы убедиться, что вы используете загрузку процессора выше 90% (для каждого ядра), и средняя нагрузка немного выше 1.0. Если среднее значение нагрузки выше, отмените процесс. Если все будет хорошо, в какой-то момент вы не сможете достичь этой цели, и теперь БД может стать узким местом

  • В какой-то момент вам понадобится больше энергии в веб-уровне. Вы можете либо добавить к процессору больше процессора (относительно легко), и поэтому добавить больше процессов, и/или, которые вы можете добавить в большее количество узлов обработки (горизонтальная масштабируемость). Последнее может быть достигнуто в uwsgi, используя описанный метод здесь Łukasz Mierzwa

Ответ 2

Пожалуйста, запустите тесты намного дольше минуты (по крайней мере, 5-10). Вы действительно не получите много информации из такого короткого теста. И используйте углеродный плагин uWSGI, чтобы вывести статистику на углеродный/графитовый сервер (вам нужно будет иметь один). У вас будет гораздо больше информации для отладки.

Когда вы отправляете 500 одновременных запросов к своему приложению и не может обрабатывать такую ​​нагрузку, очередь прослушивания на каждом бэкэнд будет заполнена довольно быстро (это 100 запросов по умолчанию). Возможно, вы захотите увеличить это, но если работники могут 't обрабатывает запросы, что быстрая и ожидающая очередь (также известная как backlog) заполнена, сетевой стек linux отбросит запрос, и вы начнете получать ошибки.

В вашем первом тесте указано, что вы можете обрабатывать одиночный запрос в ~ 42 мс, поэтому один рабочий может обрабатывать не более 1000 мс /42 мс = ~ 23 запроса в секунду (если db и другие части приложения не замедлялись как concurrency увеличивается). Таким образом, чтобы обрабатывать 500 одновременных запросов, вам понадобится как минимум 500/23 = 21 работник (но на самом деле я бы сказал, по крайней мере, 40). У вас всего 16, неудивительно, что это ломается под такой нагрузкой.

EDIT: у меня смешанная скорость с concurrency - по меньшей мере 21 сотрудник позволит вам обрабатывать 500 запросов в секунду, а не 500 одновременных запросов. Если вы действительно хотите обрабатывать 500 одновременных запросов, то вам потребуется всего 500 сотрудников. Если вы не запустите свое приложение в асинхронном режиме, проверьте раздел "Gevent" в документах uWSGI.

PS. uWSGI поставляется с большим балансировщиком нагрузки с автоконфигурацией бэкэнда (читать документы в разделе "Сервер подписки" и "FastRouter" ). Вы можете настроить его таким образом, чтобы вы могли при необходимости подключать новые бэкэнд, вы просто начинаете работу с новыми node, и они будут подписаны на FastRouter и начнут получать запросы. Это лучший способ масштабирования по горизонтали. И с помощью backend на AWS. Вы можете автоматизировать это, чтобы новые серверы запускались быстро, когда это было необходимо.

Ответ 3

Добавление большего количества работников и получение меньшего количества r/s означает, что ваш запрос "является чистым ЦП", и нет ожиданий IO, которые другой рабочий может использовать для обслуживания другого запроса.

Если вы хотите масштабировать, вам нужно будет использовать другой сервер с большим количеством (или быстрее) процессоров.

Однако это синтетический тест, количество r/s, которое вы получаете, является верхней границей для точного запроса, который вы тестируете, как только на производстве есть еще много переменных, которые могут повлиять на производительность.