Статьи

Ориентированные на сервер фреймворки Java: сравнение производительности

В наши дни мы привыкли к сложным веб-фреймворкам с интенсивным использованием AJAX. Эти структуры обеспечивают нам разработку стиля рабочего стола в парадигме одностраничного интерфейса (SPI). Как вы знаете, существует два основных типа фреймворков: клиент-ориентированный и серверно-ориентированный. У каждого подхода есть свои плюсы и минусы.

Тестирование производительности серверных сред Java

В серверно-ориентированном виде состояние управляется на сервере. В некотором смысле, клиент является сложным терминалом сервера, потому что большинство визуальных решений принимается на сервере, а визуальный рендеринг выполняется на сервере (генерация HTML в виде разметки или встраивание в JavaScript или более высокий уровень кода, отправляемый в клиент). Основным преимуществом является то, что данные и визуальный рендеринг находятся в одном и том же пространстве памяти, избегая пользовательских мостов клиент-сервер для обмена данными и синхронизации, что типично для подхода, ориентированного на клиента.

В этой статье рассматриваются только серверно-ориентированные среды Java. 

В SPI веб-страница частично изменена; то есть некоторые части HTML могут быть удалены, и может быть вставлена ​​новая разметка HTML. Этот подход, очевидно, экономит тонны пропускной способности и мощности компьютера, поскольку вся страница не перестраивается и не полностью отправляется клиенту, когда происходит некоторое изменение страницы.

Эффективная серверно-ориентированная среда должна отправлять клиенту ТОЛЬКО разметку, которая будет изменена, или эквивалентные инструкции в какой-либо форме, когда какое-либо событие AJAX попадает на сервер.

В этой статье рассматривается, насколько эффективны большинство веб-инфраструктур SPI Java в отношении частичных изменений, предоставляемых сервером . Нас не интересуют события без взаимодействия с сервером, то есть события без (возможного) управления сервером.

Как они будут измеряться

Мы собираемся измерить количество кода, отправляемого клиенту, относительно визуального изменения, выполненного в клиенте .

Например, для незначительного визуального изменения (некоторых новых данных) в компоненте мы ожидаем, что от сервера будет мало кода, то есть новая разметка, необходимая в виде простого HTML или встроенного в JavaScript, или некоторые высокоуровневые инструкции, содержащие новые данные, которые должны быть визуализируется. В противном случае что-то кажется неправильным, например, перестраивается весь компонент или зона страницы, что приводит к потере пропускной способности и мощности клиента (и, возможно, мощности сервера).

Поскольку мы будем использовать публичные демонстрации, мы не собираемся получать окончательный и точный эталонный тест . Но вы увидите очень сильные различия между фреймворками.

Техника тестирования очень проста, и каждый может сделать это без специальной инфраструктуры, нам просто нужны FireFox и FireBug. В этом тесте используются FireFox 3.6.8 и FireBug 1.5.4.

Консоль FireBug, когда «Show XMLHttpRequests» включена, регистрирует любой AJAX-запрос, показывая ответ сервера.

Процесс прост:

  1. Консоль будет включена перед загрузкой страницы с демонстрацией.
  2. Некоторые щелчки приведут конкретный компонент к желаемому состоянию.
  3. Последний щелчок приведет к небольшому изменению анализируемого компонента.
  4. Затем мы скопируем код вывода запроса AJAX (HTML, XML, JavaScript …), отправленного с сервера.

Чем больше кода, тем меньше эффективность, тем больше трата трафика и клиентской обработки.

Мы не можем измерить используемую мощность сервера, потому что нам нужны глубокие знания о том, как работает инфраструктура на сервере, и мы можем легко «заподозрить», что чем больше кода генерируется на сервере, тем больше энергии сервера тратится впустую.

Каркас проверен

RichFaces , IceFacesMyFaces / Тринидад , OpenFaces , PrimeFaces , Vaadin , ZK , ItsNat

ADF Faces не тестировался, потому что больше нет публичной демоверсии. Поскольку ADF Faces основан на Тринидаде, анализ Тринидада можно экстраполировать на Faces ADF (?).

Обновление : НЕТ, Лица АПД сильно отличаются от Тринидада.

Примечание перед началом

Кажется, что некоторые фреймворки работают очень хорошо (в отношении этого вида теста), то есть соотношение между визуальным изменением и объемом кода является приемлемым, но в некоторых конкретных случаях (компонентах) они «с треском» терпят неудачу. Эта статья пытается измерить компоненты с плохой производительностью.

