坏的Django / uwsgi性能(Bad Django / uwsgi performance)

2019-08-17 01:16发布

我正在运行nginx的与uwsgi一个Django应用程序。 以下是我运行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并发级别运行

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的架构,用于JSON内容到客户端。

我已经安装Django的分析和Django的调试工具栏,看看怎么回事。 在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的调试工具栏显示以下内容:

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)单个请求我正在仿形是最重的一个。 它根据Django的调试工具栏里执行4个查询。 什么情况是,所有的查询中运行:3.71,2.83,0.88,4.84毫秒分别。

(4)在这里,我们指的是内存分页? 如果是这样,我怎么会知道?

(5)在16个工,100并发率,1000个请求平均负载上升到〜12我跑(并发级别是100)不同数量的工人的测试:

  1. 1名工人,平均负载〜1.85,19个请求数/秒,每个请求时间:5229.520,0非2xx
  2. 2工人,平均负载〜1.5,19个请求数/秒,每个请求时间:516.520,0非2xx
  3. 4工人,平均负载〜3,16请求数/秒,每个请求时间:5929.921,0非2xx
  4. 8工人,平均负载〜5,18个请求数/秒,每个请求时间:5301.458,0非2xx
  5. 16工人,平均负载〜19,15个请求数/秒,每个请求时间:6384.720,0非2xx

正如你可以看到,更多的工人,我们有,更多的负载,我们在系统上。 我可以在uwsgi的守护程序日志看到,当我增加工人的数量,单位为毫秒的响应时间增加。

在16名工人,跑500并发级别请求uwsgi启动洛错误:

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

负载上升到约10为好。 而测试不花太多时间,因为非2xx应答是923出的1000这就是为什么这里的反应相当快,因为​​它几乎是空的。 这也是在总结你的观点#4的答复。

假设我现在面临这里是基于I / O和网络操作系统的等待时间,什么是建议操作规模这个吗? 新的硬件? 更大的服务器?

谢谢

Answer 1:

编辑1可以看出你有1个虚拟核心的评论,在所有的初步认识点通过添加评论

编辑2从小牛更多的信息,所以我消除思想排除和发展证实的问题。

编辑3填充了约uwsgi请求队列和缩放选项的详细信息。 改进的语法。

从小牛和小的改进EDIT 4个更新

注释是太小了,所以这里有一些想法:

  1. 平均负载基本上是许多过程是如何运行或等待CPU的关注。 对于具有1个CPU核心完全负载的系统中,负载平均应为1.0; 对于4核心系统,它应该是4.0。 那一刻你运行Web测试,丝扣火箭和你有很多等待CPU的进程。 除非平均负荷超过CPU内核由显著保证金的数量,这是不是一个问题
  2. 4S的首个“每个请求的时间的价值关联请求队列的长度 - 倾倒的Django几乎在瞬间,并就平均4S服务,约3.4s其中在队列中等待了1000个请求。 这是由于请求(100)的数量与处理器(16)使所述请求84在任何一个时刻在等待的处理器的数量之间的非常重的不匹配。
  3. 运行在100并发性,在测试需要在24个请求/秒41秒。 你有16个进程(线程),所以每个请求被处理大约700毫秒。 鉴于您的交易类型,即每个请求很长一段时间。 这可能是因为:

    1. 每个请求的CPU成本高在Django(这是非常不可能给出从调试工具栏的低CPU值)
    2. 该操作系统是任务切换很多(特别是如果平均负载高于4-8),并等待时间是纯粹下降到具有太多进程。
    3. 有没有服务于16个进程使进程正在等待有一个来足够的可用数据库连接。 你有每个进程中可用的至少一个连接?
    4. 周围有DB 相当延迟,或者

      1. 小请求数万每次服用,比方说,10ms的,其中大部分是网络开销。 如果是这样,你可以将高速缓存或减少SQL调用到一个较小的数字。 要么
      2. 一个或一对夫妇的请求正在100米的毫秒。 要进行检查,运行数据库分析。 如果是这样,你需要优化该请求。
  4. 系统和用户CPU成本之间的分裂是在系统异常的高,虽然总的CPU低。 这意味着,大多数在Django的工作是内核有关,如网络或磁盘。 在这种情况下,它可能是网络成本(如接收和发送HTTP请求和接收和发送请求到DB)。 有时,这将是很高的,因为分页的。 如果没有进行分页,那么你可能不必担心这个的。

  5. 你必须在16集的过程,但具有较高的平均负载(你怎么不高的状态)。 理想情况下,你应该始终有至少一个进程等待CPU(因此CPU不会坐视旋转)。 进程似乎这里就不CPU的约束,但是有显著的等待时间,所以你需要比内核更多的进程。 还有多少? 尝试运行具有不同数量的处理器(1,2,4,8,12,16,24,等等)的uwsgi直到你有最佳的吞吐量。 如果你改变平均过程的延迟,则需要再次调整此。
  6. 500并发级别肯定是有问题,但它是客户端还是服务器? 报告说,50(满分100分)有不正确的内容长度,这意味着一台服务器的问题。 非2xx的似乎也有道理。 是否有可能捕获调试非2xx应答-堆栈跟踪或特定的错误信息会非常有用 (编辑),并通过与它运行uwsgi请求队列造成的100默认值。

