不佳的Django/uwsgi性能表现

53

我正在使用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/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 Framework向客户端提供JSON内容。

我已经安装了Django Profiling和Django Debug Toolbar来查看发生了什么。在运行单个请求时,Django Profiling给出以下结果:

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-toolbar 显示如下:

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
问题在于"top"显示负载平均值迅速上升,而我在本地服务器和网络内的远程机器上运行的apache基准测试显示我没有服务很多请求/秒。问题出在哪里?这是我对代码进行分析时取得的进展,所以如果有人能指出我在做什么,那将不胜感激。
编辑(23/02/2013):根据Andrew Alcock的回答添加更多细节: 需要注意/回答的要点是 (3)(3)我在MySQL上执行了“show global variables”,发现MySQL配置中max_connections设置为151,这足以为我启动的uwsgi工作线程提供服务。
(3)(4)(2)我正在分析的单个请求是最重的请求。根据django-debug-toolbar,它执行了4个查询。发生的情况是所有查询都在以下时间内运行:3.71、2.83、0.88、4.84毫秒。
(4)这里指的是内存分页吗?如果是的话,我该怎么办?
(5)在16个工作线程、100并发率、1000个请求的情况下,负载平均值会上升到约12。我在不同数量的工作线程上运行了测试(并发级别为100):
1个工作线程,负载平均值~1.85,19个请求/秒,每个请求的时间:5229.520,0个非2xx; 2个工作线程,负载平均值~1.5,19个请求/秒,每个请求的时间:516.520,0个非2xx; 4个工作线程,负载平均值~3,16个请求/秒,每个请求的时间:5929.921,0个非2xx; 8个工作线程,负载平均值~5,18个请求/秒,每个请求的时间:5301.458,0个非2xx; 16个工作线程,负载平均值~19,15个请求/秒,每个请求的时间:6384.720,0个非2xx。
正如您所看到的,我们拥有的工作线程越多,系统上的负载就越大。我可以在uwsgi的守护程序日志中看到,当我增加工作线程数时,响应时间(以毫秒为单位)会增加。
在16个工作线程、500并发级别请求下运行时,uwsgi开始记录错误:
 writev(): Broken pipe [proto/uwsgi.c line 124] 

负载高达约10,由于非2xx响应达到1000个中的923个,因此测试时间不长,这也是响应速度快的原因,因为它几乎是空的。 这也是对总结中第4点的回复。

假设我在这里面临的是基于I/O和网络的操作系统延迟,建议采取什么措施扩展规模? 新硬件? 更大的服务器?

谢谢


磁盘IO性能如何?VMware虚拟机是否处于负载状态或为空闲状态?数据库方面呢? - Bruce
这两个盒子连接的速度是多少? - Joe Doherty
@Bruce 唯一的I/O操作将是从mysql读取。这两个实例都有我从生产实例获取的完全相同的转储。 - Maverick
@JoeDoherty 网络速度?我正在使用已添加到 /etc/hosts 文件中的域名从服务器内部进行测试。至于 CPU 速度,小型实例具有“1 EC2 计算单元(带有 1 个 EC2 计算单元的虚拟核心)”。根据常见问题解答: “一个 EC2 计算单元提供与 2007 Opteron 或 2007 Xeon 处理器的 1.0-1.2 GHz 的 CPU 容量相当。” 如果在 vmware 上使用 1 个核心,则使用我的 Mac 上的物理核心为 2.3 GHz。 - Maverick
1
抱歉,我没有看到你在盒子上运行它们。你是否已经将UWSGI从循环中移除,并针对盒子上的静态内容运行AB? - Joe Doherty
显示剩余4条评论
3个回答

155

编辑1:看到您有1个虚拟核心的评论,因此在所有相关点上添加评论。

编辑2:来自Maverick的更多信息,因此我正在排除已被排除的想法并开发确认的问题。

编辑3:填写了关于uwsgi请求队列和扩展选项的更多细节。改进了语法。

编辑4:来自Maverick的更新和小改进。

