Статьи

Влияние на производительность сценариев в процессах

Мы часто видим людей, использующих сценарии (например, в служебной задаче, слушателе выполнения и т. Д.) Для различных целей. Использование сценариев против логики Java часто имеет смысл:

  • Это не должно быть упаковано в банку и помещено в путь к классу
  • Это делает определение процесса более понятным: нет необходимости просматривать разные файлы
  • Логика является частью определения процесса, что означает отсутствие хлопот, чтобы убедиться, что используется правильная версия логики

Однако важно также учитывать аспект производительности использования сценариев в определении процесса и сбалансировать эти требования с указанными выше преимуществами.

Два языка сценариев, которые мы обычно видим при использовании Activiti, — это Javascript и Groovy. Javascript поставляется в комплекте с JDK ( Rhino для JDK 6 и 7) и Nashorn для JDK 8, что позволяет легко подобрать. Для Groovy необходимо добавить механизм сценариев Groovy в classpath.

Но позвольте мне сказать вам, что я не фанат использования Javascript в качестве языка выбора сценариев, так как при переходе между версиями JDK происходят небольшие изменения (подробнее об этом читайте в предыдущем посте, здесь и здесь , и это те, которые были задокументировано…). Таким образом, это означает, что вы могли бы написать свою логику в один прекрасный день, и все это сработало, а на следующий день после обновления JDK все это не удалось. Я скорее трачу свое время на собственно кодирование.

Для проверки производительности я сделал очень маленький микробенчмарк:

Screenshot-2015-09-08-23.06.34-300x121

и где скрипт делал что-то глупое (смысл был в том, чтобы там были getVariable () и setVariable () и что-то еще, например, получение текущего дня):

1
2
3
var input = execution.getVariable(‘input’);
var today = new Date().getDay();
execution.setVariable(‘result’, input * today);

Тот же код в задаче службы Java:

1
2
3
4
5
6
7
8
9
public class MyDelegate implements JavaDelegate {
 
    @Override
    public void execute(DelegateExecution execution) throws Exception {
        Integer input = (Integer) execution.getVariable("input");
        int today = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
        execution.setVariable("result", input * today);
    }
}

и Groovy аналог:

1
2
3
def input = execution.getVariable('input');
int today = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
execution.setVariable('result', input * today);

Я запустил этот экземпляр процесса 10.000 раз и просто записал общее время выполнения, думаю, цифры говорят сами за себя:

  • JavaDelegate : 6255 мс
  • Groovy : 7248 мс
  • Javascript : 27314 мс

Используемая версия JDK была последней версией (1.8.0_60). В первый раз, когда я запускал тесты, я был на 1.8.0_20, и результаты Javascript были на 25% выше (я читал, что улучшения производительности были в JDK 1.8.0_40). Для Groovy я использовал версию 2.4.4 (которую вы должны использовать, так как старые версии имеют проблемы с безопасностью !)

Просто чтобы наглядно представить разницу между вариантами:

chart2-1024x679

Использование Groovy для языка сценариев кажется гораздо лучшим выбором по сравнению с использованием Javascript. Учтите, что это микробенчмарк для одного очень простого варианта использования. Но, учитывая наши проблемы в прошлом с обновлениями JDK, которые нарушают сценарии Javascript, и этот результат, очень трудно обосновать выбор Javascript по умолчанию.

ОБНОВЛЕНИЕ 11 СЕНТЯБРЯ ’15: Довольно много людей спрашивали меня, почему разница столь велика . Я предполагаю, что это потому, что движок javascript в JDK не является потокобезопасным и, следовательно, не может быть повторно использован или кэширован, что каждый раз приводит к дорогостоящей загрузке ScriptingEngine. Если вы посмотрите на http://docs.oracle.com/javase/7/docs/api/javax/script/ScriptEngineFactory.html , вы можете прочитать, что есть специальный параметр THREADING, который мы используем в Activiti: https : //github.com/Activiti/Activiti/blob/master/modules/activiti-engine/src/main/java/org/activiti/engine/impl/scripting/ScriptingEngines.java#L111, чтобы определить, может ли механизм сценариев быть кэшируются. Nashorn (и Rhino) возвращает здесь значение null, то есть его нельзя использовать для выполнения сценариев в нескольких потоках, т. Е. Каждому потоку нужен свой собственный экземпляр. Я могу только предположить, что ScriptEngineManager в JDK делает нечто подобное.