На днях у меня появилось дизайнерское решение для небольшого сайта. Достаточно маленький, чтобы поместиться на одном физическом сервере. Ничего особенного, у меня была довольно хорошая идея по поводу использования фреймворков, и это будет классическое развертывание переднего и внутреннего серверов. С другой стороны, внутренним сервером, скорее всего, будет JBoss. И вот тут все стало интересно.
Я планировал использовать Spring на серверной части. Но потом я подумал, какой смысл в том, чтобы запускать полноценный JBoss только для того, чтобы использовать Spring поверх него? Может быть, EJB будет лучшим выбором в этом случае? В частности, я думал о связи между двумя серверами: развился ли RMI-IIOP с течением времени, чтобы стать лучшим гессианом? Хороший инженер-программист должен знать спецификации для обоих и уже иметь ответ. Поэтому мне пришлось придумать что-то еще.
Я быстро написал два сервиса, один для Spring, другой для EJB. И клиент для обоих. Это будет делать несколько вызовов в цикле и распределять их по времени — для обеих служб. Службы будут возвращать char [] различных размеров, чтобы я мог видеть влияние различных полезных нагрузок (0, 1024, 2048, до 16384). Правда, это не идеальный сценарий реального мира, если я не использую БД и не выполняю бизнес-логику в процессе, но я пытаюсь сравнить здесь реализации протоколов.
Все еще ничего особенного. Так о чем эта статья? Ну что ж … результаты!
Вот что я получил за первые запуски:
Run 1 - Time [s] Run 2 - Time [s]
Calls Payload EJB Spring EJB Spring
===============================================================
1,000 0 3.652 .836 3.628 .754
1k 2.473 .694 2.679 .633
2k 2.143 .653 2.291 .634
4k 40.547 .611 40.242 .766
8k 40.311 1.102 40.335 1.251
16k 25.658 1.725 27.965 1.672
---------------------------------------------------------------
10,000 0 17.218 3.218 16.751 3.413
1k 11.919 2.236 12.017 2.279
2k 11.205 2.454 11.280 2.717
4k 401.615 4.618 401.225 4.655
8k 401.252 7.999 401.645 8.248
16k 91.225 12.545 78.676 12.762
---------------------------------------------------------------
100,000 0 112.856 13.395 111.962 14.113
1k 111.384 19.956 111.236 22.019
2k 109.806 26.476 107.961 27.201
4k 4019.744 51.204 4019.709 38.711
8k 4025.306 70.093 4013.178 68.229
16k 1094.349 123.929 1044.610 124.129
Запуск 1 и 2: Kubuntu 9.10 beta, ядро 2.6.31 x86_64, SunJDK 1.6.0
Итак, вот мой ответ: Spring / Hessian быстрее, чем удаленный EJB. Даже использование процессора благоприятствует комбинации Spring / Hessian. Для Spring / Hessian типичное соотношение нагрузки клиент: сервер составляет около 3: 1, в то время как для EJB большую часть времени оно составляло от 1: 1 до 1: 2. Более конкретно, Spring накладывает около 30% нагрузки на процессор на клиенте и 10% на сервер; в то же время EJB загружает около 25% — 25% (нагрузка клиента может снизиться ниже, нагрузка на сервер может превысить 30%).
Но в то время как Spring демонстрирует меньшее время отклика и хорошее, почти линейное масштабирование, как насчет этих дурацких чисел EJB? Я специально провел эти тесты дважды, чтобы подтвердить их.
Для полезных нагрузок до 2 КБ графики практически плоские как для Spring, так и для EJB, но есть некоторые неприятные всплески для EJB при полезных нагрузках 4 и 8 КБ. Также странно, что при вызовах EJB 4k и 8k нагрузка на процессор была практически равна как для клиента, так и для сервера.
В поисках ошибок в JDK я переключился на OpenJDK.
Run 3 - Time [s]
Calls Payload EJB Spring
=======================================
1,000 0 3.599 .870
1k 2.522 .664
2k 2.469 .590
4k 40.191 .680
8k 40.220 1.108
16k 26.832 1.671
---------------------------------------
10,000 0 16.076 3.029
1k 11.712 2.229
2k 10.673 2.876
4k 401.004 4.669
8k 401.069 7.815
16k 93.677 12.297
---------------------------------------
100,000 0 111.863 13.325
1k 107.690 19.045
2k 106.736 25.726
4k 4016.907 40.947
8k 4023.713 72.376
16k 902.372 125.865
Прогон 3: Kubuntu 9.10 beta, ядро 2.6.31 x86_64, OpenJDK 1.6.0
Результаты OpenJDK практически идентичны. Это может быть либо JBoss, либо ОС, вызывающая это.
Далее несколько номеров Windows, давайте посмотрим, если это ОС.
Run 4 - Time [s] Run 5 - Time [s]
Calls Payload EJB Spring EJB Spring
===============================================================
1,000 0 6.246 1.350 3.531 .781
1k 5.623 1.174 2.703 .704
2k 4.594 1.433 2.516 .718
4k 6.354 1.710 2.375 .891
8k 5.852 3.237 2.562 1.281
16k 6.729 3.996 2.563 1.953
---------------------------------------------------------------
10,000 0 41.604 6.824 23.797 4.266
1k 23.960 6.534 22.250 3.906
2k 32.041 9.198 22.406 4.781
4k 37.564 9.312 22.391 6.657
8k 23.752 10.246 22.421 10.547
16k 27.492 24.511 N/A 18.109
---------------------------------------------------------------
100,000 0 371.700 42.819 N/A 30.266
1k 267.341 59.343 N/A 39.640
2k 353.062 83.713 N/A 48.610
4k 335.661 90.985 N/A 67.265
8k 294.875 141.625 N/A 105.719
16k 306.000 270.254 N/A 182.906
Прогон 4: Win7RC / Virtualbox x86_64, SunJDK 1.6.0
Прогон 5: WinXP x86, SunJDK 1.6.0
Хорошо, больше нет всплесков времени отклика EJB, хотя времена все еще выглядят подозрительно плоскими. Кроме того, на WinXP, после 8k / 1000 вызовов, я просто получаю исключение (Исключение в потоке «main» org.jboss.remoting.CannotConnectException: Невозможно получить соединение с сервером. Проблема при установлении сокетного соединения для InvokerLocator [socket: //127.0 .0.1: 3873 /]). Тем не менее, я использую ту же ревизию JBoss (5.1.0GA-jdk6, кстати), так что кажется, что JBoss не вызывает эти пики. Это оставляет меня с ОС.
Вернуться к Linux.
Run 6 - Time [s] Run 7 - Time [s] Calls Payload EJB Spring EJB Spring =============================================================== 1,000 0 2.996 .779 3.314 1.012 1k 2.553 .617 2.462 .658 2k 2.568 .843 2.301 .900 4k 42.797 1.015 43.080 1.002 8k 42.122 1.639 43.046 1.369 16k 5.507 2.489 22.103 2.401 --------------------------------------------------------------- 10,000 0 21.182 3.502 22.448 4.074 1k 19.669 4.604 22.458 6.292 2k 19.714 5.975 22.794 8.794 4k 423.331 8.382 2590.024 10.251 8k 421.513 14.162 2597.836 14.667 16k 29.179 26.055 132.132 21.854 --------------------------------------------------------------- 100,000 0 188.130 26.938 221.967 53.123 1k 189.032 42.154 224.095 67.212 2k 189.972 55.225 226.334 86.580 4k 4184.557 78.404 21679.748 103.160 8k 4169.555 136.656 N/A N/A 16k 306.762 236.289 N/A N/A
Прогон 5: Kubuntu 9.10 beta / Virtualbox, ядро 2.6.31 x86, SunJDK 1.6.0
Прогон 6: CentOS 5.3 / Virtualbox, ядро 2.6.18 x86, SunJDK 1.6.0 При
переключении на 32 бита шипы активны и
работают — это не проблема архитектуры тоже. Это ошибка ядра? Предполагается, что этот проект будет размещен на CentOS 5.3, поэтому я протестировал этот следующий (и последний). Шипы вернулись с удвоенной силой (~ 5 раз выше). Я прекратил тестирование после достижения нелепых значений (~ 6 ч для 100 000 вызовов).
На данный момент я могу только сделать вывод, что это либо давняя ошибка в ядре Linux, либо неправильное использование API ядра реализациями JVM — я знаю, что использовал две разные JVM, но они, тем не менее, очень похожи. Тестирование с JDK 1.5.0, вероятно, было бы лучше, но у меня тоже не было на это времени.
Что мы можем извлечь из всего этого? Урок, который мы уже знаем: никогда не предполагайте, всегда проверяйте. По крайней мере, в этом случае я считаю, что результаты были довольно неожиданными. Если бы я только что поработал с EJB и выполнил некоторое профилирование окончательного приложения, я бы увидел только приложение, которое не масштабируется далеко за определенным моментом (или неожиданное сбой соединения, если я нацеливался на WinXP). И я был бы счастлив, если бы смог исправить это после многих рабочих часов, если вообще смог.
PS: Прилагается, вы найдете архив тестового кода, если вы хотите подтвердить мои выводы или протестировать дополнительные комбинации.
PPS: я знаю, что вы всегда можете написать еще одну статью о том, насколько небрежен код, пожалуйста, не обращайте на это мое внимание. Это достаточно хорошо, чтобы служить своей цели ?