评论太小了,所以这里有一些想法:

  1. 平均负载基本上是指正在运行或等待CPU关注的进程数量。对于一个有1个CPU核心的完美负载系统,平均负载应该是1.0;对于一个4核系统,应该是4.0。当你运行Web测试时,线程会飙升,你会有大量等待CPU的进程。除非平均负载超过CPU核心数量的显著范围,否则这不是一个问题
  2. 第一个“每个请求时间”值为4秒,与请求队列的长度相对应——将1000个请求几乎瞬间倾倒在Django上,并且平均需要4秒来服务,其中大约有3.4秒是在队列中等待的。这是由于请求数(100)与处理器数量(16)之间非常不匹配,导致84个请求在任何一个时刻都在等待处理器。
  3. 以并发数100运行测试,24个请求/秒需要41秒。你有16个进程(线程),因此每个请求处理大约需要700毫秒。鉴于你的交易类型,这是一个很长的请求时间。这可能是因为:

    1. Django中每个请求的CPU成本很高(鉴于调试工具栏中的低CPU值,这是极不可能的)
    2. 操作系统正在频繁任务切换(特别是如果平均负载高于4-8),且延迟完全是由于进程过多导致的。
    3. 没有足够的数据库连接为16个进程提供服务,因此进程正在等待一个可用的连接。每个进程至少有一个连接可用吗?
    4. 数据库周围存在相当大的延迟,包括:

      1. 很多小请求,每个请求需要10毫秒左右,其中大部分是网络开销。如果是这样,您可以引入缓存或将SQL调用减少到更小的数量。或者
      2. 一个或几个请求需要100毫秒以上。要检查这一点,请对数据库运行分析。如果是这样,您需要优化该请求。
  4. 系统和用户CPU成本之间的分配异常地偏向系统,尽管总CPU很低。这意味着Django中的大部分工作与内核相关,例如网络或磁盘。在这种情况下,可能是网络费用(例如接收和发送HTTP请求以及接收和发送请求到数据库)。有时候这会因为页面交换而很高。如果没有页面交换,则您可能根本不必担心这个问题。

  5. 您将进程设置为16,但负载平均值很高(你没有说明有多高)。理想情况下,您应该始终至少有一个进程等待CPU(以便CPU不闲置旋转)。这里的进程似乎没有受到CPU约束,但存在显着的延迟,因此您需要比核心更多的进程。需要多少?尝试使用不同数量的处理器(1、2、4、8、12、16、24等)运行uwsgi,直到获得最佳吞吐量。如果改变了平均进程的延迟,则需要再次进行调整。
  6. 500个并发级别肯定是一个问题,但是是客户
    所以,总的来说:

    enter image description here


    1. Django看起来不错
    2. 负载测试的并发量(100或500)与进程数(16)之间存在不匹配:你正在将过多的并发请求推入系统,而进程数量无法处理。一旦超过进程数,所有会发生的就是你会延长Web服务器中的HTTP请求队列。
    3. 存在较大的延迟,因此要么

      1. 进程数(16)与CPU核心数(1)之间存在不匹配:如果负载平均值> 3,则可能是进程太多了。请使用较小的进程数再试一次

        1. 负载平均值> 2-尝试8个进程
        2. 负载平均值> 4-尝试4个进程
        3. 负载平均值> 8-尝试2个进程
      2. 如果负载平均值<3,则可能在数据库中,请对数据库进行分析,以查看是否存在大量的小请求(导致延迟增加),或者一个或两个SQL语句是问题所在。

    4. 如果没有捕获失败的响应,我对500并发的故障没有什么可说的。

    开发想法

    在单核机器上,您的负载平均值>10真的很糟糕,并且(正如您所观察到的)会导致大量任务切换和一般的缓慢行为。我个人不记得看过负载平均值为19的机器(您有16个进程),恭喜您将其提高到这么高 ;)

    数据库性能很好,所以我现在可以放心地给出一个全部清除。

    分页:要回答您关于如何查看分页的问题-您可以通过多种方式检测操作系统分页。例如,在top中,标题具有页面输入和输出(请参见最后一行):

    进程:总共 170 个,其中运行中 3 个,卡住 4 个,睡眠中 163 个,线程数 927。负载平均值为 0.90、1.19、1.94,CPU 使用率为 1.37% 用户,2.97% 系统,95.65% 空闲。共享库:144M 常驻内存,0B 数据,24M 链接编辑。内存区域:共计 31726 个,2541M 常驻内存,120M 私有,817M 共享。物理内存:1420M 有线,3548M 活跃,1703M 不活跃,6671M 已用,1514M 空闲。虚拟内存:392G 虚拟大小,1286M 框架虚拟大小,1534241(0) 页面输入,0(0) 页面输出。网络:数据包:789684/288M 接收,912863/482M 发送。磁盘:读取 739807/15G,写入 996745/24G。
    进程数量:在您当前的配置中,进程数量太高了。将进程数量缩减至 2。根据进一步将负载从该服务器转移的情况,我们可能会稍后再提高此值。

    Apache Benchmark的位置:一个进程的平均负载为1.85,这让我想到您正在同一台机器上运行负载生成器和uwsgi - 这是正确的吗?

    如果是这样,您真的需要从另一台机器上运行负载测试,否则测试运行结果不能代表实际的负载情况 - 您会从Web进程中获取内存和CPU以用于负载生成器。此外,负载生成器的100或500个线程通常会以在现实生活中不存在的方式对您的服务器造成压力。这可能是整个测试失败的原因。

    数据库的位置:一个进程的平均负载也表明您正在同一台机器上运行数据库和Web进程 - 这是正确的吗?

    如果我关于数据库的猜测是正确的,那么开始扩展的第一种最好的方法是将数据库移动到另一台机器上。我们这样做有几个原因:

    1. 数据库服务器需要不同于处理节点的硬件配置:

      1. 磁盘:数据库需要大量快速、冗余、备份的磁盘,而处理节点只需要基本的磁盘。
      2. CPU:处理节点需要尽可能快的CPU,而数据库机器通常可以不需要(通常其性能受到磁盘和RAM的限制)。
      3. RAM:数据库机器通常需要尽可能多的RAM(最快的数据库将所有数据存储在RAM中),而许多处理节点需要较少的RAM(您的处理节点每个进程需要约20MB-非常小)。
      4. 扩展性:原子数据库通过使用具有多个CPU的大型机器来实现最佳扩展性,而Web层(没有状态)可以通过连接许多相同的小盒子来实现扩展性。
    2. CPU亲和性:CPU负载平均值为1.0且进程对单个核心具有亲和力更好。这样做可以最大化使用CPU缓存并最小化任务切换开销。通过分离数据库和处理节点,您正在通过硬件强制执行此亲和性。

    500并发处理中的异常情况 上述图表中的请求队列最多为100个 - 如果uwsgi在队列已满时收到请求,则会拒绝该请求并返回5xx错误。我认为这在您的500并发负载测试中发生了 - 基本上是队列填满了前100个线程,然后其他400个线程发出了剩余900个请求并立即收到了5xx错误。

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

    1. 请求队列大小配置为可处理突发情况:使用--listen参数设置uwsgi
    2. 如果500个请求是正常情况或峰值略低于500,系统可以处理高于500个请求/秒的吞吐量。请参见下面的缩放说明。

    我想uwsgi将队列设置为较小的数字以更好地处理DDoS攻击;如果处于巨大负载下,大多数请求几乎不经过处理就立即失败,从而使整个系统对管理员仍具有响应能力。

    扩展系统的一般建议

    你最重要的考虑可能是最大化吞吐量。另一个可能需要的是最小化响应时间,但我不会在这里讨论。在最大化吞吐量时,你正在尝试最大化系统而不是单个组件;一些局部减少可能会提高整个系统的吞吐量(例如,在 Web 层添加延迟以便提高 DB 性能是一种净增益)。

    关于具体问题:

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

      • 0% 的延迟,则每个核心需要 1 个进程
      • 50% 的延迟(即 CPU 时间为实际时间的一半),则每个核心需要 2 个进程
      • 67% 的延迟,则每个核心需要 3 个进程
    4. 在测试期间检查 top,以确保您的 CPU 利用率(对于每个核心)超过 90%,并且您的负载平均值略高于 1.0。如果负载平均值更高,请缩减进程。如果一切顺利,在某些时候,您将无法实现此目标,而数据库可能是瓶颈

    5. 在某个时候,您将需要更多的 Web 层动力。您可以选择向机器添加更多 CPU(相对容易),从而添加更多进程,和/或您可以添加更多处理节点(水平可扩展性)。后者可以使用 uwsgi 中讨论的方法 here by Łukasz Mierzwa 来实现。

