Java не отстой – вы просто неправильно его используете

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

10 страниц Wikis для настройки Dev среды отстой

Настройка новой среды разработки не должна быть более 3 шагов:

1.    Установите JDK

2.    Клонировать / проверить SCM repo

3.    Запустить настройку / начало приложения

Шутки в сторону. Оно и должно быть так легко. Современные инструменты для сборки, такие как Gradle и SBT имеют пусковые, которые вы можете вставить прямо в корневую исходного дерева, так что новые разработчики могут просто запустить ./gradlew or./activator  (для sbt). Сборка должна иметь все необходимое, чтобы поднять и запустить приложение - в том числе сервер. Самый простой способ сделать это, чтобы сделать бесконтейнерную обработку с такими вещами, как Play Framework  и Drop Wizard, но если вы застряли в контейнере, то вам нужно веб-приложение Webapp Runner. Одна из многих проблем с контейнерным подходом – это  очень высокая вероятность получения синдрома «это работает только на моей машине», потому что среды легко различаются, когда критическая зависимость существует вне области сборки и SCM. Сколько вики сохраняет изменения server.xml обновленными? Конфигурация на базе вики-это отличный способ причинить боль.

Насчет зависимостей услуг, таких как базы данных и внешних веб-служб - должны ли разработчики устанавливать эти вещи и настраивать их? Нет, если ваша сборка может сделать это за них. Умные системы сборки должны быть в состоянии сохранять необходимые услуги либо локально, либо на облаке. Докер возник как отличный способ управлять местными средами, которые являются точной копией производственной системы.

Если ваше приложение нуждается в реляционной базе данных, то используйте db в памяти, как hsql или облачные услуги, такие как Heroku PostgresRDSRedis Labs, и т.д. Однако риск с большинством баз данных в памяти в том, что они отличаются от того, что используется в производстве. JPA / Hibernate пытаются скрыть это, но иногда ошибки возникают из-за тонких различий. Так что лучше имитировать производственные услуги для разработчиков вплоть до версии базы данных. Базы данных на основе Java, такие как Neo4J, работают так же, в памяти и вне процесса минимизации рисков, а также делает проще установку новых сред развития. Внешние веб-службы должны иметь либо множество вариантов среды, которые могут быть использованы разработчиками или веб-услуги должны быть сведены на нет.

Неконгруентные среды развертывания отстой

Чтобы свести к минимуму риск при продвижении от разработки к производству, единственное, что должно измениться между каждой средой - это конфигурация. Развертываемый артефакт не должен меняться, когда он движется между средами. Непрерывные системы интеграции должны запускать те же сборки и тесты, с которыми работают разработчики. Делает ли система CI автоматическое развертывание на испытательной или промежуточной среде. Правильный релиз конвейерной обработки упрощает продвижение развертываемого артефакта от постановки к производству.

Я обычно поддерживал веб-приложение на Java, где процесс развертывания был таков:

1.    Создать WAR файл

2.    SCP WAR файл на сервер

3.    SSH на сервер

4.    Извлечь WAR файл

5.    Изменить web.xml файл, чтобы в нем содержалась информация о соединении новой базы данных  

6.    Перезапустить сервер

Это установка не самая худшая из тех, что я видел, но всегда была рискованная. Было бы гораздо лучше использовать переменные среды, так что единственное, что изменилось между средами,  это переменные. Переменные среды могут быть автоматически считываться приложением, так что артефакт остается точно таким же. В этом воспроизведении установки среда - супер легкая – просто установить переменные среды.

Серверы, которым нужно более чем 30 секунд для запуска - отстой

Для производительности разработчика так, чтобы расширение масштабов могло произойти мгновенно, серверы должны запускаться быстро. Если вашему серверу нужно более чем 30 секунд для старта, тогда разорвите приложение на более мелкие куски, приняв архитектуру Microservices. Безконтейнерность или  правило одного приложения-на-контейнер может реально помочь уменьшить время запуска. Если ваш контейнер занимает много времени для запуска, вы должны спросить себя: что де там за контейнерные услуги? Могут ли сервисы быть разбиты на отдельные приложения? Могут ли они быть удалены или выключены?

Если вам нужно вооружение, чтобы доказать своему руководству, что ваше время запуска убивает производительность вашей команды, то используйте секундомер на телефоне, чтобы подсчитать общее количество минут в день впустую, ожидая приложение запуститься. Бонусные баллы, если вы рассчитаете, сколько впустую денег потрачено на вас, на вашу команду, и ваш орг. Двойной бонус очков, если вы покажете график поражений "мы потратили много денег на этот сервер приложений" аргумент понесенных расходов.

Управляемые вручную зависимости отстой

