Статьи

Удаленный доступ к вашим услугам: EJB против Spring / Hessian


На днях у меня появилось дизайнерское решение для небольшого сайта.
Достаточно маленький, чтобы поместиться на одном физическом сервере. Ничего особенного, у меня была довольно хорошая идея по поводу использования фреймворков, и это будет классическое развертывание переднего и внутреннего серверов. С другой стороны, внутренним сервером, скорее всего, будет 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: я знаю, что вы всегда можете написать еще одну статью о том, насколько небрежен код, пожалуйста, не обращайте на это мое внимание. Это достаточно хорошо, чтобы служить своей цели 😉