RichFaces

 Консоль должна быть включена, настроена и открыта, как показано ранее.

  1.  Откройте демо этого дерева (тип переключателя Ajax)
  2.  Развернуть узел «Baccara»
  3.  Развернуть «Гранд Коллекция»
  4.  Свернуть «Гранд Коллекция»

 Как вы можете видеть, дочерние узлы под «Grand Collection» были удалены или скрыты (инспектор DOM FireBug говорит, что они были удалены).

<html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head></head><body><div xmlns:rich="http://richfaces.ajax4jsf.org/rich"><table border="0" cellpadding="0" cellspacing="0" class="rich-tree-node" id="j_id354:j_id355:0:1::j_id358"><tbody><tr id="j_id354:j_id355:0:1::j_id358:mainRow" onclick=" "><td class="rich-tree-node-handleicon rich-tree-h-ic-line-node" id="j_id354:j_id355:0:1::j_id358:handles" style="background-image:expression(this.parentNode.parentNode.parentNode.nextSibling.nextSibling ? 'url(/richfaces-demo/a4j/g/3_3_3.Finalorg.richfaces.renderkit.html.images.TreeLineNodeImage/DATB/eAH7!!!!72fXGBgYACWpBbU_.jsf)' : 'url(/richfaces-demo/a4j/g/3_3_3.Finalorg.richfaces.renderkit.html.images.TreeLineLastImage/DATB/eAH7!!!!72fXGBgYACWpBbU_.jsf)')"><div><a class="rich-tree-node-handle" href="#" id="j_id354:j_id355:0:1::j_id358:handle" onclick="var c = Tree.Item.findComponent(this); if (!c) return; c.fireExpansionEvent();;A4J.AJAX.Submit('j_id354',event,{'similarityGroupingId':'j_id354:j_id355:0:1::j_id358','parameters':{'j_id354:j_id355:0:1::j_id358AjaxExpanded':true,'j_id354:j_id355:0:1::j_id358NodeExpanded':'true'} } ); return false;"><img alt="" class="rich-tree-node-handleicon-collapsed" id="j_id354:j_id355:0:1::j_id358:handle:img:collapsed" src="/richfaces-demo/a4j/g/3_3_3.Finalorg.richfaces.renderkit.html.images.TreePlusImage/DATB/eAH7!!!!72fXGBgYACWpBbU_.jsf" style=";border:0" /><img alt="" class="rich-tree-node-handleicon-expanded" id="j_id354:j_id355:0:1::j_id358:handle:img:expanded" src="/richfaces-demo/a4j/g/3_3_3.Finalorg.richfaces.renderkit.html.images.TreeMinusImage/DATB/eAH7!!!!72fXGBgYACWpBbU_.jsf" style="display: none;;border:0" /></a></div></td><td class="rich-tree-node-icon rich-tree-h-ic-line-clp" id="j_id354:j_id355:0:1::j_id358:icon" rich:draggableoptions="{'parameters':{'dragSourceId':'j_id354:j_id355:0:1::j_id358','j_id354:j_id355:0:1::j_id358':'j_id354:j_id355:0:1::j_id358'} } " rich:dropzoneoptions="{} "><img alt="" class="rich-tree-h-ic-img" src="/richfaces-demo/images/tree/disc.gif" /></td><td class="rich-tree-node-text " id="j_id354:j_id355:0:1::j_id358:text" rich:highlightedclass="rich-tree-node-highlighted" rich:selectedclass="rich-tree-node-selected">Grand Collection</td></tr></tbody></table><div id="j_id354:j_id355:0:1::j_id358:childs" style="display: none;" class="rich-tree-node-children rich-tree-h-ic-line"></div><div id="j_id354:j_id355:script" class="rich-tree-h"><script type="text/javascript">$('j_id354:j_id355').component.refreshAfterAjax(['j_id354:j_id355:0:1::j_id358'] ,'')</script></div></div><div xmlns:rich="http://richfaces.ajax4jsf.org/rich"></div><div xmlns:rich="http://richfaces.ajax4jsf.org/rich"></div><div xmlns:rich="http://richfaces.ajax4jsf.org/rich"></div><div xmlns:rich="http://richfaces.ajax4jsf.org/rich"></div><meta name="Ajax-Update-Ids" content="j_id354:j_id355:0:1::j_id358,j_id354:j_id355:0:1::j_id358:childs,j_id354:j_id355:script" /><span id="ajax-view-state"><input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id3" /></span><meta id="Ajax-Response" name="Ajax-Response" content="true" /><meta name="Ajax-Update-Ids" content="j_id354:j_id355:0:1::j_id358,j_id354:j_id355:0:1::j_id358:childs,j_id354:j_id355:script" /><span id="ajax-view-state"><input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id3" /></span><meta id="Ajax-Response" name="Ajax-Response" content="true" /></body></html>

 Как вы видите, слишком много HTML-кода было отправлено для небольшого визуального изменения.

 Более серьезное снижение производительности:

  1.  Откройте демонстрационную таблицу расширенных данных
  2.  На «State Name» вставьте «Alaska» (вставьте имя из буфера обмена), отображается одна строка
  3.  Вставьте «Алабама» вместо «Аляска» (снова вставьте из буфера обмена, выбрав сначала Аляску), снова появится еще одна строка.