Это отстой, если любая из ваших библиотечных зависимостей не управляется с помощью инструмента сборки. Вручную копирования файлов Jar в WEB-INF/lib  ужасно ошибочно. Это затрудняет соотношение файлов с версиями. Переходные зависимости адресованы ClassNotFound  ошибками. Зависимости являются хрупкими. Знать лицензии библиотек  трудно. Вытягивание источников с помощью IDE и JavaDocs для библиотек  - жестко.

Итак, сначала ... Используйте инструмент сборки. Не имеет значения выбрать вам Ant + Ivy, Maven, Gradle или sbt. Просто выберите один и используйте  его для автоматического вытягивания зависимости от Maven Maven или собственного сервера Artifactory / Nexus. С WebJars вы даже можете управлять вашей библиотекой зависимостей JavaScript и CSS. Затем автоматически откажитесь от SCM проверок, которые включают файлы Jar.

Неверсированные & Неопубликованные Библиотеки отстой

Предприятия, как правило, имеют много библиотек и услуг совместно по приложениям и командам. Чтобы сделать команды более продуктивными и для управления зависимостями эти библиотеки должны быть версиями и публиковать их на внутренние серверы артефактов, таких как Nexus и Artifactory. SNAPSHOT релизов следует избегать, поскольку они нарушают гарантии воспроизводимой сборки. Вместо этого, рассмотрите версии на основе вашей информации SCM. Например, sbt-git плагин по умолчанию имеет сборку версии для git хэш или если есть git тег для текущей позиции, то тег используется вместо этого. Это делает опубликованные релизы непреложными, так что потребители библиотек точно знают корреляцию между версией они используют и точки-в-времени в коде.

Долгая разработка/ циклы валидации в действительности  отстой

Миллиарды долларов в год, вероятно, потрачены на разработчиков только чтобы увидеть / проверить изменения. Современные веб-системы, как Play Framework  и инструменты, такие как JRebel могут значительно сократить время на проверку изменений. Если каждое изменение требует восстановления файла WAR или перезапуска контейнера, то вы тратите смешные суммы денег. Точно так же, выполнение тестов должно происходить непрерывно. Тестирование изменения кода (через перезагрузки обозревателя или запуска теста), не должно занимать много времени, больше чем на дополнительную компиляцию. Веб системы, которые отображают полезные компиляции и выполнения ошибки в браузере после обновления также очень полезны для уменьшения длинных циклов ручного тестирования.

Когда я работаю с игровыми приложениями, я постоянно восстанавливаю источник на файл, повторно запускаю тесты, и перезагружаю веб-страницы - все автоматически. Если ваши средства разработки и структуры не поддерживают этот вид процесса, то пришло время, чтобы это все модернизировать. Я использовал много структур Java на протяжении многих лет и Play Framework безусловно поддерживает самый последний и быстрый цикл изменений. Но если вы не можете переключиться на Play, JRebel с непрерывным плагином тестирования для Maven или Gradle тоже хорош.

Монолитные релизы отстой

Если вы работаете в НАСА , то иметь циклы выпуска дольше, чем две недели не резонно. Вполне вероятно, что причина, что у вас такие длинные циклы релиза потому, что где-то менеджер пытается уменьшить риск. Этот менеджер, вероятно, привык к водопадам, а затем перешел на Agile, но никогда больше ничего не менял. Так у вас есть короткие спринты, но код не достигает производства в течение нескольких месяцев, потому что это было бы слишком рискованно выпускать чаще. Правда в том, что непрерывная подача (CD) на самом деле снижает совокупный риск-релизов. Независимо от того, как часто вы выпускаете, вещи иногда ломаются. Но с малыми и более частыми релизами, фиксирующими  поломку,  гораздо проще. При монолитном релизе идущем на юг, проходит ваш уик-энд, неделя, или иногда месяц. Кроме того ... При релизах появляется хорошее чувство. Почему бы не делать это все время?

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

  • Упрощенное обеспечение и развертывание приложений: Каждый разработчик должен иметь возможность мгновенно обеспечить и развернуть новое приложение.
  • Микросервисы: логически группированные услуги / приложения в независимых развертываниях. Легче для команды, чтобы двигаться вперед в своем собственном темпе.
  • Роллбеки: Откат к предыдущей версии приложения также просто, как листать переключатель. Существует очевидная сторона развертывания для этого, но есть также некоторые политики, как правило, нужно идти на место вокруг изменения схемы.
  • Развязанные схемы и изменения кода: Когда изменения схемы и кода зависят друг от друга, откаты действительно трудны. Развязка обоих изолирует риск и позволяет вернуться к предыдущей версии приложения без выяснения, что нужно сделать в то же время изменения схемы.
  • Неизменные Развертывания: Знание корреляции между развертыванием и точным местом-во-времени в СКМ важно для устранения неполадок. Если вы SSH на сервер, и что-то измените на развернутой системе, вы значительно снизите вашу способность к репродукции и поймете проблему.
  • Развертывания нулевого вмешательства: Среда , в которую вы развертываете, должна владеть конфигурацией приложения. Если вам нужно редактировать файлы или выполнять другие действия вручную после развертывания, то ваш процесс хрупок. Развертывание не должно быть более, чем копирование испытанного артефакта к серверу и начало процесса.
  • Автоматическое развертывание: Обеспечение виртуальных серверов, добавление & удаление серверов за балансировкой, авто-запуск серверных процессов и перезапуск мертвых процессов должны быть автоматизированы.
  • Одноразовые Серверы: Не позволяйте вызвать Chaos Monkey. Серверы умирают.  Подготовьтесь к этому, имея без статусную архитектуру и эфемерные диски. Обеспечьте постоянное внешнее, стойкое хранилище данных.
  • Центральный Сервис Входа: Не используйте локальный диск для журналов, потому что это предотвращает одноразовость и усложняет поиск по нескольким серверам.

