В Поисках Оптимального Решения: Результаты Тестирования и Выводы

Posted by Alexey Kovyrin under Development · english

После выхода моей предыдущей статьи о методах высокопроизводительной настройки проектов на RoR, я получил море сообщений/писем и поговорил с кучей народу через IM и многие сообщения касались логических или методических ошибок в процессе тестирования или содержали в себе просьбу расширить или изменить параметры тестирования. Именно поэтому я решил провести новое тестирование – более глубокое и намного более широкое в плане охвата различных решений. В этой статье Вы сможете ознакомиться с методикой тестирования и, конечно же, с результатами проведенных тестов. Если Вас интересуют детали настройки ПО в отдельных тестах, Вы можете заглянуть в раздел сайта, посвещенный “Ruby On Rails” и прочесть отдельные сообщения из серии “В поисках оптимального решения” о каждом из тестировавшихся подходов к запуску rails-приложений (статьи о конфигурации будут готовы в течение ближайших нескольких дней).

Честно говоря, когда я начинал все это тестирование, моей основной целью был поиск наиболее оптимального решения для инсталляции моего проекта. Но позже, блягодаря критике Дмитрия Штефлюка, я решил проводить тестирование, стараясь выжать максимум из каждого теста с целью найти максимальную теоретическую скорость, которую я могу ожижать от моих серверов при работе с Ruby on Rails. Поэтому Вы можете интерпретировать результаты тестирования с двух различных точек зрения: как простое сравнение производительности различных решений или как оценку сверху возможной производительности вашего решения (конечно, вы можете получить больше от этих решений если будете использовать более сложные схемы вроде “nginx/lighttpd для статики + rails для динамики”, но это тетсирование проводилось только для динамики без использование различных схем кеширования).

В первую очередь хотелось бы описать платформу, на которой производилось тестирование. Наш development-сервер имеет следующую аппаратно-программную конфигурацию:

  • CPU: 4 x XEON CPUs
  • Память: 4 Gb of RAM
  • OS: Debian GNU/Linux Testing с последним 2.6 ядром.
  • Ruby: ruby 1.8.4 (2005-12-24) [i486-linux] из репозитория Debian Testing.
  • Rails: Rails 1.1.6 проинсталлирован из gem.

Все тесты проводились на простом rails-приложениис одним single-action контроллером:

class TestController < ApplicationController
  def hw
    @hello = "Hello, world!"
    @time = Time.now()
  end
end

м простым view:

<h1>Test#hw</h1>

<p>Hello: <%= @hello %></p>
<p>Time: <%= @time %></p>

Rails Framework был запущен с настройками по-умолчанию для production mode. Тесты были выполнены при помощи программы ab (Apache Benchmark) со следующими параметрами:

$ ab -c 100 -n 10000 http://SERVERIP:PORT/test/hw

где PORT – это специфичный для каждого теста номер порта, на котором принимаются запросы.

Я решил не добавлять никакого I DB-related кода, т.к. моей целью было тестирование проиводительности движка Ruby On Rails, а не mysql/postgres/oracle/etc. Простой код, простые тесты, простые и понятные результаты.

Замечение: Перед каждым тестом все файлы из каталога tmp/sessions удалялись, т.к. после огромного количества запросов там скапливается очень много файлов и на результаты теста начинают влиять проблемы файловых систем. Потому, если вы захотите повторить проделанные мною тесты на своей платформе, очищайте этот каталог перед каждым тестом.

В процессе работы тестов я наблюдал за сервером при помощи top/iostat/vmstat с целью понять, почему результаты получились именно такими… и именно поэтому во всех тестах с учатием fastcgi/proxy/lsapi использовались 4 backend-процесса – у сервера 4 CPU и 4 процесса показали себя наиболее производительно по сравнению с 2/5/8/10.