Ответ (HTML code) слишком велик, чтобы помещать его здесь, 3.474 байта. Если вы проверите результат, вы увидите полное переписывание таблицы, включая заголовок.

ICEfaces

  1. Откройте демо Календарь
  2. Нажмите в любой другой день

 Примерно так выглядит последний ответ AJAX:

Ответ (XML с метаданными) действительно большой, 6,452 байта , для простого изменения дня в соответствии с визуальными изменениями.

MyFaces / Тринидад

  1. Откройте эту таблицу Tree Demo
  2. Развернуть узел_0_0
  3. Разверните node_0_0_0 (показан узел node_0_0_0_)
  4. Свернуть узел_0_0_0 (скрывает / удаляет узел_0_0_0_0) 

Последний ответ AJAX слишком велик, чтобы помещать его здесь, 18,765 байт , потому что это полная перезапись компонента дерева.

Обновление : живой демо ADF Faces компонентов здесь , и они , кажется, работают отлично , как и ожидалось, то есть соотношение между кодом отсылается клиенту и визуальное изменение является «правильным» (несмотря на HTML разметка очень многословен в код , присланный для клиента это почти то же самое, что будет отображаться).

OpenFaces

  1.  Откройте демонстрацию Tree Table
  2.  Разверните «Re: Scalling the image»
  3.  Разверните нового ребенка «Re: Scalling a image»

 Последний ответ AJAX

<?xml version="1.0" encoding="UTF-8"?><ajaxResponse><head> </head><updatable id="subRows:2" type="portion" value="<tr><td><table cellspacing="0" cellpadding="0" class="o_cellWrapper"><tr><td class="o_treetable_indent"><div class="o_treetable_indent"></div></td><td class="o_treetable_indent"><div class="o_treetable_indent"></div></td><td class="o_treetable_indent"><div class="o_treetable_indent"></div></td><td class="o_treetable_indent"><div class="o_treetable_indent"></div></td><td><span class="treeTableText">Re: Scaling an image</span></td></tr></table></td><td><span class="treeTableText">Christian Smile</span></td><td><span class="treeTableText">Aug 3, 2007</span></td></tr>" data="{"structureMap":{"0":"1"}}" scripts="<script>O$.updateViewId('j_id3:j_id6');</script>" /></ajaxResponse>

 Этот код является очень разумным в соответствии с изменением (новый дочерний узел / строка таблицы).

 Тем не менее, некоторые компоненты терпят неудачу:

  1.  Откройте демонстрационную таблицу данных
  2.  Выберите «AK» в качестве «State», получив одну строку.
  3.  Замените на «AR», получая снова новую строку

  Последний результат AJAX слишком велик, 38.209 байт , потому что это полная перезапись таблицы, включая заголовки.

PrimeFaces

Ответы AJAX на все проверенные примеры были очень разумными. Это говорит о том, что в PrimeFaces отсутствует «компонент фильтрованной таблицы» или что-то подобное, ахиллесова пята других реализаций JSF.

Обновление : Как отмечает Cagatay Civici (один из отцов PrimeFaces), у PrimeFaces есть заполненная таблица , этот компонент прекрасно работает в отношении соотношения визуальных изменений / кода, отправляемого клиенту (попробуйте выполнить те же тесты, что и для платформ prvious).

Vaadin

Это первый не-JSF-фреймворк.

Откройте демо одиночного выбора дерева

Выберите «Dell OptiPlex GX240»

Нажмите кнопку «Применить» (никаких изменений не требуется)

Это последний ответ AJAX:

for(;;);[{"changes":[["change",{"format": "uidl","pid": "PID190"},["12",{"id": "PID190","immediate":true,"caption": "Hardware Inventory","selectmode": "single","nullselect":true,"v":{"action":"","selected":["2"],"expand":[],"collapse":[],"newitem":[]}},["node",{"caption": "Desktops","key": "1","expanded":true,"al":["1","2"]},["leaf",{"caption": "Dell OptiPlex GX240","key": "2","selected":true,"al":["1","2"]}],["leaf",{"caption": "Dell OptiPlex GX260","key": "3","al":["1","2"]}],["leaf",{"caption": "Dell OptiPlex GX280","key": "4","al":["1","2"]}]],["node",{"caption": "Monitors","key": "5","expanded":true,"al":["1","2"]},["leaf",{"caption": "Benq T190HD","key": "6","al":["1","2"]}],["leaf",{"caption": "Benq T220HD","key": "7","al":["1","2"]}],["leaf",{"caption": "Benq T240HD","key": "8","al":["1","2"]}]],["node",{"caption": "Laptops","key": "9","expanded":true,"al":["1","2"]},["leaf",{"caption": "IBM ThinkPad T40","key": "10","al":["1","2"]}],["leaf",{"caption": "IBM ThinkPad T43","key": "11","al":["1","2"]}],["leaf",{"caption": "IBM ThinkPad T60","key": "12","al":["1","2"]}]],["actions",{},["action",{"caption": "Add child item","key": "1"}],["action",{"caption": "Delete","key": "2"}]]]]], "meta" : {}, "resources" : {}, "locales":[]}]

Кажется, это не очень много, но если вы просматриваете код, все дерево снова перестраивается

ZK

Еще одна не-JSF-структура. В последних версиях ZK используется гибридный подход, большая часть визуальной логики находится в клиенте как компоненты JavaScript, сервер отправляет клиенту команды высокого уровня в клиентскую библиотеку высокого уровня (Vaadin не отличается). Я не нашел компонента, отправляющего слишком много кода от клиента (согласно визуальному изменению) в демоверсии ZK .

ItsNat

Последний изученный фреймворк, опять же фреймворк не JSF. 

В ItsNat сервер сохраняет то же состояние DOM, что и в клиенте, и с помощью событий мутации DOM любое изменение DOM на сервере автоматически генерирует JavaScript, необходимый для соответствующего обновления клиента.  

  1. Откройте демо
  2. Нажмите на обработчик папки «Core», дочерние узлы (11) скрыты.

Код результата события AJAX:

itsNatDoc.addNodeCache(["cn_10","cn_14","0,1,1,0,0",["cn_15","cn_16"]]);
itsNatDoc.setAttribute2("cn_14","src","img/tree/tree_node_collapse.gif");
itsNatDoc.setAttribute2(["cn_15","cn_17","1"],"src","img/tree/tree_folder_close.gif");
itsNatDoc.setAttribute2(["cn_16","cn_18","1,0",["cn_19"]],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_20","1"],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_21","2"],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_22","3"],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_23","4"],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_24","5"],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_25","6"],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_26","7"],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_27","8"],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_28","9"],"style","display:none");
itsNatDoc.setAttribute2(["cn_19","cn_29","10"],"style","display:none");

Без сюрпризов.

Еще один тест

Откройте это дерево демо

Нажмите «Вставить ребенка». Новый дочерний узел («Актеры») вставляется и добавляется новое сообщение журнала.

Код результата AJAX:

itsNatDoc.removeAttribute2(["cn_15","cn_39","13"],"style");
itsNatDoc.setInnerHTML2("cn_39","<div><input> click</div><div>javax.swing.event.TreeModelEvent 13802934 path [Grey's Anatomy, Actors] indices [ 4 ] children [ Actors ]</div>");
var child = itsNatDoc.doc.createElement("li");
itsNatDoc.setAttribute(child,"style","padding:1px;");
itsNatDoc.appendChild2(["cn_17","cn_40","0,1,1,1",["cn_41","cn_42"]],child);
itsNatDoc.setInnerHTML(child,"<span><img src=\"img/tree/tree_node_expanded.gif\"><img src=\"img/tree/tree_folder_open.gif\"><span><b>Label</b></span></span>\n <ul style=\"list-style-type: none;\"></ul>\n ");
itsNatDoc.setTextData2(["cn_40","cn_43","4,0,2,0",["cn_44","cn_45"]],null,"Actors");
itsNatDoc.setAttribute2(["cn_45","cn_46","0"],"style","display:none");
itsNatDoc.setAttribute2(["cn_45","cn_47","1"],"src","img/tree/gear.gif");

Снова никаких сюрпризов.

И победителем становится…

Победителя нет, потому что были протестированы только некоторые компоненты.

Сказав это, по-видимому, единственной реализацией JSF без серьезных потерь производительности является PrimeFaces.

В не-JSF-средах использование JS-библиотеки очень высокого уровня, как в Vaadin или ZK (PrimeFaces?), Очень сильно помогает уменьшить пропускную способность сети (несмотря на то, что некоторые компоненты в Vaadin имеют серьезные проблемы с производительностью), этого нельзя сказать для производительности клиента, потому что в ItsNat точный код JS DOM отправляется клиенту.

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