• Мониторинг и уведомления: Настройте автоматизированные здоровые проверки, мониторинг производительности и мониторинг логгов. Узнавайте раньше ваших пользователей, когда что-то идет не так.

Есть тонны деталей к этому всему, в которые я не буду вдаваться. Если вы хотели бы узнать о чем-то подробней в следующих постах, напишите мне в комментариях.

Залипающие сессии и состояние сервера отстой  

Залипающие сессии и состояние сервера обычно одни из лучших способов убить вашу производительность и устойчивость. Состояние сессии (в традиционном смысле Сервлет) усложняет постоянную подачу и масштабирует по горизонтали. Если вы хотите , чтобы кэш сессии использовал реальную систему кэш - то, что была разработано, чтобы иметь дело с использованием мульти-узла и неудачи. например Memcache, ehcache и т.д. В памяти кэш быстро, но трудно объявить недействительным в многоузловой среде и не долговечный в перезапуске - он имеет свое место, как расчетное/ полученное свойство, где недействительность и пересчет - это легко.

Веб-приложения должны двигаться неизменными к концу. Интерфейс, связанный со статусом, должен жить на клиенте (например, cookies, локальное хранение, а внутренняя память) и во внешних хранилищах данных (например, баз данных SQL / NoSQL, Memcached хранилища и распространенные кэш кластеры). Храните эти REST услуги 100% без статуса, либо статус - монстр буквально съест вас во сне.

Бесполезное  блокирование отстой

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

Большинство традиционных сетевых библиотек Java (сервлеты, JDBC, Apache HTTP и т.д.) блокируют. Таким образом, даже если соединение простаивает (например, когда соединение с базой данных ждет запрос, чтобы вернуться), поток по-прежнему выделяется. Модель блокирования ограничивает параллелизм, горизонтальную масштабируемость, а число одновременных подключений. Реактивная модель использует только потоки, когда они активны. В идеале ваше приложение Реактивно весь путь вниз к базовой сети событий. Когда приходит запрос, приходит поток, а затем, если запрос должен получить данные из другой системы,  поток, обрабатывающий запрос, может быть возвращен в пул при ожидании данных. После того как данные прибыли, поток может быть выделен на запрос, так что ответ может быть возвращен запрашивающему.

Java имеет большой фундамент для Reactive с Java NIO. Но, к сожалению, большинство традиционных фреймворков Java, драйверов баз данных, и HTTP клиентов, не используют его. К счастью, есть целый новый ряд реактивных библиотек и фреймворков, построенных на NIO и Netty  (большой библиотеки NIO). Например, Play Framework является полностью реактивным веб-фреймворком, который многие люди используют с реактивными библиотеками баз данных, таких как Reactive Mongo.

Быть Реактивным означает, что вы также должны иметь конструкцию для асинхронности. Традиционный способ сделать это в Java  - с анонимными внутренними классами, как:

public static F.Promise<Result> index() {

    F.Promise<WS.Response> jw = WS.url("http://www.jamesward.com").get();

    return jw.map(new F.Function<WS.Response, Result>() {

        public Result apply(WS.Response response) throws Throwable {

            return ok(response.getBody());

        }

    });

}

Java 8 обеспечивает гораздо более краткий синтаксис для асинхронных операций с Лямбдами. То же самое Реактивный обработчик выше Java 8 & Лямбда это:

public static F.Promise<Result> foo() {

    F.Promise<WS.Response> jw = WS.url("http://www.jamesward.com").get();

    return jw.map(response -> ok(response.getBody()));

}