Итак, позвольте мне кратно описать протестированные конфигурации перед тем, как показать вам результаты тестирования (ссылки с названий тестов позже будут вести на более детальные описания конфигураций).

  1. WEBrick/1.3.1 – Был протестирован с единственной целью – получить некоторую величину, с которой можно будет сравнивать все остальные результаты тестов.
  2. mongrel (single process) – Этот тест был проведен для того, чтобы получить информацию, насколько сильно меняет производительность использование одного процесса mongrel без балансирующего ПО.
  3. lighttpd (4 mongrels) – Тест на качество реализации в lighttpd балансировки нагрузки между несколькими tcp backend серверами.
  4. lighttpd (4 fastcgi processes) – Тест на качество реализации в lighttpd балансировки нагрузки между несколькими fastcgi серверами.
  5. nginx (4 mongrels) – Тест на качество реализации в nginx балансировки нагрузки между несколькими tcp backend серверами.
  6. nginx (4 fastcgi processes) – Тест на качество реализации в nginx балансировки нагрузки между несколькими fastcgi серверами.
  7. pen (4 mongrels) – Тест на качество реализации в pen балансировки нагрузки между несколькими tcp backend серверами.
  8. pound (4 mongrels) – Тест на качество реализации в pound балансировки нагрузки между несколькими tcp backend серверами.
  9. haproxy (4 mongrels) – Тест на качество реализации в haproxy балансировки нагрузки между несколькими tcp backend серверами.
  10. apache 2.0 (4 fastcgi processes) – Тест на качество реализации в apache 2.0 балансировки нагрузки между несколькими fastcgi серверами.
  11. LiteSpeed (4 lsapi instances) – Этот экзотический продукт был проверен только из-за того, что в коментариях к прошлому тестированию я увидет просьбу проверить его. Веб-сервер LiteSpeed содержит в себе какой-то SAPI модуль для ruby который, AFAIU, работает как обычный FastCGI, но с некоторыми улучшениями в плане производительности. Но, к несчастью, оптимизированная версия стоит некоторого количества денег, что значительно ограничивает круг ее пользователей (у них есть бесплатная версия, но если вам нужна от сервера производительность, но придется заплатить достаточно много денег).

Ну, вот и настало время представления результатов тестирования. Прежде всего хочу представить результаты в табличном виде (screenshot из Excel-таблицы:

Ruby On Rails Deployment Schemes

Если Вам приятнее анализировать результаты в графическом представлении, Вы можете взглянуть на следующую диаграмму, показывающую QPS (количество запросов в секунду) для всех тестов:

Ruby On Rails Deployment Schemes Performance Diagram

Говоря о результатах, Я могу скзаать, что все они были предсказуемы – TCP действительно медленнее, чем unix sockets, которые использовались в тестах с fastcgi. Поэтому все тесты, в которых общение с backend-сервером происходит через tcp, имеют QPS ниже, чем основанные на unix socket. Но должен обратить Ваше внимание на следующий факт: общение frontend-backend через tcp тоже дает отличную производительность сохраняя при этом практически не ограниченную масштабируемость решения и возможность переносить backend-сервера между физическими машинами без каких-либо проблем. Итак, если вам нуобходима абсолютно максимальная производительность от одного сервера, я бы порекомендовал использовать nginx с fastcgi backend-процессами. Но если вам необходима масштабируемость, вы можете использовать nginx с mongrel-серверами в качестве backend-процессов.


Related posts:

  1. В поисках оптимального решения: Ruby On Rails и Mongrel
  2. Ошибка в Perl’овом Thread::Semaphore: Утечка памяти (решение прилагается)
  3. Обзор Типичных Конфигураций Для Nginx
  4. Варианты настройки Ruby On Rails на максимальную производительность: mongrel vs lighttpd vs nginx
  5. HAProxy – Надежный, высокопроизводительный балансировщик нагрузки для TCP/HTTP

42 Responses to this entry

Bart Braem says:

That’s incredible, Apache performs almost as well as the best solution. We have lots of newer solutions like nginx and mongrel and still Apache is better?

Scoundrel says:

2Bart: Yes. Apache performs well, but it requires really more memory than all other fastcgi solutions. And that is why you should use it only if you need some additional apache features… but I don’t know what these features may be…

Alexander Solovyov says:

В русском варианте ошибки: послещенный, тетсирования, каждам.

> Но если вам необходима масштабируемость, вы можете использовать nginx с mongrel-серверами в качестве backend-процессов.

Тут хотелось бы заметить – мне кажется, что fastcgi через tcp будет всё равно быстрее, чем mongrel.

nginx жжот. :D

Scoundrel says:

2Bart: I have no concrete results, but as you maybe know, Apache forks additional processes for every new client… so comparing to FSM-based nginx and lighttpd this approach is not so memory efficient. This is known flaw of apache and that is why many sites using nginx/lighttpd+php even if it can’t give them any performance gain.

frust says:

На сколько я вижу, apache идет ноздря в ноздрю, ну разве, что кушал оперативы больше… я правда надеялся на больший разрыв :)

al says:

Hi,

thanks for this tests and your time ;-) .
Is it possibel to get the configfiles, as tar or every single file ;-)

