Почему производительность падает так сильно, когда мой прокси-сервер Erlang TCP получает много одновременных запросов? - PullRequest
4 голосов
/ 19 августа 2010

В целях обучения я написал простой TCP-прокси на Erlang. Это работает, но у меня возникает странное падение производительности, когда я использую ab (Apache Bench) для выполнения множества параллельных запросов. Я удивляюсь не падению производительности как таковому, а масштабу падения. Бэкэнд - это nginx как веб-сервер. Мой прокси находится между ab и nginx.

Это код моего прокси.

-module(proxy).
-export([start/3]).

start(InPort, OutHost, OutPort) ->
  {ok, Listen} = gen_tcp:listen(InPort, [binary, {packet, 0}, {active, once}]),
  spawn(fun() -> connect(Listen, OutHost, OutPort) end).

connect(Listen, OutHost, OutPort) ->
  {ok, Client} = gen_tcp:accept(Listen),
  spawn(fun() -> connect(Listen, OutHost, OutPort) end),
  {ok, Server} = gen_tcp:connect(OutHost, OutPort, [binary, {packet, 0}, {active, once}]),
  loop(Client, Server).

loop(Client, Server) ->
  receive
    {tcp, Client, Data} ->
      gen_tcp:send(Server, Data),
      inet:setopts(Client, [{active, once}]),
      loop(Client, Server);
    {tcp, Server, Data} ->
      gen_tcp:send(Client, Data),
      inet:setopts(Server, [{active, once}]),
      loop(Client, Server);
    {tcp_closed, _} ->
      ok
  end.

Запустив 64 последовательных запроса на моем прокси, я получаю очень хороший результат.

ab -n 64 127.0.0.1:80/

Concurrency Level:      1
Time taken for tests:   0.097 seconds
Complete requests:      64
Failed requests:        0
Write errors:           0
Total transferred:      23168 bytes
HTML transferred:       9664 bytes
Requests per second:    659.79 [#/sec] (mean)
Time per request:       1.516 [ms] (mean)
Time per request:       1.516 [ms] (mean, across all concurrent requests)
Transfer rate:          233.25 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       1
Processing:     1    1   0.5      1       2
Waiting:        0    1   0.4      1       2
Total:          1    1   0.5      1       2

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      2
  75%      2
  80%      2
  90%      2
  95%      2
  98%      2
  99%      2
 100%      2 (longest request)

Это немного медленнее, чем использовать Apache Bench напрямую против nginx.

Но при выполнении 64 одновременных запросов на прокси производительность падает с ума

ab -n 64 -c 64 127.0.0.1:80/

Concurrency Level:      64
Time taken for tests:   2.011 seconds
Complete requests:      64
Failed requests:        0
Write errors:           0
Total transferred:      23168 bytes
HTML transferred:       9664 bytes
Requests per second:    31.82 [#/sec] (mean)
Time per request:       2011.000 [ms] (mean)
Time per request:       31.422 [ms] (mean, across all concurrent requests)
Transfer rate:          11.25 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   31 121.7      0     501
Processing:     3 1135 714.4   1001    2006
Waiting:        3 1134 714.3   1000    2005
Total:          3 1167 707.8   1001    2006

Percentage of the requests served within a certain time (ms)
  50%   1001
  66%   1502
  75%   2003
  80%   2004
  90%   2005
  95%   2005
  98%   2005
  99%   2006
 100%   2006 (longest request)

В чем / где проблема? Я ожидал снижения производительности, но почему так много? Посмотрите на запросы в секунду!

Кажется, это не имеет большого значения, если я даю много потоков, используя + A. Я даже попробовал SMP, но результаты почти такие же.

Мои настройки: Windows 7 64, Intel QuadCore, 8 ГБ ОЗУ. Я получаю аналогичные результаты в Ubuntu, используя 128 одновременных запросов.

РЕДАКТИРОВАТЬ: Включено новое понимание. Общее количество запросов не имеет значения. Это просто количество одновременных запросов.

Ответы [ 4 ]

4 голосов
/ 20 августа 2010

Эта часть connect/3 является последовательной:

connect(Listen, OutHost, OutPort) ->
  {ok, Client} = gen_tcp:accept(Listen),
  spawn(fun() -> connect(Listen, OutHost, OutPort) end),

Вы не можете принять новое соединение, пока новый порожденный процесс, выполняющий gen_tcp:accept/1, не готов.Это может привести к узким местам в вашем коде.Вы можете попробовать пул "акцепторов", чтобы улучшить производительность в этом случае.Я бы также попытался добавить предложение catch all в loop/2 receive, чтобы избежать случайного заполнения почтового ящика.

А также каковы ваши параметры erl?+A темы и +K true вовлечены?

1 голос
/ 25 сентября 2011

Вы пытались увеличить количество невыполненных заданий сокета прослушивания с параметром {backlog, B}, равным gen_tcp:listen?

Значение по умолчанию 5, я думаю, вам следует попробовать с более высоким значением, например, 30 или50.

1 голос
/ 19 августа 2010

Я не могу воспроизвести ваши результаты. Я попробовал ваши тесты, используя apache, yaws и nginx в качестве веб-серверов, и обнаружил, что ни один из них не использует прокси-сервер с прокси и без него. Я запускал их на Linux, так что, возможно, это проблема с Windows или версией Erlang VM для Windows.

1 голос
/ 19 августа 2010

пробовали ли вы те же тесты непосредственно для nginx? если он не настроен правильно, он также может привести к падению производительности.

...