Улучшение производительности JSF
Я работаю над WebApp с использованием JSF 2.2 + Primefaces... проект быстро развивается, и первый тест производительности был очень плохим (из-за моих слабых знаний о JSF LifeCylce в сочетании с нефункциональными требованиями - то есть с полностью ajax-сайтом), затем мы могли бы немного улучшить, но результаты все же не такие, как ожидалось. (У нас есть время от 300 до 1500 мс в зависимости от действия, идея в том, чтобы иметь производительность около 500 мс, отдача или взятие). В основном фаза восстановления и отклика рендеринга являются потребителями времени (в других случаях затраченное время бесполезно). Для некоторых действий Invoke Aplication также требуется время (из-за вызовов БД).
После прочтения большого количества статей в Интернете было принято во внимание множество замечательных советов (многие, конечно, из stackru), таких как:
- Улучшение запросов к БД
у нас есть несколько сложных, которые выполняются с двумя запросами Hibernate Criterias, поэтому у нас есть работа здесь. (может быть, использовать чисто SQL-запросы, а на сложные использовать подзапросы?)
- Никогда не определяю бизнес-логику для получателей Бина
Понял!
- Установка соответствующих областей действия для хранения бобов и хранение на них ничего, кроме необходимых вещей
У нас есть полный ajax-сайт, поэтому View Scoped для нас почти как Session Scoped, мы используем SessionBeans как своего рода "кэш" для хранения ключевых данных, которые мы не хотим получать из БД каждый раз + определяем бизнес-логику Вот.
- Выбор правильного метода сохранения состояния JSF - Client Vs Server
Для этого мне нужно еще немного изучить, проверить возможности с их плюсами и минусами, а затем проверить производительность на каждом из них.
Пока очень ясно, теперь есть несколько дополнительных советов, в которых у меня есть некоторые сомнения.
- Используйте как можно больше ванильного HTML, и желательно использовать h: тэги, а не p: тэги
Простой HTML понятен и имеет смысл, теперь между h: и p: сколько это стоит? Например.
<p:commandButton value="Value"
styleClass="class"
actionListener="#{myBean.doStuff()}"
ajax="true" process="@form" update="@form"
onsuccess="jsFunction()" />
против
<h:commandButton value="Value"
styleClass="class"
actionListener="#{myBean.doStuff()}" >
<f:ajax execute="@form" render="@form" event="onclick" />
</h:commandButton>
или же
<ui:fragment... vs <p:fragment...
или же
<p:outputLabel value="#{myBean.value}" rendered="#{myBean.shouldRender}" />
против
<ui:fragment rendered="#{myBean.shouldRender}">
<label>#{myBean.value}</label>
</ui:fragment>
Я уже некоторое время использую смесь Primefaces с тегами Jsf и некоторым простым HTML здесь и там (в основном PF из-за особенностей их компонентов). Теперь я понимаю, что простой HTML всегда будет быстрее, но между JSF и другим Framework? Если я пойду на это, его изменение займет много времени, и мне не хотелось бы, чтобы результат знал, что это вообще не имеет значения.
- Пользовательские теги Facelets против составных компонентов
Я думаю, что здесь ключ. Все еще есть некоторые сомнения по поводу их различий, в реализации обоих, CC довольно просты и гибки в использовании, но имеют недостаток в том, что они полностью включены в ViewTree и JSF повторно генерирует для каждого запроса (если я не ошибаюсь) в то время как пользовательские теги кажутся немного более сложными в использовании (не так много), но имеют то преимущество, что в ViewTree включено только то, что фактически отображается, и ничего более, что делает RESTORE VIEW менее трудоемким. У нас есть несколько составных компонентов и нет тегов Facelets, поэтому здесь будет много работы. Я до сих пор не нашел хорошую статью, объясняющую различия между ними, когда один должен использоваться, а когда другой (прочитал это для входных данных, сообщения используют TAGS и для более сложных вещей CC). Если идея состоит в том, чтобы предпочесть теги против CC, то в каком случае у меня не было бы выбора, вместо использования CC? Можно ли использовать пользовательские теги внутри CC, чтобы облегчить их обработку JSF?
Я собираюсь отправиться в путешествие по изменению проекта отверстия, чтобы получить лучшую производительность, которая займет у меня несколько дней, идея состоит в том, чтобы получить лучшие результаты в этот раз; поэтому каждый совет, совет и предложение очень приветствуются! Спасибо за ваше время, ребята!
6 ответов
Есть несколько вещей, которые вы можете сделать, чтобы улучшить производительность ваших экранов
- Фильтр GZIP значительно сократит время начальной загрузки. Он сжимает содержимое страницы при передаче клиентскому браузеру. См. /questions/23553990/kakov-nailuchshij-sposob-gzip-stranits-veb-prilozheniya-jsf-seam/23554008#23554008
- Вы можете дополнительно реализовать cacheFilter для приведения производительности ваших экранов в соответствие с пользовательским интерфейсом на основе JavaScript. Это позволит кэшировать статическое содержимое вашего экрана, например значки, изображения, таблицы стилей, сценарии JavaScript и т. Д. Вы можете контролировать, что кэшировать, а что исключать. См. /questions/5768340/filtr-staticheskih-resursov-jsf-cache/5768370#5768370
- Для компонентов пользовательского интерфейса на стороне клиента вы можете использовать Primefaces, который является пользовательским интерфейсом на основе JQuery.
Как проверить, использует ли мой экран gzip и кеш
Чтобы увидеть, что ваш контент уже использует gzip и кеш, в браузере Google Chrome -> щелкните правой кнопкой мыши на экране -> осмотрите -> щелкните вкладку сети -> обновите экран. Нажмите на изображения, значки, таблицы стилей и посмотрите, видите ли вы следующее в заголовке ответа
Cache-Control:max-age=2592000
если статус элемента 304 (поступающий из кеша)
Content-Encoding:gzip
если статус элемента 200
Я столкнулся с той же проблемой полгода назад. Большую часть времени проводят в фазах RestoreView и RenderResponse. Если ваша проблема такая же, то вы ничего не можете сделать:
- использование
partialSubmit="true"
как можно больше - Уменьшить количество компонентов
- Не используйте составные компоненты много
В моем конкретном случае у меня было много динамического HTML без элементов управления вводом, и я использовал составной компонент в качестве шаблона для условного рендеринга. Для увеличения производительности я использовал http://freemarker.org/ для создания динамического HTML.
PS. Мое мнение не в том, чтобы использовать JSF, если у вас есть сложные страницы со многими компонентами. Я думаю, что Гобелен более оптимизирован для этого. Он не воссоздает компоненты при каждом запросе, как это делает JSF.
RenderResponse занимает 98% вашего времени, потому что... в основном это делает JSF. Я был бы обеспокоен, если бы JSF посвятил 50% времени обработки запроса, чтобы сделать что-то кроме обработки ответа! Тебе нужно копать глубже. Рассмотрим этот пример.
Скажем, у вас есть страница, которая отображает таблицу. Вы используете компонент p: dataTable PrimeFaces, например так:
<h:html>
<h:body>
<p:dataTable value="#{myBean.data}">
</p:dataTable>
</h:body>
</h:html>
И у вас есть это @ManagedBean
:
@ManagedBean(name="myBean")
public class MyBean {
public List<MyObject> getData() {
return // fetch list from database;
}
}
Каждый раз getData()
называется, есть доступ к базе данных, и это время добавляет к глобальному времени фазы рендеринга. Кроме того, рендеринг p: dataTable может потребовать много вызовов getData.
Опять же, вам нужно углубиться в свое приложение и определить, где именно, на этапе рендеринга, тратится время. По моему опыту, наивные реализации @ManagedBean
s имеют много кода для запросов к базе данных в тех же геттерах, на которые есть ссылки в xhtml
с, как в моем предыдущем примере. JSF (или такой компонент, как PrimeFaces p:dataTable) может вызывать эти методы получения много раз при отображении одного HTML-файла. Обычной стратегией является выборка данных только один раз с использованием события preRenderView, например:
В вашем xhtml:
<h:html>
<f:metadata>
<f:event type="preRenderView" listener="#{myBean.fetchDAta}"/>
</f:metadata>
<h:body>
<p:dataTable value="#{myBean.data}">
</p:dataTable>
</h:body>
</h:html>
В вашем управляемом бобе:
@ManagedBean(name="myBean")
public class MyBean {
private List<MyObject> data;
public void fetchData() {
data = // fetch data from database.
}
public List<MyObject> getData() {
return data;
}
}
Я не уверен, есть ли большая разница между "p" и "h", так как оба они отображаются как html к концу. Я вижу, вы часто используете атрибут процесса. Вы можете указать конкретные идентификаторы в этом атрибуте, чтобы повысить производительность, отправляя только части формы. Еще одна идея для увеличения производительности - использовать кеш простых чисел. http://www.primefaces.org/showcase/ui/misc/cache.xhtml для нединамического контента, который вы хотите отображать очень часто (см. документацию). Я предполагаю, что "кеш" будет пытаться получить представление дерева из кеша, а не восстанавливать его.
Если вы хотите создать jsf-приложение с другим фреймворком, ваше приложение будет плохим из-за производительности для пользователей. Потому что приложение Jsf стало 6-й фазой, которая слишком долго ждет фазы рендеринга. Таким образом, вы можете использовать микро сервисы для бэкэнда и использовать javascript для фронтэнда. В результате вы можете сделать такое хорошее приложение, как производительность и архитектура. вы можете посмотреть весенние загрузочные микро сервисы для приложений Java ee
Я прочитал интересную статью о повышении производительности JSF. На этапе восстановления представления JSF восстанавливает дерево компонентов, включая привязку данных к таблицам данных, вызывая ненужные выборки данных из базы данных. В статье рассматривается один из способов избежать дополнительной работы по извлечению данных.
Я сообщаю о возможном решении здесь, и полную статью для справки.
Решение проблемы состоит в том, чтобы вообще пропустить выборку данных. Он не нужен, он даже не используется, и это даже не правильные данные (не волнуйтесь, я объясню это позже). В объекте FacesContext есть метод getRenderResponse(), который возвращает true, если был вызван метод ответа рендеринга, и мы находимся в фазе рендеринга. Пока мы не отрисовываем страницу, нам на самом деле не нужны наши данные, поэтому мы загружаем их, только если этот метод возвращает true.
Например:
//generates the list of data, think of this as an expensive DB operation
private List<Integer> generateData() {
if (!FacesContext.getCurrentInstance().getRenderResponse()) {
return null;
}
System.out.println("Generating Data starting from "+pageNumber);
...
...
...
}
Таким образом, мы получаем только одну выборку данных на запрос страницы даже при обратной передаче, потому что мы не загружаем данные до тех пор, пока они нам действительно не понадобятся в ответе рендеринга, и если вы посмотрите на время, наши страницы загружаются в два раза быстрее, так как мы только выбираем половина данных.
Итак, вы можете увеличить скорость ваших JSF-страниц, загружая данные только тогда, когда они вам действительно нужны, я полагаю, что это ленивая и ленивая загрузка данных.
Удвойте скорость (некоторые из) ваших страниц JSF (и избегайте ошибок тоже)
Я надеюсь, что это полезно для других.