Do you have intentions to make some tests with two maschines, first the webserver second the *appserver*?!

Scoundrel says:

2al: All config files will be posted in additional posts within next few days. Each post will describe one specific configuration with configs and test results.

Bob Silva says:

I know benchmarks are never the same across hardware and setups, but I performed similar benchmarks on similar hardware and LiteSpeed came out with a 2×1 advantage over the Apache2 fcgi setup. My Apache spec’d out similarly at ~275 and Litespeed was over 600qps. LiteSpeed does take some configuration for a performance setup so that may very well be the difference. Nice job though.

Scoundrel says:

2Bob: Have some questions:
1) What HW did you use to get 600 qps from rails?
2) What rails code you have tested?
3) What performance settings you mean in LSWS?

George Wang says:

As the developer of LSWS and Ruby LSAPI, I am pretty surprised that nginx+FCGI can beat LSWS+LSAPI in your test.
So, I spent some time on benchmarking LSWS and nginx tonight. I will post the details on our blog tomorrow.

My result is: nginx + 4 FCGI does 270-330 rps, LSWS Enterprise + LSAPI does 370 rps consistently.

Which version of LSWS was tested? Standard or Enterprise?
Maybe LSWS has not been properly tuned in your test. A few check points: the lastest 2.2 release is used, .htaccess is off, epoll instead of poll is used, “Run On Start Up” is “Yes” and “Priority” set to “5″ in Rails Default Settings.

Scoundrel says:

2George Wang: Can you contact me (I would prefer some IM) to speak about my test parameters. Maybe I can redo some tests to get another results from lsws. Or maybe you have used some unoptimal nginx settings… Will look forward for your answer (my TZ is GMT-5, and I will be online from 9-10 AM).

Scoundrel says:

Даже не знаю, что ответить. Когда хотел потестить его mod_proxy_balancer, тот тупо не скомпилился на gcc 4.1… И я решил, что не буду тестить то, что никогда бы не поставил в продакшн по причине сырости (а она очевидна).

Макс Лапшин says:

Понял. Ну я на apache2.2 не перешел еще по той простой причине, что его нет в репозитории дебиана, но дома на Макоси (не продакшн, конечно) из портов он собрался моментально и так же моментально настроился.

Я почему хотел бы в этих тестах видеть apache2.2 — что бы в тех же условиях сравнить. Я серьезно думаю насчет использования именно такой схемы в production.

С fastcgi есть огромные проблемы с правами. Заебывает, когда в твоем каталоге есть что-то, что принадлежит веб-серверу, а если оно ему не будет принадлежать, то просто увидишь Application Error.

Т.е. для shared хостинга мне сейчас mongrel кажется более удобным вариантом, нежели fastcgi.

Timur Vafin says:

Покажите, пожалуйста, конфиг nginx.

Конкретно интересует удалось ли его настроить так чтобы корректно работало кеширование в рельсах?

h says:

Apache doesn’t fork a new instance for every connection.

Rather it “preforks” some instances, and all those wait for connections. When there is an incoming connection, a random instance processes it. Since selecting this process is an O(1) operation, it actually scales better than single-process servers which have to allocate data structures for processing the connection when receiving it, this depends on the data structures used but it’s usually O(log n) [In the best case?!]. (Because the preforked process’ stack is already allocated)

All the instances share the same memory segment for code and data, only when modifying data is it copied and then written to. So every instance only uses minimal memory, and only while processing connections. This is the same with single-process servers, processing connections takes at least as much memory for them too (or more: counting the poll arrays they can very well take more).

Since “zero” copy file sending is more efficient than copying from kernel to user-space and back again, efficient servers will want to use sendfile or equivalent. But sendfile is synchronous, so the kernel has no chance to reorder disk accesses when a single process is sendfileing from scattered places from all over the disk(s). When multiple processes sendfile however, the kernel can efficiently reorder disk access, because the threads block and yield. Therefore sending the file will have more latency, but higher throughput, and therefore overall higher performance. The disk has an upper limit to throughput, for example with a modern raid array this can be 300MB/s for sequential access, and 20MB/s when seeking randomly. Depending on the size of the site served (sites needing to pay attention to httpd performance can to be huge), the disk and in-memory cache can saturate, and single-process servers will hit the 20MB/s throughput limitation because of seeking, while multi-process servers will only hit the sequential throughput limit which is much higher (say 300MB/s).

Therefore, “preforking” servers such as Apache, xs-httpd or AOLserver are theoretically, in every respect i can think of, higher performance than single-processing servers such as lighttpd, nginx, thttpd or Zeus.