因此,在总结:

  1. Django的似乎罚款
  2. 负载测试(100或500)的并发性之间的错配与工艺(16):你推太多的并发请求到系统中的进程来处理的数量。 一旦你上面的进程数,都将发生的是,你会延长HTTP请求队列中的Web服务器
  3. 有一个大的延迟,所以无论是

    1. 处理(16)和CPU内核之间的失配(1):如果负载平均为> 3,则它可能是过多的进程。 与进程的一个较小数目再试一次

      1. 平均负载> 2 - >尝试8个处理
      2. 平均负载> 4 - >试试4个进程
      3. 平均负载> 8 - >尝试2个流程
    2. 如果平均负载<3,也可能是在DB,所以配置文件中的DB,看看是否有小的请求(相加造成的等待时间),或者一个或两个SQL语句的负载问题

  4. 如果没有捕获失败的响应,有没有什么我可以说在500并发失败

发展思路

你的平均负荷> 10单芯机器是真的讨厌(正如你观察)导致了大量的任务切换和一般的缓慢行为。 我个人不记得看到一台机器的19平均负载(你有16个进程) - 恭喜你得到它这么高;)

该数据库的性能是伟大的,所以我给的是一个全清楚现在。

分页 :要回答你如何看分页的问题-你可以发现在几个方面OS分页。 例如,在上面,头有页面来龙去脉(见最后一行):

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。 以后我们可能会带来这个值时,根据换挡其他负荷关闭此服务器。

阿帕奇基准的位置 :平均负载的1.85一个过程建议,我认为你是在同一台机器上运行的负载生成器为uwsgi -是正确的?

如果是这样,你真的需要从另一台机器上运行这个本来在测试运行并不代表实际负载的 - 你正在做的内存和CPU从网工艺在负载生成器使用。 此外,负载生成器的100条或500线通常会强调你在不发生在现实生活的方式服务器。 事实上,这可能是整个测试失败的原因。

DB的位置 :平均负载为一个过程还建议你在同一台机器上运行的数据库作为web程序-这是正确的?