12
对于一个难以回答的问题,这是非常详尽和全面的回答。 - Anorov
非常感谢您,安德鲁。请在我的问题底部找到我对您提出的要点的答案。如果您需要更多细节,我愿意尽可能地理解发生了什么,请告诉我。 - Maverick
再次感谢您,您非常有帮助。由于我来自PHP背景,我想知道为什么相比这个第一个Python / Django应用程序,扩展PHP要容易得多...当处理扩展PHP应用程序时,我不必考虑所有这些问题,而只需考虑其中一些要点。我想知道在此时使用PHP编写的同一应用程序将获得什么性能。 既然您提到很少会出现500个并发r/s,那么应该寻找多少个是合理的数量?我正在测试500个,基于我从Google Analytics实时用户那里得到的结果。 - Maverick
我一定会尝试您提供的建议并看看会发生什么。再次感谢您。 附言:您对我上面的评论有何看法? - Maverick
令人难以置信的答案,经得起时间的考验。对于您的第一个观点,当您说“核心数量”时,是指vCPU还是物理CPU?在启用超线程的EC2系统上运行uWSGI - 4个物理CPU> 8个vCPU,并尝试应用您提供的一些有用的指导。 - B Robster
显示剩余3条评论

7
请运行比一分钟更长的基准测试(至少5-10分钟),这样短暂的测试不会提供太多信息。使用uWSGI的carbon插件将统计数据推送到carbon/graphite服务器(您需要一个),您将获得更多的调试信息。
当您向应用程序发送500个并发请求并且它无法处理这样的负载时,每个后端的监听队列都会很快填满(默认情况下为100个请求)。您可能希望增加监听队列(也称为backlog),但如果工作进程无法快速处理请求并且监听队列已满,则Linux网络堆栈将放弃请求并且您将开始收到错误。
您的第一个基准测试表明您可以在约42毫秒内处理单个请求,因此单个工作进程最多可以处理1000ms / 42ms = ~23个请求/秒(如果db和应用程序堆栈的其他部分随着并发性增加而减慢的话)。因此,要处理500个并发请求,您至少需要21个工作进程(但实际上我会说至少40个),而您只有16个,所以很容易在这样的负载下崩溃。
编辑:我混淆了速率和并发——至少21个工作进程将允许您每秒处理500个请求,而不是500个并发请求。如果您真的想处理500个并发请求,那么您只需要500个工作进程。除非您以异步模式运行应用程序,请查看uWSGI文档中的“Gevent”部分。
附:uWSGI带有出色的负载均衡器和后端自动配置(请阅读“订阅服务器”和“FastRouter”下的文档)。您可以将其设置为允许您根据需要热插入新的后端,您只需在新节点上启动工作进程,它们将订阅到FastRouter并开始获取请求。这是水平扩展的最佳方式。如果在AWS上有后端,则可以自动化此过程,以便在需要时快速启动新后端。