Thanks for reading this rather long comment. I know that just about everybody else out there disagrees with it, but the truth is that they don’t understand the processes involved. So Apache in the same league as lighttpd and nginx is not surprising to me, rather an indication that this is one of the few benchmarks which are at least almost done correctly.

Viget’s Four Labs » Blog Archive » Ruby on Rails: Making the Jump to LiteSpeed says:

[...] Why go to that much trouble when LiteSpeed “Just Works”™? For a basic “Hello World” application, performance compares very well to the cobbled together systems, and installing it takes less than five minutes. To summarize the screencast, here are the ten essential steps for implementing a name-based virtual host running on an Ubuntu server (this will work on any Unix OS): [...]

infotage.net » Blog Archive » Yet another post on how to setup Ruby on Rails to deal with high traffic websites says:

[...] The problem was articulated on the mongrel_users mailing list in an email entitled Why Rails + mongrel_cluster + load balancing doesn’t work for us and the beginning of a solution. The problem is that many load balancers choose poor algorithms to balance requests which can become a problem if the requests vary in the amount of time they take to complete. A round-robin algorithm, for example, could place a show request on one process, and then place another one the next trip around despite that process still being busy while others are available. The solution is that only 1 request at a time can be sent to each backend process for ruby on rails. If all processes are busy then the request should be queued for the next available backend process. Now reviewing the apache 2.2 docs about the ProxyPass Directive it seems like apache has some options to address this. It would be worth testing the various options before going to another solution such as writing another balancer for apache method as suggested here and here or adding another load balancer such as haproxy, nginx, pen, pound, lighttpd, etc.  I also think that this test case would be en excellent way to benchmark various ruby on rails setups absent of a working app as compared to the case used in this comparison: Looking For Optimal Solution: Benchmark Results Summary and Findings.  If nothing else this highlights the need to be very aware of the configuration of the web setup to run ruby on rails applications. [...]

Pythy » Блог Архив » says:

[...] Еще раз отмечу, что FastCGI-сервер определяется вне виртуального хоста и для него указывается абсолютный путь, а для rewrite-правил используется относительный путь к fcgi. FastCGI может "общаться" с фронтенд-севером либо через unix socket, либо через tcp. В первом случае чуть быстрее, во втором – более гибко (в общем случае. FastCGI может быть на другой машине), подробности см. у Ковырина. Конфиги особо не отличаются, но для единообразия я указываю настройки для unix socket. [...]

Pythy » Блог Архив » Развертываем WSGI приложение… says:

[...] Еще раз отмечу, что FastCGI-сервер определяется вне виртуального хоста и для него указывается абсолютный путь, а для rewrite-правил используется относительный путь к fcgi. FastCGI может "общаться" с фронтенд-севером либо через unix socket, либо через tcp. В первом случае чуть быстрее, во втором – более гибко (в общем случае. FastCGI может быть на другой машине), подробности см. у Ковырина. Конфиги особо не отличаются, но для единообразия я указываю настройки для unix socket. [...]

Octablog » Now Serving: Octabox says:

[...] For a performance comparison, Lighttpd provided benchmarks of their own (the page is a little messy, you’ve been warned) which claim superiority. Not everyone agrees. And some more benchmarks for good measure (taken from a blog on Ruby on Rails performance). Even more benchmarks from a competing server technology, Litespeed. [...]

商業服務的Rails HTTP Cluster觀念及測試 says:

[...] 網路上可以看見許多Cluster的文章,但多半都是介紹單方面的功能,或是許多實做的細節牽扯在一起。這篇文章用商業服務的整體規劃來看Cluster及Rails之間的種種問題與解決方法。文章後面附帶了一節是要解釋如何解讀apache menchmark的數據。而各位如果想要瞭解的是效能,數據上的差異,已經有一篇相當棒的可以參考: http://blog.kovyrin.net/2006/08/28/ruby-performance-results/ [...]

NaTTs says:

А Вы не пробывали запустить mongrel на юникс сокете? в мануалах к nginx разглядел, что прокси можно кидать и на сокет, если mongrel сможет слушать сокет – мы получим не плохой прирост производительности, не так ли? ищу материалы, свяжусь с вами, если что получится ;)

Adam Byrtek says:

Benchmarking HTTP Performance…

Deployment of Rails application is a subject that tends to raise some hot discussions, leading to many misunderstandings. That’s why I decided to try different deployment strategies and check for myself how they perform.
To make any reasonable co…