如果我是正确的关于DB,然后启动缩放第一,最好的办法是到数据库移动到另一台机器。 我们为一对夫妇的理由这样做:

  1. 甲DB服务器需要从处理节点的不同的硬件配置文件:

    1. 磁盘:DB需要大量快速,冗余,备份盘,以及处理节点需要只是一个基本的磁盘
    2. CPU:一个处理节点需要你能负担得起而DB机往往能做到无最快的CPU(通常它的性能而被选通在磁盘和RAM)
    3. RAM:一个DB机一般需要尽可能多的RAM越好(和最快的DB有其RAM中的所有数据),而许多处理节点需要少得多的(你大概需要每个进程20MB -非常小
    4. 缩放: 原子 DB的规模最好具有多个CPU,而web层(没有状态)怪兽机可以通过许多相同的小Boxen有结垢堵塞。
  2. CPU亲和力:这是更好为CPU具有1.0和处理的负荷平均具有亲和性的单核。 这样做最大限度地利用CPU缓存,最大限度地减少任务切换开销。 通过分离DB和处理节点,你在执行这个HW亲和力。

高于500并发有例外的请求队列在图中为至多100 -如果uwsgi接收到一个请求时,队列是满的,请求与5xx错误拒绝。 我认为这是在你的500并发负载测试发生了 - 基本上填满了第一个100级左右的线程队列中,那么其他400个线程发出剩余的900个请求,并收到了立竿见影的5XX错误。

为了处理每秒500个请求,你需要确保两两件事:

  1. 请求队列大小被配置成处理所述脉冲串:使用--listen参数uwsgi
  2. 该系统可以处理每秒超过500请求的吞吐量,如果500是一个正常情况下,或者低于500如果一个位是一个峰。 见缩放下面的注解。

我想,uwsgi有队列设置为一个较小的数字,以更好地处理DDoS攻击; 如果放在巨大的负荷下,大多数请求立即失败,几乎没有加工允许盒作为一个整体仍是响应管理员。

用于缩放系统一般建议

你最重要的考虑可能是最大限度地提高吞吐量 。 另一种可能需要尽量减少响应时间,但我不会在这里讨论这个问题。 在最大限度地提高吞吐量,您试图最大化系统 ,而不是个别的组件; 一些地方降低可以提高整个系统的吞吐量(例如,使这种情况发生, 以便提高数据库性能的增加延迟在Web层的变化是净增益)。

在细节:

  1. 移动数据库到一个单独的机器 。 在此之后,通过运行负载测试期间轮廓DB top和你喜欢的MySQL监控工具。 你需要能够轮廓。 移动数据库到一个单独的机器将引入每个请求一些额外的延迟(几毫秒),因此预计略有增加的进程数在Web层,以保持相同的吞吐量。
  2. 确保uswgi请求队列是大到足以处理使用流量突发--listen参数。 这应该是几十倍的最大稳态请求每秒您的系统可以处理。
  3. 在web /应用层: 平衡的进程数与CPU内核的数量 ,并在过程中的固有等待时间。 进程太多会降低性能,你永远不会充分利用系统资源太少手段。 没有固定的平衡点,因为每个应用和使用模式是不同的,所以基准调整。 作为一个指南,使用进程的延迟,如果每个任务都有:

    • 0%的等待时间,那么你就需要每个核心1点的过程
    • 50%的等待时间(即,CPU时间为一半的实际时间),则需要每芯2个流程
    • 67%的等待时间,则需要每核心3个流程
  4. 检查top在测试过程中,以确保您有高于90%的CPU使用率(每一个核心),你有一个平均负载在1.0以上一点点。 如果平均负载越高,缩减的过程。 如果一切顺利,在某些时候,你将无法实现这一目标,和DB现在可能是瓶颈

  5. 在某些时候,你需要在Web层更多的权力。 您可以选择更多的CPU添加到机器(比较容易),因此增加更多的流程, 和/或可以在多个处理节点(水平可扩展性)增加。 后者可以在uwsgi使用所讨论的方法来实现这里通过卢卡斯Mierzwa


Answer 2:

请运行基准超过一分钟长得多(5-10至少),你真的不会得到这么短的测试多的信息。 并使用uWSGI的碳插件,推动统计信息碳/石墨服务器(您需要有一个),您将有调试的更多信息。

当您发送500个并发请求到你的应用程序,它不能处理这样的负载,在每个后端将很快填补听队列(这是默认100个请求),您可能要增加,但如果工人不能处理如此快速的侦听队列(也称为积压)充满请求,Linux的网络栈将会丢弃请求,你会开始得到错误。

你的第一个基准状态,你可以处理〜42毫秒一个请求,让单身职工可以最多1000毫秒/ 42ms =〜每秒23个请求进程(如果应用程序堆栈的DB和其他地区的并发上升并没有减缓) 。 因此,要处理500个并发请求,则至少需要23分之500= 21名工人(但在现实中,我会说至少40),你只有16,怪不得它打破了这样的负载下。

编辑:我已经并发混合率 - 至少21名工人将让您每秒处理500个请求,而不是500个并发请求。 如果你真的要处理500个并发请求比你只需要500名工人。 除非你将运行在异步模式下的应用程序,检查uWSGI文档“GEVENT”一节。

PS。 uWSGI带有与后端自动配置大负载平衡器(阅读“订阅服务器”和“FastRouter”文档)。 您可以设置它的方式,让您热插拔新的后端根据需要,你刚开始新节点上的工人,他们将认购FastRouter并开始获得请求。 这是横向扩展的最佳途径。 并与AWS后端,以便在需要时新后端将很快开始使用可自动执行此。



Answer 3:

添加更多的工人和越来越少的R / S意味着您的要求“是纯CPU”并没有IO等待直至另一工作人员可以用它来服务于其他请求。

如果你想扩展您将需要使用另一台服务器有更多的(或更快)的CPU。

然而,这是一个综合测试,你会得到R / S的数量上限为您所测试的确切要求,一旦生产还有很多变量会影响性能。



文章来源: Bad Django / uwsgi performance