非常好的观点 - 我已经将我的答案链接到你的答案并给你点赞。 - Andrew Alcock
很好的观点...但是如果单个进程可以处理23个请求/秒,那么我应该做大约23 *进程数的工作吗?也就是说,23 * 16大约是368个请求/秒。此外,我拥有的工人越多,负载平均值就越高,这将阻止系统有效地处理请求。当负载上升时,我可以看到uwsgi日志中的请求从42毫秒上升到单个请求的~150毫秒。您的答案非常详细,我不能忽略任何一点,但我认为我需要扩展我的硬件以便能够拥有更多的工人。 - Maverick
2
如果您拥有足够的 CPU 和 I/O 能力(最好是无限制的),那么您可以期望这样简单的数学运算能够正常工作。但如果这只是单个 EC2 实例,那么在达到理论请求速率之前,您的 CPU 就会被占满。您在 AWS 中每个实例上都有漂亮的图表,可以使用它们 ;)另外,扩展后端是一件事情,如果您有很多后端,那么数据库将开始成为瓶颈。您不能仅扩展堆栈的一个部分,整个系统与最慢的组件一样缓慢。 - Łukasz Mierzwa

1

增加更多的工作进程,得到更少的 r/s 意味着你的请求 "是纯 CPU",没有 IO 等待,另一个工作进程可以用来处理另一个请求。

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

然而这是一个合成测试,你得到的 r/s 数量是你正在测试的确切请求的上限,在生产环境中会有许多其他变量影响性能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接