Если ваше приложение делает вещи параллельно и / или управляет пуш подключениями, то вы действительно должны быть реактивными. Проверьте мою презентацию Строительство Реактивных приложений, если вы хотите погрузиться глубже во все это.

Язык Java типа отстой

У языка Java много великих аспектов, но из-за его массивного принятия и желания своих корпоративных пользователей очень постепенного изменения, язык показывает свой возраст. К счастью, есть тонны других вариантов, которые работают на JVM. Вот краткий список наиболее интересных вариантов и моих положительных и отрицательных мнений по некоторым из них:

  • Scala

·         Супер

  • Вероятно, наиболее широко принятая альтернативная формулировка на JVM
  • Хорошо работает с Reactive и нуждами больших данных
  • Зрелая экосистема для библиотеки, фреймфорков, поддержки и т.д.
  • Хорошо
  • Java совместимость велика, но часто бесполезна, так как библиотеки Java строятся не для реактивных и Scala идиом
  • Современные концепции программирования с очень мощным и гибкым языком
  • гибкость языка приводит к значительно различным способам написания Scala, в ущерб универсальной читаемости
  • Огромное время на обучение из-за большого количества функций
  • Плохо
  • Groovy

·         Супер

  • Большая экосистема библиотек, фреймворков, поддержки и т.д.
  • Простой язык с несколькими очень полезными функциями
  • Хорошо
  • Взаимодействие с Java работает и чувствует себя довольно естественно
  • Плохо
  • Я предпочитаю хороший вывод типа (как Scala) против Groovy динамичной и дополнительной статической типизации
  • Clojure

·         Супер

  • Элегантность Lisp на JVM
  • Зрелая экосистема для библиотеки, фреймфорков, поддержки и т.д.
  • Целевой JavaScript, кажется, хорош, но не основной
  • Плохо
  • Отсутствие некоторых конструкций делает управление большого объема кода сложным
  • Динамический ввод
  • Kotlin

·         Супер

  • Взаимодействие с Java кажется естественным
  • Целевой JavaScript первоклассный

·         Хорошо

  • IDE и инструменты сборки достойны, но незрелы
  • Современные языковые особенности, которые не ошеломляют
  • Плохо
  •  Неопределенность, где он будет в течение 5 лет – достигнет ли он критической массы?

Начиная с нового / зеленого проекта, было бы легко попробовать новый язык, но большинство предприятий не часто это делают. Для существующих проектов некоторые структуры и инструменты поддерживают существующий смешанный Java с альтернативными языками для JVM лучше, чем другие. Play Framework / sbt я использовал для этого, но я уверен, что есть и другие, которые делают это также хорошо. По крайней мере, написание только новых тестов в альтернативном языке JVM может быть отличным местом для начала экспериментирования.

Лямбды Java 8 являются хорошим обновлением для Java языка. Лямбды действительно помогают снизить шаблонность и хорошо вписываются в реактивную модель. Но есть еще много других областей, где язык отстает. С того момента, как я попробовал Scala , я не могу жить без нескольких вещей, которые до сих пор отсутствуют в Java: Тип Вывода, сопоставление образцов, Case классы, строковая интерполяция, и неизменность. Также очень приятно иметь Option , и параллельные конструкции запеченные в основну и экосистемы библиотеки.

Проверка в реальных условиях

Если вы работаете в типичном предприятии то, возможно, вам повезло, и вы уже делаете большинство из описанного. Шокирующе, как это может показаться для некоторых из нас, это действительно редкость. Большинство из вас, вероятно, читаете это и вам грустно, потому что перемещение предприятия ко многому из этого материала действительно трудно. Как физика говорит нам, что гораздо труднее перемещать большие вещи, чем маленькие. Но не отчаивайтесь! Я видел ряд тяжеловесных предприятий медленно выползающих из Java отсталых частей. Walmart Canada недавно переехал на Play Framework!

Моя рекомендация  - выбрать одну из этих отсталых вещей и поставить цель -  исправить ее в следующем году. Часто для этого требуется покупка с согласием управления, которые воспринимают покупки жестко. Вот мое предложение ... Проведите пару вечеров или выходные на реализацию одного из этих пунктов. Затем покажите своему менеджеру, что вы сделали в свое свободное время (чтобы показать, насколько вы заботитесь), а затем позволить им взять кредит на удивительный новый предмет, который вы предложили. Работает каждый раз. И если это не проходит, то существует множество хорошо оплачиваемых стартапов, которые уже делают все эти вещи.

И последнее… Почитайте  Двенадцать факторов  приложения  –которые вдохновили меня на эту статью.

Перевод выполнен с английского