Статьи

Spring Environment предназначен только для кода инициализации

Начиная с версии 3.1, среда Spring предлагает абстракцию к нескольким различным источникам, с помощью которых вы можете легко настроить свое приложение: Environment .

В этой статье я опишу микропроцессор, который я выполнил, чтобы доказать, что, хотя это удобный API, если вы используете Spring в своем приложении, он может привести к снижению производительности, из-за которого вы не должны использовать его вне кода инициализации.

Как это работает

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

Из документации:

Свойства играют важную роль практически во всех приложениях и могут быть получены из различных источников: файлов свойств, системных свойств JVM, системных переменных среды, JNDI, параметров контекста сервлета, специальных объектов свойств, карт и т. Д. Роль объекта среды по отношению к свойствам заключается в предоставлении пользователю удобного служебного интерфейса для настройки источников свойств и разрешения свойств из них.

Таким образом, вы можете использовать Environment чтобы иметь общий интерфейс к свойствам, предоставляемым с различными стратегиями, используя простой вызов getProperty для доступа к требуемому значению. Посмотрите на следующий Groovy код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
@Component
    public class Greeter {
 
        private Environment environment
 
        @Autowired
        public Greeter greeter(Environment environment){
            this.environment = environment
        }
 
        def nickName(user) {
            environment.getProperty("user") // here be magic
        }
 
        def greet(user) {
            def nick = nickName(user)
            if (name == null) println "Hi, ${user}!"
                         else println "Hi, ${nick}!"
        }
 
    }

Теперь я могу указать псевдонимы в файле свойств, чтобы я мог приветствовать знакомых пользователей с более знакомым псевдонимом, при этом имея возможность приветствовать также пользователей, которым не дали ни одного псевдонима. Аккуратно, но как насчет производительности?

Теперь я могу указать псевдонимы в файле свойств, чтобы я мог приветствовать знакомых пользователей с более знакомым псевдонимом, при этом имея возможность приветствовать также пользователей, которым не дали ни одного псевдонима. Аккуратно, но как насчет производительности?

Скрытое Исключение

Я попал в это упражнение во время отладки нескольких медленных страниц на веб-сайте, над которым я работаю: целевые страницы основного сайта KLM. Хотя производительность в целом удовлетворительная, две страницы постоянно давали время ответа выше второго. Определенно слишком много .

В нашем коде мы переводили названия некоторых стран в запрашиваемые ключи для внешних служб. Нам также нужно было переопределить простой алгоритм преобразования с очень конкретными исключениями из правила. Фактический код был в значительной степени похож на приведенный выше Greeter.greet(user) , и сессия Flight Recorder в итоге предоставила нам узкое место в производительности (нажмите, чтобы открыть):

исключения

Для обновления 12 страниц мы молча выдавали 140k + исключений. И исключения являются sloooooow , даже если вы просто создаете их.

Глядя на верхнее исключение, было довольно легко понять, что происходит: Environment проверяет, определено ли запрошенное свойство в текущем контексте JNDI. Но если имя не найдено, NameNotFoundException . В нашем конкретном случае мы использовали поиск свойств для исключительных случаев, что означает, что подавляющее большинство случаев приводило к исключению.

Микро тест

Я собрал микропроцессор для оценки потенциального прироста производительности исходной стратегии поиска свойств по сравнению с более простым, когда соответствующие свойства загружаются во время создания класса. Я использовал Java Microbenchmark Harness , который делает невероятную работу по упрощению микропроцессорных тестов на JVM: JIT, прогрев, загрузка классов, все позаботились о вас, и вы можете просто пойти дальше и проверить свой код. Вот результаты (чем выше цифры, тем лучше):

[Поиск недвижимости за вызов]

Результат: 28917,876? (99,9%) 183,630 операций / с [Среднее] Статистика: (min, avg, max) = (25688.067, 28917.876, 30976.876), stdev = 777.500
Доверительный интервал (99,9%): [28734.246, 29101.505]


[Загрузка имущества при строительстве класса]

Результат: 159062,900? (99,9%) 1013,309 операций / с [Среднее] Статистика: (min, avg, max) = (138707.926, 159062.900, 177183.549), stdev = 4290.413
Доверительный интервал (99,9%): [158049.591, 160076.209]

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

Выводы

Я не большой поклонник Spring, но если вы используете его, класс Environment — это очень простой интерфейс для конфигурации вашего приложения. Но если вы не используете JNDI в качестве основного хранилища свойств конфигурации, его характеристики производительности делают его отличным инструментом только в том случае, если вы используете его в коде инициализации, а не во время онлайновой обработки запросов.

Ссылка: Spring Environment предназначен для кода инициализации только от нашего партнера по JCG Карло Скиоллы из блога Skuro .