Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 18048 сообщений
Cообщения с меткой

java - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

Обзор IntelliJ IDEA 2017.1: Java 9, Kotlin 1.1, Spring, Gradle, JavaScript, Go и многое другое

Среда, 22 Марта 2017 г. 20:39 (ссылка)

Привет Хабр!



Спешу поделиться хорошей новостью. Работа над IntelliJ IDEA 2017.1 закончена, и новую версию IDE можно наконец скачать и попробовать. В этом обновлении исправлены многие важные баги а также появились улучшения для многих поддерживаемых языков, фреймворков и встроенных инструментов. Ниже краткий обзор наиболее важных изменений.











Java 9: полностью поддерживаются последние билды JDK 9, работает помощь при импорте проекта и подсказки при редактировании деклараций модулей. Встроенные инспекции позволяют валидировать декларации модулей и корректировать зависимости проекта с помощью quick-fixes.







Java 8: улучшены quick-fixes для переноса циклов for в вызовы Stream API — теперь поддерживаются более сложные случаи. Также добавлен quick-fix, превращающий вызовы Stream API обратно в циклы for, что удобно для отладки или изучения кода.







Отладчик с поддержкой асинхронного кода: появились stacktraces для асинхронного кода — данные из места вызова асинхронного кода подставляются в stracktrace, связанный с исполнением этого кода. Это позволяет сосредоточиться на отлаживаемом коде. Улучшенная команда Smart Step Into теперь также поддерживает асинхронный код и лямбда-выражения, выполняемые в других потоках.







Улучшена поддержка VCS: на панель Log для Git и Mercurial добавлены новые параметры отображения, в диалоговом окне Diff появился параметр Ignore imports and formatting, а функция File History для Git теперь работает быстрее. Также в окно Branches для Git добавлены избранные ветки и speed search







Поиск: диалоговое окно Find in Path, в которое ранее уже была добавлена вкладка Preview, полностью переделано — теперь сразу отображаются мгновенные результаты. Что еще важнее, простым нажатием клавиши Enter любой выбранный результат теперь можно открыть в редакторе.







Spring: обновление Spring Testing принесло поддержку Spring Boot 1.4.3 и будущей версии Spring 5.0. Инструменты Spring Data обновлены до версии 2.0 (в т. ч. MongoDB, Redis, Solr, KeyValue, Gemfire, Apache Cassandra, REST, Neo4j, Couchbase и Elasticsearch). В окне инструмента Spring появилась новая вкладка Data с удобной навигацией по репозиториям.







Gradle: поддержка Composite Builds усовершенствована — теперь IDE автоматически находит includeBuild в конфигурации Gradle и соответственно настраивает проект.



Kotlin 1.1: среди прочего в новой версии этого языка для JVM появились courtutines — новый неблокирующий асинхронный API. Также полностью поддерживается компиляция в JavaScript. Это значит, что строки, коллекции, последовательности, массивы и другие стандартные API можно использовать в приложениях на JavaScript.



Scala: новый Scala плагин предлагает обновленный и более удобный Project Wizard, много улучшений поддержки SBT, дополнительные подсказки для Akka, и новый REPL режим в Worksheet.



JavaScript: реализована первоклассная поддержка Vue.js, множество новых настроек Code Style для JavaScript и TypeScript, более быстрые и надежные интеграции с Angular, ESLint и TSLint (в т. ч. поддержка языковых сервисов и quick-fixes, использующих TSLint). Кроме того, редактировать зависимости проекта в package.json стало проще благодаря автодополнению имен и версий пакетов, тесты Mocha и Jest стало удобнее запускать, а на иконке Run в гаттере теперь отображается состояние теста.









Инструменты для баз данных: IntelliJ IDEA теперь позволяет переносить схемы таблиц и данные между любыми базами данных (да, даже из MySQL в Microsoft SQL Server и обратно).



Эмодзи: редактор теперь поддерживает символы Unicode для эмодзи (например, в комментариях).



Android Studio 2.2.2: в новую версию включены все изменения из Android Studio 2.2.2.



Docker: плагин Docker теперь поддерживает Docker for Mac и работает через «unix://».



Windows: 64-разрядный установщик для Windows позволяет выделить IntelliJ IDEA больше оперативной памяти.



Go: Gogland, новая Go IDE анонсированная несколько месяцев ранее стала также плагином для IntelliJ IDEA Ultimate.



Подробнее об IntelliJ IDEA 2017.1 можно узнать на странице What’s New.



P.S. Также вам может быть интересно попробовать приложение Toolbox App — с его помощью удобно устанавливать и обновлять IDE и открывать проекты. Toolbox App позволяет быть в курсе последних релизов и, если что-то пойдет не так, откатить установку до стабильной версии.



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



Программируйте с удовольствием!

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/324578/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

JNI Получение и Подключение к JVM в Delphi

Среда, 22 Марта 2017 г. 15:39 (ссылка)

Всем доброго времени суток!

Сегодня разберем пример как получить загруженную JVM и подключиться к ней. Нужно нам это для того что выполнить внутри JVM некий код.

И так приступим:



Создаем новый проект DLL. Добавим Process Attach:



procedure DllMain(dwReason: LongWord);
begin
case dwReason of
DLL_PROCESS_ATTACH:
begin
//**************************
end;
DLL_PROCESS_DETACH:
begin
//***************************
end;
end;
end;

begin
DllProc := @DllMain;
DllProc(DLL_PROCESS_ATTACH);
end.




Отлично добавили. Далее нас потребуется в Uses добавить компонент JNI:



uses
System.SysUtils,
System.Classes,
windows,
JNI;




А теперь давайте реализуем поиск и подключение к JVM, Для этого в DllMain добавим переменные:



var
I: Integer;
JVMArray: array of PJavaVM;
NumberOfVMs: JSize;
JNIEnv: PJNIEnv;
GetCreatedJavaVMs: TJNI_GetCreatedJavaVMs;
const
BufferSize = 128;




Далее в DLL_PROCESS_ATTACH: реализуем поиск и подключение загруженной JVM



begin
try
GetCreatedJavaVMs := GetProcAddress(GetModuleHandle('jvm.dll'), 'JNI_GetCreatedJavaVMs');
SetLength(JVMArray, BufferSize);
GetCreatedJavaVMs(@JVMArray[0], BufferSize, @NumberOfVMs);
except
Exit;
end;
if NumberOfVMs > 0 then
begin
for I := 0 to NumberOfVMs - 1 do
begin
JVMArray[I]^.GetEnv(JVMArray[I], @JNIEnv, JNI_VERSION_1_8);
JVMArray[I]^.AttachCurrentThread(JVMArray[I], @JNIEnv, Nil);
end;
end
else
begin
Exit;
end;




Что же тут происходит. Для начала нам нужно получить адрес функции JNI_GetCreatedJavaVMs из jvm.dll. Затем установим длину буфера. Затем используем функцию GetCreatedJavaVMs для получения всех загруженных JVM. Ну а дальше просто отсев в буфере пока не останется именно та загруженная JVM и подключаемся к ней AttachCurrentThread.

И так мы нашли и подключились к загруженной JVM. Теперь можно использовать любой код внутри JVM после строчки AttachCurrentThread.

Теперь просто скомпилируем DLL и любым инжектором DLL засунем её в Java процесс.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/324594/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] JVM не такая тяжёлая

Среда, 22 Марта 2017 г. 13:11 (ссылка)

В основном ответ на то, что Clojure — это JVM. Мол, эта хрень такая тяжёлая.



Это появилось на канале ZA Tech в группе Slack несколько недель назад. Во время некоторых выступлений по Clojure спикеры делали такое замечание снова и снова.



По этому поводу я выступил в Slack. Теперь запишу для более широкого чтения и обсуждения.



Предисловие



Я тоже раньше думал, что JVM тяжёлая. Это было в начале 2000-х, в сравнении с PHP. Там были и другие тяжеловесы, вроде .NET и ColdFusion. Были и более лёгкие альтернативы вроде Perl и Python, но я тогда сидел на Windows, так что ActivePerl и ActivePython тоже были несколько тяжеловаты.



Впервые я преодолел свой «страх» перед JVM, когда развернул небольшое производственное приложение JRuby на Heroku. Этот маленький монстрик должен был выполнять только одну задачу в день. Он генерировал ряд PDF'ов, потом загружал их на iSign (сейчас не функционирует) для хранения и распространения. Сам iSign был классическим приложением Rails, которое хостилось на трёх AMI. Этот маленький динозавр на стоковом JVM (за исключением -server -Xmx=512M) производил PDF'ки так быстро, что он буквально убивал трёхнодовый кластер при каждом запуске.



Я по-прежнему думал, что он немного тяжеловат в работе, но влюбился в этого гадкого утёнка.



Я более-менее следил за событиями в разработке JRuby и историями успеха и замечательно провёл время на Rubyfuza 2015 с Чарльзом Наттером. Это меня настолько вдохновило, что я стал открывать пулл-реквесты в проектах Ruby, которые просто запускают их тесты на JRuby. На том одном и закончил, к моему стыду.



И к 2016 году



В ноябре 2016 года я попытался с нуля написать приложение Rails. Впервые за несколько месяцев программировал на Ruby на своей машине. Обновление brew upgrade врезалось в rbenv и поэтому выбросило все установленные версии Ruby, а я даже не заметил.



Мне предстояла презентация по WebSockets на Jozi.rb.



В начале выступления хотелось поиграться с репозиторием React on Rails, просто для общего ощущения совместного использования React и Rails. Я уже пару месяцев использовал re-frame и был уверен, что смогу вытянуть его голым React'ом.



Поезд сошёл с рельсов, и очень зрелищно.



Для клонирования и запуска одного шаблонного приложения требовалось обновить XCode, обновить инструменты командной строки для XCode (в сумме более 6 ГБ), установить новую версию Ruby и Bundler, а затем связать инсталляцию в шаблонном приложении… Всё просто, верно? Как и у большинства приложений Rails, у этого шаблонного приложения где-то в графе зависимостей была зависимость от libv8, и только это прибавляет более 1 ГБ в размере.



Всё упражнение заняло несколько часов.



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



Опять то же самое, нужно обновить nvm, установить приемлемую версию node, установить ember-cli, сгенерировать приложение и установить зависимости через npm и bower.



Я немного поигрался и признал своё поражение, а вместо этого поделился своим опытом с коллегами. Это было унизительно, по-настоящему унизительно. Я чувствовал себя чужаком в мире, частью которого был так долго.



Вернёмся к заявлению, что JVM тяжёлая.



Как будем взвешивать?




  • Размер JDK при скачивании слишком большой?

  • Он использует много ресурсов в работе?

  • Библиотеки занимают много места на диске?

  • Развёртывание слишком тяжёлое?

  • Она затормаживает вас день ко дню?



Вот некоторые вопросы, которые помогают преодолеть наши эмоциональные барьеры насчёт JVM.



Так что давайте займёмся ими и посмотрим, что выйдет на самом деле.



Первоначальный объём действительно настолько велик?



Эта «тяжесть» JVM — чистый FUD, начиная с якобы большого объёма первоначальной установки. Вы сравниваете примерно 200 МБ скачивания JDK с примерно 15 МБ скачивания Node или Ruby. Но это только начало. И для Node, и для Ruby в системе нужен нужен компилятор C, только он один — сотни мегабайт. Хуже того, вероятно вам понадобится компилятор в продакшне!



Куча раздувающей нагрузки и для Node, и для Ruby прячется от вас такими маленькими последовательными шагами. Если вы остановитесь и посчитаете объём, не говоря уже о потраченном времени, то 200 МБ окажутся гораздо более оптимальным вариантом.



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







JVM так тяжела в работе?



JVM быстра, это вероятно одна из самых быстрых сред из существующих. Со временем она просто становится всё быстрее и легче. Тысячи самых умных инженеров работают над её улучшением, а ещё больше внесли свой вклад за последний 21 год.



Здесь есть реальные потоки выполнения, поддержка многоядерности, её можно адски конфигурировать или просто оставить как есть. Единственной полезной вещью, которую вам вероятно стоит знать, это установки ограничений памяти для JVM, и тогда она покажет чудеса при работе с ограничениями этого окружения.



Развёртываете на Heroku? java -server -Xmx512m beast.jar. Если этого недостаточно, то у вас вероятно есть деньги, чтобы заплатить кому-то за совет… Ой, ну или просто посмотреть на StackOverflow.



Вот ключевая мысль, которую Чарльз и другие парни из сообщества JRuby продолжают продвигать. Без всяких телодвижений, ваши приложения наверняка будут работать быстрее и быстрее с каждым релизом JVM (независимо от прогресса JRuby).



Использование диска настолько большое?



Мне было интересно, и я посмотрел в папку ~/.m2, где за 9 месяцев разработки c Clojure накопилось 1010 МБ зависимостей. Даже гигабайта ещё нет.



$ du -sh /usr/local/opt/rbenv/versions/2.3.3 ~/.nvm/versions/node/v6.9.1 ~/.m2

690M /usr/local/opt/rbenv/versions/2.3.3

232M /Users/kenneth/.nvm/versions/node/v6.9.1

1010M /Users/kenneth/.m2




Установка Ruby снова свежая и соответствует базовым требованиям для ведения этого блога и Middleman (я там работаю над исправлением). Да, чтобы вести этот статичный блог и участвовать в разработке инструментов, на которых он работает, требуется почти 700 МБ места.



У Node установлены только ember, docpad и bower, и он более 200 МБ.



Развёртывание настолько грузное?



Вероятно, вы можете предсказать, о чём я буду говорить…



При сборке вы делаете единственный файл JAR. В нём есть всё для работы вашего приложения на любой платформе. Просто помещаете JAR где нужно и предоставляете JVM разбираться с ним.



Нет такого требования, чтобы приложение работало на каком-то массивном сервере приложений, можете очень легко связать с производительным HTTP-сервером прямо в JAR-файле. Парни из Node так делают, и из Ruby тоже, но как-то файлы JAR не могут оставаться сами по себе? Я тоже так думал…



Я, например, освобождён от необходимости запускать apt-get install build-essentials в продакшне.



День ото дня с JVM



У меня работает минимум пять процессов JVM на моём 2012 MacBook Pro с 8 ГБ памяти. Работают постоянно и каждый день. Я никогда не пробовал запустить пять приложений Rails одновременно.



Почему пять? Два для Datomic (сбор данных и консоль), один для наших API бэкенда и один для того фронтенда, с которым я работаю. Иногда в фоне также идут автоматические тесты. Уверен, сжатие памяти macOS наверняка тут помогает, потому что у значительной части этих процессов JVM должна быть загружены в память одни и те же байты.











Если бы вы сказали мне об этом 10 месяцев назад, я бы только посмеялся. Кто в здравом уме запустит 5 или более процессов JVM? Я мог бы отдать руку на отсечение, что я никогда так не сделаю.



О, а что же насчёт путей классов и всех остальных сумасшедших вещей? Они вообще вас не коснутся благодаря отличному инструментарию Clojure. Это та же причина, по которой вы используете npm или bundler и не мучаетесь с включением путей. Вы могли бы делать это, но тогда у вас вероятно другая проблема, которую вы не замечаете.



Радости REPL



Если бы мне пришлось непрерывно останавливать и запускать экземпляры JVM, я бы точно сошёл с ума. Это беспокоило меня насчёт JRuby в старые времена. К счастью, с Clojure и великолепным REPL приходится перезапускать экземпляр JVM только в редком случае, когда я действительно где-то крупно облажался. Здесь довольно хорошая защита от дурака. Figwheel днями работает без проблем.



Заключение



Будьте осторожны, оценивая JVM как объект. Оценивайте Java как язык во всех смыслах, но не смешивайте его с виртуальной машиной.



Я тоже раньше думал как вы. Я думал о JVM как о том бегемоте. К счастью, теперь я смог избавиться от этих ограничений и получил результат работы тысяч гигантов, поддерживающих её.



Ни в коем случае не считайте этот пост как знак «конца Node» или «конца Ruby». Воспринимайте как свежую перспективу. Если не можете перейти на JVM, хотя бы подумайте, как уменьшить раздутость в своём собственном мире.



Спасибо, что уделили мне своё время. Изучайте Clojure и пробуйте Simple Made Easy.
Original source: habrahabr.ru.

https://habrahabr.ru/post/324572/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

«Сложную архитектуру очень просто сделать» — интервью с Олегом Анастасьевым из Одноклассников

Вторник, 21 Марта 2017 г. 16:16 (ссылка)





Знакомьтесь, Олег Анастасьев — ведущий разработчик Одноклассников, спикер на конференциях по Java и Cassandra, эксперт в области распределенных и отказоустойчивых систем. С Олегом мы поговорили о следующем:




  • Что не так с термином «архитектор»

  • Зачем Одноклассникам 11 000 серверов

  • Как выглядят учения по ликвидации аварий

  • Что такое «Правило большого З»

  • Как в Одноклассниках используют Cassandra

  • В чём для современной компании сложности с размещением кода в Open Source

  • Как в Одноклассниках работают с Big Data











Как всегда, под катом — полная текстовая расшифровка беседы.





Архитектура и архитекторы



— Олег, хотя по своей роли ты близок к архитектору, ты везде говоришь, что ты «ведущий программист». Я знаю, что к слову «архитектор» у тебя особенная нелюбовь. Расскажи, пожалуйста, почему.



— Сейчас в индустрии понятие «архитектор» означает некоего человека с огромной головой, который всё всегда знает наперёд, естественно, давно отошедшего от дел и уже не марающего руки кодом. Его задача — раздавать указания тем, кто ещё не дорос до архитектора, максимум — рисовать квадратики, соединять их стрелочками. Как строительный архитектор, который рисует, но ничего сам не делает.



Понятие «архитектор» уже сильно выхолощено. И поэтому я против этого термина, я больше за то, чем архитектор должен бы быть в нашей индустрии. Это лидер, ведущий разработчик, который сам пишет код, сам делает архитектурные решения, сам их реализует, проверяет, один или с помощью кого-то ещё, видит свои ошибки, чужие ошибки, переделывает это всё до того, как архитектура становится хорошей, выверенной, понятной, не содержащей ничего лишнего.



— То есть ты всё-таки занимаешься «архитектурой», но ещё и код пишешь? Мы тебя поймали, всё-таки архитектор!



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



Поскольку у нас очень сложная техническая индустрия, один человек может очень легко ошибиться, не увидеть каких-то очевидных решений, сам себя загнать в какую-то ментальную ловушку. И тут очень важно, чтобы рядом был второй, третий, четвёртый человек, которые рассмотрят ту же самую проблему под немножко другими углами и предложат такие решения, про которые ты сам потом удивляешься «почему я до этого не додумался, это же очевидно, просто и понятно».



— Судя по тому, что я услышал, простота является для тебя важным инженерным принципом, ты сейчас два или три раза про это сказал. Я правильно тебя услышал?



— Да. Сложную архитектуру очень просто сделать. Как говорится, любая проблема может быть решена введением ещё одного уровня абстракции. А искусство и собственно сложность состоят в том, чтобы сделать сложную систему просто. Чтобы она содержала как можно меньше компонент. Потому что чем меньше в системе компонент, тем меньше число отказов. Получается более надёжная архитектура, более надёжная система, за ней надо меньше следить, она реже ломается, значит, она чаще приносит прибыль.



11 тысяч серверов



— Сейчас в Одноклассниках много тысяч серверов, я уже даже запутался, сколько. Называют цифры «9 500», «10 000», «11 000». Первый вопрос — сколько же их всё-таки, второй — зачем их так много, и третий — как это стыкуется с простотой?



— Сейчас (интервью записывалось в декабре 2016 — прим.авт.) их, наверное, ближе к 11 000. По большей части это связано с тем, что в Одноклассниках запускаются новые сервисы, и они требуют иметь под собой какие-то мощности — это раз. Во-вторых, нагрузка растёт и по старым сервисам. Допустим, по видео за последний год мы удвоили трафик, мы сейчас по просмотру видео вообще занимаем первое место в рунете. А видео — это очень ресурсоёмкий проект, он требует очень больших мощностей. Поэтому я думаю, что, скорее всего, где-то к 11 000, и, скорее всего, будем и дальше расти.



— А зачем так много машин, и как это стыкуется с простотой, которую ты декларируешь как принцип?



— Серверов много, потому что нагрузка большая. У нас в стране есть две социальные сети, ВКонтакте и Одноклассники. Сама по себе социальная сеть — это такая задача, где очень много связей между людьми, между системами. То есть её не получится шардировать по самой простой архитектуре shared nothing, когда у вас есть два сервера, и между ними ничего общего, они не знают друг о друге и работают индивидуально. С социальной сетью так не получится, потому что её предназначение — как раз связывать людей, соответственно, точно так же и сервера связываются внутри, чтобы гонять информацию туда-сюда. Поэтому, с одной стороны, эта задача не очень просто масштабируется, с другой стороны — это то количество серверов, которое для социальной сети нашего объёма, наверное, близко к необходимому минимуму. Допустим, у ВКонтакте серверов значительно больше, чем у нас.



— Ну, они у вас же, так понимаю, всё равно не на 100% загружены?



— Нет, не на 100.



— Как вы выбираете баланс между Throughput и Latency, между «загрузить машину по самые помидоры» и «оставить её разгруженной, чтобы она быстро отвечала на запросы»?



— Есть такое понятие, как «эффективное использование дата-центра». На текущий момент дата-центр у нас полностью «железный», то есть каждая задача работает на выделенном ей сервере или наборе серверов.



— В том плане, что нет виртуализации?



— Нет никакой виртуализации или чего-то такого. Соответственно, такой дата-центр, с одной стороны, недогружен, а с другой — способен наиболее быстро отвечать на запросы, и при этом наиболее предсказуем, потому что на машине, кроме одной задачи, ничего не работает. То есть ничто не может помешать этой задаче что-то сделать.



— Диагностика проще?



— Проще диагностика, поиск каких-то аномалий, поломок. Мы пошли по пути наименьшего сопротивления: нужно было решать и много других задач, не только эти, поэтому ставили всё больше железа.



— Место в дата-центрах вокруг Москвы — оно же не резиновое, да?



— Место не резиновое, мы, наверное, уже в 2017-м или в 2018-м выберем все мощности, которые есть сейчас в дата-центрах…



— Ох же весело вам будет.



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



— То есть запасы есть?



— Запасы есть, и сейчас у нас делаются технические решения, которые позволят достаточно безопасно запускать несколько задач на одной машине. То, что принято называть «облаком», что-то типа такого. Двигаемся в эту сторону.







Отказоустойчивость



— С внешним трафиком всё понятно: видео колоссально растёт, поэтому и общий колоссально растёт. А внутренний трафик? Когда есть такое количество машин, это же порождает какое-то дикое количество внутреннего трафика — не знаю, в десятки, в сотни или в тысячи раз больше, чем внешнего. Как вы вообще эту задачу решаете? Как вы управляете трафиком между дата-центрами — нужны же огромные мощности, чтобы туда-сюда гонять данные?



— Вот здесь ты вступаешь в ту область, к которой я последние лет, наверное, восемь не имел никакого отношения. Этим занимается отдельная команда, которая называется «сетевики», я достаточно далёк от них.



— Хорошо, давай попробую в таком ключе переформулировать свой вопрос. Дата-центров у вас три или четыре, даже не знаю...



— Три с половиной!



— Три с половиной. Как происходит разделение данных и нагрузок между этими дата-центрами, по каким принципам это было сделано?



— Дата-центра у нас три для того, чтобы мы выдерживали отказ любого из них. И наша цель в том, чтобы, во-первых, мы могли выдерживать такой отказ, и, во-вторых, пользователи бы при этом ничего не заметили. Многим кажется, что отказ дата-центра — это какая-то очень редкая вещь, бывает буквально раз в столетие. Так вот нет! Отказы дата-центров регулярны, по своему опыту могу сказать, что приблизительно раз в три месяца один из них отказывает. Прогнозировать, на сколько он лёг по времени — на 15 минут, на час, на два — мы не можем. А каждый этот час стоит нам очень много денег.



— А почему так часто отказывают, что это — какие-то ЧП, или?..



— Разные вещи. Бывают ЧП, бывает человеческий фактор. Допустим, совсем недавно в одном из наших дата-центров целиком вылетели два зала, это много-много-много машин. Из-за того, что оба луча питания отвалились. При этом все наши дата-центры — самой высшей категории, есть дизель-генераторы, UPS’ы и т.п. Но вот в программном обеспечении генераторов закралась ошибка, и при отключении дата-центра они тупо не завелись. Там были какие-то аномалии в электрической сети, они посмотрели на эти аномалии и решили, что не хотят заводиться. Аккумуляторы продержались 10 минут. А дальше всё.



— И как, собственно, на всё это отреагировал портал?



— Портал отреагировал отлично, никто ничего не заметил. Это было буквально месяц назад, но в новостях не было ничего вроде «Одноклассники лежат», «#окживи», всё прошло хорошо. Разобрались в течение двух часов, включили питание, запустились.



— Была ли какая-то деградация по функциональности, или обошлось без этого?



— Абсолютно незаметно прошло. Нам немножко повезло, если бы вылетели другие два зала, то тогда была бы деградация по функциональности. Но мы двигаемся в том направлении, чтобы вылет дата-центра вообще никак не влиял на пользователя.



— А какими путями достигается это повышение надёжности сервиса? Как мне сделать так, чтобы моя система из трёх машин, которые лежат по разным дата-центрам, работала стабильно и не деградировала?



— Основная идея здесь, естественно, в резервации. Понятно, что мощности должны быть выбраны так, чтобы вылет одной трети этой мощности позволил оставшимся двум серверам тянуть эту нагрузку. И опять же, дело не только в нагрузке, но и в доступности данных. Если у вас вылетел дата-центр, там лежали какие-то важные данные, и сейчас они недоступны, то в лучшем случае у вас происходит отказ в функциональности, потому что не можете достучаться до этих баз данных, а в худшем случае вообще ничего не работает.



У нас в каждом дата-центре имеется реплика данных, на Западе это называется термином «replication factor 3», то есть каждый из дата-центров имеет свою реплику. Когда мы меняем данные, записываем во все дата-центры, во все три реплики, и дожидаемся ответа от двух самых быстрых, потому что третья может не работать в данный момент. И мы считаем, что данные записаны, как только получили два подтверждения от двух самых быстрых.



По такому принципу сейчас построены все хранилища данных в Одноклассниках, кроме Cold Storage — это такое специальное хранилище, которое хранит большие блобы — для видео, для фоток, для музыки.



— А почему так? Это дорого?



— Да, replication factor 3 всё же подразумевает, что у нас три копии данных, а видео у нас очень быстро растёт, там уже петабайты-петабайты, и хранить их в трёх копиях — это достаточно дорогое удовольствие. Но, чтобы не терять надёжность, всё равно используется резервация, и у этой системы replication factor 2.1. Грубо говоря, это RAID 5, собранный не в рамках локальной машины, а в рамках распределённой системы. То есть, если в RAID 5 репликами служат локально установленные диски, то в данном случае эти диски распределены по разным дата-центрам.



— И если что-то из этого вылетает, то идёт распределённое восстановление?



— Да.



Во что играют админы



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



— Здесь должно быть достаточно работы не только программиста, но и администратора системы. Поэтому, чтобы понимать, когда при восстановлении что может упасть, мы устраиваем учения и тестирование аварий. Это очень интересная игра, которой периодически занимается отдел администрирования.



— А как это выглядит?



— Какой-то человек назначается режиссёром этой аварии, и он придумывает сценарий: что происходит, какие симптомы. А остальные не знают заранее, что же будет происходить. И он начинает учения: говорит, что отключилось то-то, зажглись такие-то лампочки, мониторинг говорит то-то, на графиках это. А участники пишут, что они делают и как эту ситуацию будут исправлять.



Это теоретические учения, а есть ещё практические. Когда мы знаем, что cold storage должен выдерживать падение трети данных, мы выбираем какой-нибудь день (естественно, сначала такие тесты делаются обычно в моменты наименьшей нагрузки, например, ночью). Дата-центр или машины в нём полностью выключаются или партиционируются по сети, в зависимости от того, какой сценарий мы отрабатываем, потом включаются, и смотрим, что происходит.



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



— Страх, что ты что-то сломаешь на продакшене, проходит приблизительно после десятого раза, когда ты что-либо сломал на продакшене. Система сама должна реагировать на то, что её сломали, правильно. То есть с минимальным количеством спецэффектов для пользователя.



— Расскажи об этом, пожалуйста. Что такое «правильно реагировать» и «неправильно реагировать»?



— Неправильно реагировать — например, давным-давно, если у нас вылетал один из SQL-серверов, то портал не работал. Показывал колесо «Извините, до свидания, заходите попозже». Это было в самом-самом начале Одноклассников, год 2007-2008. Серверов было 16, они не так часто вылетали, и их достаточно быстро заводили назад и всё было хорошо. Когда их стало 64, а потом 128, а потом 256, понятно, это стало происходить всё чаще, и такой уже вариант не работал. Он просто плохо влиял на бизнес.



Поэтому стали думать о failure tolerance системы, то есть об устойчивости к сбоям. Тогда в интернете об этом не было вообще никакой информации. Failure tolerance — это то, что мы сами внутри себя придумали и начали реализовывать. Идея была достаточно простая: если сервер не работает, то мы не можем оттуда прочитать данные. Если эти данные нужны нам для того, чтобы что-то по ним отобразить, то и фиг с ним, мы пропустим.



Пример. Возьмём граф друзей. В обычной ситуации у вас 32 друга. 16 партиций — по 2 друга на каждую. Одна партиция отказывает, двух друзей мы прочитать не можем. В этой ситуации мы покажем только 30 друзей и надпись: «Возможно, отображаются неполные данные». Потому что мы знаем, что с одного из шардов мы не смогли прочитать данные.



По большому счёту, пользователю может быть в конкретный момент наплевать на этих двух друзей, тем более, что из-за вылетевшей партиции они сейчас всё равно не могут работать. Но при этом юзер может продолжать работать с остальной информацией. Это то, что относится к рендерингу данных, к их отображению на экране. И наоборот, если мы хотим изменить данные, которые лежать на отказавшем в данный момент сервере, то такую транзакцию выполнять нельзя, она отваливается с сообщением «извините, сейчас не смогла, может быть, попозже».



Таким образом, из-за одного отказавшего сервера страдает очень маленькая часть функциональности, требующая изменения данных на этом сервере. А всё остальное работает. Вы не можете просмотреть всех своих друзей в соцсети (этих двух вы временно потеряли), вы не можете раздружиться с ними в данный момент, но зато вы можете делать в соцсети все остальное — читать ленту, постить фоточки, обсуждать что-то в комментариях, и так далее. Это называется «изоляция отказа». Мы изолировали отказ этого сервера ценой деградации полноты данных.



— А если весь кластер друзей отвалился: вирус или что-то такое?



— Не обязательно вирус. Весь кластер может отвалиться очень просто: люди могут положить, баги могут быть, кто-то неаккуратно захватил lock на всю таблицу, причин может быть очень много. Баги в самом софте баз данных, сеть отвалилась — кластер полностью недоступен, его вообще нет. В этом случае нам не остаётся ничего, кроме как деградировать уже функционально, то есть мы полностью убираем часть функций друзей.



— А что пользователь видит при этом?



— Пользователь видит заглушку: на соответствующем разделе будет показано, что сейчас функция недоступна, заходите попозже, но остальные разделы сайта будут работать, если они не затронуты этой аварией. Такой режим может включаться и автоматически, и вручную. Администратор тоже может отключить любую функцию сайта в любой момент.







Почему отказались от SQL-серверов



— Ты говорил про Microsoft SQL Server, которых у вас было 16, 32, 64 инстанса и так далее. Как оно вообще в таком количестве дружит друг с другом? До сих пор вопрос скалируемости баз данных, репликации, шардирования остаётся открытым. Люди говорят, что даже самые навороченные системы типа Oracle могут скалироваться до 10 машин, а дальше, как правило, начинается беда. Что сейчас в Одноклассниках с MS SQL-серверами?



— С MS SQL-серверами в Одноклассниках всё очень просто: они выводятся из эксплуатации. Мы по возможности отказываемся от них.



— А в пользу чего? Какие системы заменяют SQL-сервера? Что примерно на них было, и на что заменяется?



— На них были обычные бизнес-данные, которые требуют ACID-транзакций. Данные о пользователях, дружбах, деньгах, фотках (не блобы, а метаинформация), всякие классы, комментарии, сообщения — всё подряд. SQL-сервер — нормальная парадигма, она хорошо работает, на ней удобно быстро писать код. Мы так и делали — быстро писали код, пока в какой-то момент не стало ясно, что парадигма SQL-сервера архитектурно не укладывается в модель отказа дата-центра.



— Расскажи про это чуть подробнее.



— Есть SQL-сервер. И он сделан так, что это single-master, то есть одни данные изменяет только один конкретный инстанс сервера. Есть какие-то процедуры, которые переключают мастер, если первый отвалился — leader election, failover… Я уже начал подзабывать эти термины. Отказоустойчивый кластер можно построить несколькими способами. Классический энтерпрайзный вариант: у вас есть shared storage, два сервера смотрят в него по оптической связи, и если один отказывает, то другой берёт управление — вот и вся схема. Но во-первых, сами железки стоят очень дорого. Во-вторых, эти два сервера находятся рядом с shared storage, то есть в принципе не могут находиться в другом дата-центре. И если отказывает дата-центр, в котором эта железка стоит, то выходит, что кучу денег вы потратили просто так. Эта схема не дает никакой отказоустойчивости в такой ситуации.



Есть другая схема, когда master в одном дата-центре, slave в другом, и выполняется…



— Cross data center replication.



— Да-да. При коммите сначала делается запись в slave, и если slave закоммитил и всё хорошо, то только тогда мастер говорит системе, что закоммитил. В этой схеме тоже не всё хорошо. Когда у вас немножко притормаживает slave, master перестаёт подтверждать коммиты. Или если что-то случилось с сеткой. Такая транзакция работает со скоростью самого медленного сервера плюс latency по сетке.



Собственно, вот основные проблемы. Есть ещё мультимастер, но это отдельная песня, очень сложная и очень много конфликтов возникает. На наших нагрузках он вообще не работает, поэтому мы даже не рассматривали этот вариант.



Поэтому стало ясно, что такие классические ACID-базы данных не работают. Они не позволяют нам сделать такую отказоустойчивость, как мы хотим. Чтобы эту проблему решить, мы написали свою ACID-систему, которая распределяет данные по нескольким дата-центрам. Я про неё рассказывал года два назад на Joker — как она работает, основные её принципы. И сейчас мы с максимальной скоростью переносим все наши данные на эту систему.



— Мы добавим ссылку на тот доклад, но для тех, кто не сможет посмотреть его, ты можешь как-то базово описать, как вы решили в новой системе описанные проблемы?



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



Далее, координаторы, которые управляют транзакциями, в нашей системе данными не владеют. Данными владеют только storage-ноды… То есть мы выделили отдельно transaction-координаторов и storage.



— Координаторы транзакций занимаются чем — логикой, отказами?..



— Логикой транзакций. На нём можно открыть транзакцию, составить её — ну, когда вы делаете insert несколько раз подряд, это составляет из себя транзакцию, он операции не запускает, а только складывает в память. И если вы, как клиент, который открыл транзакцию, что-то читаете из базы данных, то ваш запрос на чтение пройдёт через этот координатор. Координатор транзакций прочитает текущее состояние из Cassandra storage, наложит то измененное состояние, которое должно быть в рамках транзакции, которую клиент открывал, и отдаст её клиенту.



Таким образом, вы собственные изменения в транзакции видите, а соседние клиенты, или другие клиенты, или просто другие треды в пределах этого клиента идут напрямую в Cassandra storage, минуя transaction-координаторы, и видят состояние данных до начала транзакции. При коммите транзакшн-координатор вот так кворумно, в две головы из трёх, запишет эти данные и даст подтверждение о успешном коммите клиенту. И те клиенты, которые в транзакции не участвовали, начнут читать эти данные, как только транзакция была закоммичена. Таким образом мы и получили read committed. Поскольку координаторы транзакций не владеют данными, то переключения между ними получились очень простыми: если один вылетает, то другой начинает использоваться.



— В случае с базами данных бывают разные уровни изоляции транзакций. Почему read committed, везде ли read committed?



— В нашей системе поддерживается только read committed. В большинстве систем достаточно read committed на самом деле. Я видел мало систем, которые требовали serializable read, и очень мало людей, которые шли на read uncommitted по собственной инициативе. Мы во времена работы с Microsoft SQL Server использовали read uncommitted не потому что он такой хороший, а из-за производительности.



— Какие версии Cassandra у вас используются «в бою»?



— У нас сейчас используются три версии. Одна — старая-старая 0.6, она простая, как автомат Калашникова, там ничего лишнего нет. Она используется в качестве key-value хранилища, точнее, column family хранилища, скажем так. Если у вас ключ, а в значении у вас ordered map колонок со значениями. Это много где очень удобно. И работа с такими хранилищами происходит именно на низком уровне.



В более современных версиях Cassandra появилось такое понятие, как CQL: Cassandra Query Language, который как-то похож на SQL. На самом деле это, конечно, совсем не SQL, но нечто похожее, что облегчает людям порог входа в эту новую систему. Тут мы используем Cassandra 2.0. Почему мы выбрали 2.0 для перевозки SQL-баз на неё: грубо говоря, потому что там SELECT, и здесь SELECT. Для человека, который программирует прикладную логику, внешне очень мало что меняется. Он как там делал селекты, так и здесь делает.



В MS SQL мы не использовали join-ы: мы отказались от них, потому что на наших объемах они очень медленно работают. В Cassandra же join-ов просто нет.



— То есть данные сильно денормализованы?



— Естественно.



— Ты сказал про Cassandra версий 0.6 и 2.0, а третья версия какая?



— А третья 1.2. Была такая очень переходная версия, мы её взяли, попробовали в одной штуке, штука работает через пень-колоду, но как-то фурычит до сих пор.







Технический долг



— Итого, на продакшене у вас три разных версии Cassandra. Я знаю много людей, которые, когда будут смотреть или читать наше интервью, в этом месте поднимут палец и скажут: «так, у вас, ребята, технический долг, не хотите ли вы его как-то начать исправлять?» Соответственно, не является ли факт использования трех разных версий одного продукта техническим долгом? Если нет, то почему, а если является, то почему не исправляете?



— Для того, чтобы зайти на эту тему, я хочу рассказать тебе о замечательном «Правиле большого З», которое используется у нас в компании. «Большое З» означает «Зачем?». То есть любое техническое действие, в том числе перевод версии софтины X с 1.0 на 2.0, должно отвечать на этот вопрос. Какие задачи это решит? Решит ли это какие-то реальные проблемы? Реальные проблемы — это тоже отдельная интересная вещь. Мы считаем проблему реальной, если она упирается в деньги, во время или в людей. Любая реальная проблема упрётся в эти три кита IT-индустрии. Технический долг… Что такое технический долг?



— Наверное, так: это набор действий, которую нужно будет сделать в будущем для того, чтобы решить какие-то проблемы. То есть у нас намечаются какие-то проблемы, но мы говорим, что сейчас ими не будем заниматься, а будем решать их потом. И проблема с техническим долгом, как я понимаю, в том, что это «оно» когда-нибудь наступит.



В данном случае, если среди Java-программистов сейчас можно найти людей, которые что-то знают про Cassandra 2, то найти людей, которые что-то знают про Cassandra 0.6, довольно трудно. Если кто-то уйдёт в отпуск, кто-то будет в командировке, кто-то будет давать мне интервью, а с этой штукой что-то случится, то довольно трудно будет найти того, кто это сможет ее быстро починить.




— В данном случае вопрос с людьми решается совершенно другим способом. У нас каждую из ключевых технологий обычно знает несколько людей, нет какого-то одного человека. Более того, мы (в том числе и я) специально стимулируем других людей, даже прикладных программистов, наткнувшихся на баг, допустим, в той же Cassandra 0.6.



Как всё это происходит в плохой конторе? Все приходят к одному человеку, который отвечает за Cassandra 0.6, и говорят: вот я нашёл баг, вот я завёл тикет в JIRA, написал там steps to reproduce, пофиксите его, пожалуйста, мне срочно нужно. Я пока пойду позанимаюсь другими делами, а вы как-нибудь пофиксите и принесёте мне потом всё на блюдечке с голубой каёмочкой.



У нас так редко происходит, чаще всего происходит так, чтобы нашёдший это человек понял, в чём этот баг, пофиксил его, закоммитил, команда Cassandra 0.6 внутри компании посмотрела на этот код, сделала ему код ревью, если всё нормально, код идёт в мастер, человек убеждается, что в его конкретном кейсе он пофиксил баг, молодец.



— А что происходит с другими инстансами? Предположим, человека работает в команде подарочков, а эта Cassandra 0.6 используется ещё в других сервисах. Другие будут обновляться?



— Если это критичный баг, то команда Cassandra 0.6 доведёт до сведения всех пользователей, что такой баг есть и его желательно фиксить. Проблема в любом случае дойдёт до людей, которые занимаются Cassandra. Но поскольку его фиксят люди, которые на него наткнулись, во-первых, у них больше мотивации это сделать, а во-вторых, они за счет такой работы узнают потроха своего сервиса, как всё на самом деле у них работает. Программист пофиксит один баг, второй, третий, а на четвёртый мы ему не нужны, я не нужен — он уже во всём может разобраться самостоятельно.



— А не страшно было брать в продакшн что-то, имеющее версию 0.6, от чего в своё время, я так понимаю, отказался Facebook? Как вообще такое вам пришло в голову? Часто говорят, что энтерпрайзы не берут библиотеки до выхода версии 1.0.



— Эта мысль пришла в голову, исходя, опять же, из «Правила большого З». Была конкретная проблема: тогда в компании не было хранилища, которое бы, во-первых, было отказоустойчивым, во-вторых, выдерживало бы падение дата-центра, и было бы достаточно быстрым.



— Какой это был год?



— Примерно 2010-й. Мы тогда работали по большей части на Microsoft SQL Server и на самодельных NoSQL-штуках, построенных по master-slave принципу и носивших смешное название «Бармалей». Бармалеи очень часто отказывали, всё-таки как хранилище назовёшь, так оно и полетит ;-). Их всё время нужно было восстанавливать, у нас для этого среди администраторов был специально выделенный человек — специалист по подъёму Бармалеев. Он не спал каждую ночь и что-то с ними делал: то бэкапы, то дамп базы, то туда, то сюда. Это занимало у него очень много времени, он даже похудел немножко. На тот момент проблема была очень животрепещущей, и стало понятно, что дальше так невозможно.



— Что было на рынке на тот момент?



— Ничего. Тогда было два решения — Voldemort от компании LinkedIn, они недавно открыли его в опенсорс, и второе, собственно, Cassandra. По-моему, когда мы в первый раз пришли на неё смотреть, была версия 0.5, которую буквально только что выбросил Фейсбук. Мы попробовали одно и другое, и для нас больше подошла именно Cassandra. Дальше мы её взяли и начали доводить до ума, потому что естественно, что стоковая Кассандра, которая есть в опенсорсе, мягко говоря, не работает.



Дальше в течение нескольких месяцев мы делали разные тесты, по-разному пытались заставить её работать, что-то переписали, какие-то части выкинули. Там осталось очень мало от оригинальной Cassandra. Кстати, эта Cassandra 0.6 есть у нас на GitHub, мы просто её не афишируем, но я периодически даже коммичу туда что-то — именно на GitHub, хотя у нас по большей части разработка делается с внутренним репозиторием. Просмотреть на её имеет смысл тем, кому нужен «просто column-family storage» — это неплохой вариант.



— Cassandra была настолько сырая на тот момент, когда Фейсбук её выкинул?



— Да, она была очень сырой, там кое-где сильно текли концепции, с тех пор её, конечно, много доводили до ума. Тут, конечно, большое спасибо команде DataStax.



— А современные версии, 2.0 и выше, уже продакшен-quality, или там тоже всё печально?



— Мне сложно говорить о совсем последних версиях, 3.0-ветке. А про 2.0 могу сказать только то, что когда её выпустили, мы внутри себя доводили её до продакшена, чтобы она как-то работала и не падала, где-то около полугода.



— Это связано с тем, что она сырая, или у вас просто какие-то специфические задачи?



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



— А в апстрим вы им что-нибудь возвращали?



— Да, конечно.



— Много?



— Когда я в последний раз считал, у меня было около 20 тикетов, которые я в стрим возвращал… Очень много коммитили именно в 2.0.0, когда она вышла. Мы начали с ней возиться, когда она была ещё релиз-кандидатом или бетой, и в том состоянии она у нас под продакшен-нагрузкой продержалась приблизительно 5 секунд.



— А вы отправляли просто баги, или прямо с багфиксами, или по-разному?



— Только фиксы. Когда вы работаете с опенсорс-проектом, мне кажется плохим тоном репортить баги без фиксов. Вы этим людям не платите, пользуетесь этим кодом, а если пользуетесь, то верните им хотя бы часть стоимости своим трудом.







Open source



— Очень интересная тема — опенсорс. Мы о нем говорили в интервью с Барухом Садогурским, у нас там всплывала история с Одноклассниками, правда, не про Cassandra, а про патченный OpenJDK… Очень мало кто реально понимает, что опенсорс — это не только про «читайте код», не только про какую-то бесплатность (free software и open source часто идут бок о бок), а ещё и про то, что я могу сам отловить баг, пофиксить, и если хочу, то ещё и вернуть в upstream. Мне удивительно, что с этой позиции очень мало кто опенсорсом занимается.



Насколько активно используется опенсорс в Одноклассниках? Насколько часто происходят коммиты в Upstream чужих опенсорс-проектов или подобного типа взаимодействия?




— Сложно сказать, насколько много. Считать ли Guava опенсорс-решением, которое мы используем? Это больше библиотека. Но, допустим, да, Guava мы используем, Андрей как раз находил там прекрасные баги про класслоадеры, репортил их и предлагал решения. Но здесь сильно зависит от команды, которая является коммитером опенсорса. Некоторые команды в принципе не дают другим людям участвовать в разработке.



— Представим такую ситуацию. Вот есть у меня большой продакшен на 11 000 машин. Вдруг в моей Cassandra обнаруживается какой-то адский баг, и всё, отваливается каждые 3 минуты. И если у меня не опенсорс-решение, у меня нет шансов. Я иду в саппорт вендора, начинаю что-то выяснять с кем-то, заводить тикеты, но шансов, что оно быстро решится, практически нет, и на самом деле я в заднице. В случае, когда используется опенсорс-продукт, есть ли ощущение, что опенсорсность даёт возможность всё это фиксить, и часто ли это происходит? Часто ли обнаруживаются в опенсорсных тулах какие-то дыры, и что можно вообще сказать о состоянии современного?



— Я могу сказать, что у нас в компании, если мы выбираем какое-то решение для продакшена, оно обязано быть опенсорсным именно из тех соображений, что оно сломается — и мы знаем, что сломается, вопрос только во времени, когда это произойдет. И мы должны мочь это пофиксить. Быстро, сами, у нас должен быть человек, который может это пофиксить. Соответственно, из всех опенсорс-решений мы берём только те, которые можем пофиксить сами, то есть оно должно быть написано на языке или технологии, которые мы понимаем.



Если такое опенсорс-решение есть — замечательно, мы его берём, пытаемся использовать, фиксим баги по ходу, если какие-то фиксы могут помочь всем остальным, в апстрим закидываем, и так далее. Если такого решения нет, то мы пишем сами, тогда это будет чистый closed source, таких решений у нас тоже очень много. Часть из них сильно завязана на инфраструктуру, как, например, система статистики — это огромная система, в неё входит очень много компонентов, они должны как-то взаимодействовать, туда вложено очень много труда. Это closed source, открывать его не имеет смысла, потому что никто не сможет этого построить или потратит очень много времени.



Netflix выпускает в опенсорс какую-то часть своей статистики, например, у них есть Atlas, но надо понимать, что это, скорее всего, только очень маленькая часть. Я не могу знать полностью, но если сравнивать то, что есть внутри у нас и что открыл Netflix, у них должен быть какой-то аналог того, что есть у нас. Так что Netflix тоже открывает далеко не всё и только то, что обычные люди могут применить.



В этом году мы рассказывали про смарт-мониторинг, про систему, которая сама автоматически детектирует аномалии среди тысяч серверов, и было много вопросов «а давайте вы её в опенсорс выложите». Но это невозможное действие, просто потому что она завязана и на систему статистики, и на сбор статистики, и на процессы, которые у нас вокруг этой статистики налажены.



— Это хороший вопрос, потому что мы как-то разговаривали с Андреем Паньгиным про библиотеку one-nio, которой он занимается. И зашла речь о том, чтобы не просто выложить ее на GitHub, а построить вокруг этого что-то ещё. И выяснилось, что это огромная работа. Это создание wiki, туториалов, вычищение всех «однокласснико-зависимых» компонентов, написание тестов, описание разнообразных юзкейсов. Помимо этого, ещё должны люди ездить с докладами по конференциям, писать статьи и так далее.



То есть выяснилось, что объём работы совершенно колоссальный. И если четыре года назад, когда Андрей начинал рассказывать про one-nio, зацепиться за это и начать делать из этого OpenSource, наверное, имело бы смысл, то сейчас подобных решений на рынке появилось довольно много и, пожалуй, этот момент уже упущен.



И у меня возникло ощущение, что вся эта опенсорсность нынешняя, Netflix и всё такое — это для компании ни фига не бесплатно, это в реальности огромное вложение. Ты думал что-нибудь из своего кода когда-то опенсорсить? Ты рассказывал про свою версию Cassandra, но опять же упомянул, что это довольно сложно использовать. А были ли в компании мысли взять хороший крутой кусок кода, решающий интересные задачи, которые мало кто решает, попытаться это заопенсорсить и что-то вокруг этого сделать?




— Мысли всегда есть, тем более, что внутри много интересных кусков кода, некоторые идут совсем вразрез с тем, как принято программировать на Java, некоторые решают довольно интересные задачи весьма оригинальным способом. Весь вопрос упирается в «Зачем?», в деньги и в людей. Потому что туда нужно вложить достаточное количество денег, времени и людей, которые всё это будут делать. А ответ на вопрос «Зачем?» должен как-то это обосновывать, и мы не нашли для себя такого ответа. На самом деле это бы означало, что Андрей Паньгин вместо того, чтобы заниматься развитием one-nio remoting (у нас сейчас есть ещё одна проблема с remoting, мы подошли к очередной границе наших технологий, и нам нужно его развивать), пошёл бы заниматься маркетингом, туториалами и one-nio, и через год мы получили бы большие проблемы на production из-за отсутствия необходимого развития.



— Я как-то попытался ему помочь, сидел, всё анализировал, но это, конечно, год работы, чтобы это нормально сделать. Несколько месяцев точно, в это надо глубоко залезать. Всякие маркдауны сделать и сгенерить документацию — это фигня, а...



— Там есть работа чисто по оформлению, а есть работа по наработке тех же примеров использования.



— И вот в это я упёрся. Я посмотрел, как это используется в компании, как Андрей использует, какие-то конкретные примеры. Но чтобы это привести к состоянию, когда кто-то другой реально может брать и юзать — мне показалось, что я как инженер это не вытяну, или вытяну, но потрачу столько собственного времени и времени того же Андрея… Потому что это же банально high-performance вещи, и в них даже банальные рефакторинги могут стоить перфоманса. Это просто нерешаемая штука.



— Про перфоманс — да, мы тут недавно с Андреем на ревью где-то полдня над одним из кусков one-nio решали вопрос, что лучше в конкретном месте: Long.reverseBytes() или XOR. Делают они абсолютно одинаковые вещи, но…



— А теперь представь, что я прихожу туда и говорю «Андрей, тут надо зарефакторить». Андрей, естественно, тут же скажет, куда мне пойти, и будет совершенно прав, потому что понятно, что я в этом разбираюсь гораздо хуже него.







Как готовить Big Data



— Сейчас очень популярны истории вокруг Big Data, Smart Data, Data Science и всего вот этого. В каком виде это есть в Одноклассниках и как это выглядит?



— В компании есть команда Data Science, которая занимается анализом и построением математических моделей всего, что угодно. Началось это всё из задачи в музыке. Там была очень простая задача: есть куча пользователей, они грузят кучу треков, в этих треках в тегах внутри иногда что-то написано, иногда не написано, иногда один и тот же трек с разными тегами грузится. Из этого разнообразия файлов нужно было создать каталог: кто есть исполнители, какие есть альбомы, объединить в альбомы, какие-то треки включать в этот каталог, чтобы красивенько было, какие-то не включать. И сделать из набора данных красивую конфетку. С этого у нас началась вся система Data Science.



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



Я вспомнил смешной случай. В те времена, когда мы это запускали, кажется, году в 2011-м, такого каталога не было на user-generated content. У ВКонтакте всё было просто списком, у Фейсбука вообще ничего не было и до сих пор нет, про Spotify никто не слышал, был разве что Last.fm. То есть этот каталог и персонализация музыки были мало изучены, мало у кого это было, и какой-то человек твитил: «Ха, я захожу на Одноклассники, смотрю популярные треки, а там всё больше “Голубые глазки”, Стас Михайлов, ха-ха-ха». Мы очень долго смеялись над этим человеком, так как ему показывали специально для него рекомендованные треки, исходя из того, что он слушает.



Возвращаясь к рекомендациям — сейчас уже много где эта система используется, в том числе «Умная лента» основана на этих технологиях. Фильтрация, ранжирование, большой кластер стримингового анализа и не только он. Используется совсем разный анализ.



— Это больше инженерная задача или научная?



— Такие штуки всегда на стыке: это и инженерная задача, и научная, и продуктовая. Мало показать нужный контент, нужно ещё этот контент привлечь, чтобы его потом показать. Здесь работает не только команда, которая занимается рекомендациями, но и инженеры, которые отвечают за latency технической реализации, и продуктовые менеджеры.



— Я много слышал, что людей, которые разбираются в Data Science, мало. В основном какие-то бородатые хипстеры бегают и машут руками вокруг этого. И, во-вторых, если есть люди, которые что-то реально в этом понимают, это довольно академические люди, а у таких людей бывают проблемы с продакшеном, когда это всё надо заводить в кластер и строить вокруг этого боевую систему. Что происходит в Одноклассниках с этим?



— Мы бы не запустили систему, если бы она не выдерживала нагрузки. Естественно, что люди, которые сейчас это запускают, понимают в нагрузках и продакшене.



— То есть это очень специальные люди?



— Здесь важно понимать, что один человек редко способен принимать все возможные технические решения. Это опять же архитектурный вопрос, а архитектура — это командная игра, в эту команду входят и учёные, и инженеры, и каждый со своей стороны привносит своё, предлагаемые технические решения. Таким образом всё это и работает. Ну и плюс, конечно, должен быть плавный запуск.



Как выкатывать фичи на десятки миллионов пользователей



— Что такое плавный запуск?



— Когда какая-то фича выкатывается в Одноклассниках, то, во-первых, она выкатывается в выключенном состоянии. Когда делается апдейт физических серверов, эта фича выключена. Она никак не должна поменять поведение сайта. Потом человек, который эту фичу программировал, начинает её постепенно включать, начиная с небольшого количества пользователей.



— Небольшое количество — это сколько процентов?



— Обычно это зависит от самоуверенности человека. Обычно это начинается либо с одной партиции (то есть 1/256 пользователей), либо с одного сервера, либо по географии берётся небольшая по числу пользователей страна. Если работает хорошо, то включается больше и проверяется на основании той же самой статистики.



— Это общепринятая в целом практика. Но система большая, если ты включил какую-то фичу на одну 256-ую, то получается, что на продакшене работает одновременно две версии (а может, и больше) одного и того же. Как оно вообще дружит друг с другом?



— По-разному. В зависимости от того, что за фича.



— Давай я тогда переформулирую вопрос. Приходится ли тратить дополнительные ресурсы, усилия на то, чтобы решать проблему «сожительства» разных версий фич на продакшене?



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



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



— Обычно тем, что включает эксперименты, занимаются люди с допуском работы на продакшене. Допуск работы на продакшене означает, что человек прошёл некий инструктаж, как надо работать на продакшене и как не надо. Ему объясняют все эти вещи про эксперименты, про то, куда смотреть, как нужно плавно включать, что делать, если что-то пошло не так. А дальше он сам включает свою фичу, смотрит на всё это и разгребает результаты.



— То есть от и до, от написания до внедрения.



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




Original source: habrahabr.ru.

https://habrahabr.ru/post/324424/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Миграция устаревшей информационной системы на платформу CUBA

Вторник, 21 Марта 2017 г. 10:21 (ссылка)



Быстрое развитие технологий и инструментов разработки ПО приводит к тому, что технологии, лежащие в основе информационной системы, теряют свою актуальность и становятся тяжелой ношей. Взять, к примеру, какую-нибудь разработку компании для автоматизации процессов, написанную на Visual Basic 6.0 или Delphi 7, которая, мягко говоря, не сочетается с новыми трендами “все в web, все в облака”, да и не соответствует амбициям разработчиков.



Проблема перевода старой ИС на новые технологии, доходя до руководства, традиционно упирается в деньги: “поживем и так...”. Для разработчиков, в свою очередь, уже перенос модели данных и шаблонное программирование стандартных экранов вызывает негатив. При этом зачастую все усложняется требованием сохранения работоспособности старой ИС на этапе разработки и внедрения новой. Так или иначе, по моему опыту, продукт либо умирает совсем, вызывая мучения как программистов, так и пользователей, либо все же приходит понимание, что обновление ИС — неотложная необходимость.



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



Под катом пошаговая инструкция, как модернизировать устаревшую систему с минимальными усилиями на перенос модели данных и стандартных CRUD экранов.





Для демонстрации мигратора, который начиная с версии 2.1 представлен в CUBA Studio, мы подготовили пошаговую инструкцию о том, как модернизировать устаревшую систему, минимизируя усилия на перенос модели данных и стандартных CRUD экранов. Так как Microsoft официально прекратил поддержку LightSwitch — популярного инструмента для быстрой разработки корпоративных информационных систем — мы выбрали для примера и перенесли на платформу CUBA демонстрационное приложение LightSwitch Vision Clinic и выложили его на GitHub с подробным описанием предпринятых действий. Если у вас есть необходимость или опыт в модернизации ПО, приглашаем вас пройти по предложенному примеру.



Мы будем использовать встроенный в CUBA Studio мигратор для реверс-инжиниринга. Он извлекает метаданные из старой базы и использует их, чтобы автоматически:



1. Сгенерировать модель данных на основе существующей БД,



2. Обновить БД, чтобы создать необходимые системные таблицы CUBA Platform,



3. Сгенерировать стандартный CRUD UI.



Таким образом, основную рутину берёт на себя CUBA Studio. Тем не менее, бизнес-логику и дизайн экранов, отличный от стандартного, в новое приложение нужно переносить вручную.



Если вам просто интересно посмотреть, что у нас получилось в итоге, пролистайте до раздела Можно ли запустить приложение без MSSQL?



Пара слов об исходном приложении



Теперь о том, что представляет собой легаси-приложение, которые мы выбрали жертвой для миграции. Мы скачали демо-приложение для абстрактной глазной клиники с официального сайта Microsoft. Оно написано в среде разработки Microsoft Lightswitch, поддержку которого Microsoft официально прекратил несколько месяцев назад.



По сути, это стандартное 3-слойное приложение, демонстрирующее основные фишки LightSwitch: создание модели данных, скаффолдинг экранов и вычисляемые атрибуты сущностей. Пример VisionClinic хранит данные одновременно в 2 разных БД, связанных между собой. Для упрощения, мы объединили эти две базы в одну.



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





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



Вот как выглядит пользовательский интерфейс приложения Lightswitch. Нам больше всего понравился экран Products list — он чуть посложнее остальных:





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



Приложение на базе CUBA



Забегая вперёд, скажу, что мы хотим в итоге получить. Мы создадим полностью функциональное приложение на Java, традиционно состоящее из 3 слоёв, с полноценным веб-клиентом, повторяющим UI и бизнес-логику исходного приложения Lightswitch. Как уже говорилось, приложение не изменит структуру существующих таблиц, так что оба приложения смогут работать параллельно на одном экземпляре БД.



На скриншоте ниже показан тот же экран продуктов, но уже в CUBA-приложении, можете сравнить:





Шаг 1. Установка окружения и Базы Данных



1. Скачаем и установим платформу CUBA согласно Инструкции по установке из документации на платформу.



2. Скачаем и установим MS SQL Server 2012+ с официального сайта. Пропустите этот шаг, если сервер у вас уже установлен.



3. Выполним скрипт create-db.sql на экземпляре MS SQL DB, чтобы создать базу данных VisionClinic.



4. Выполним скрипт insert-data.sql на свежесозданной БД VisionClinic, чтобы заполнить её тестовыми данными.



5. Разрешим SQL Server and Windows Authentication mode и вход от имени пользователя sa, как показано здесь. Запомним пароль для sa, он будет нужен для подключения к базе из приложения CUBA.



Шаг 2. Создание проекта на CUBA



1. Запустим сервер CUBA Studio (1) и откроем Studio в браузере (2).





2. Создадим новый проект (1), введём vision-clinic в поле имени проекта (2) и нажмём OK.





3. В секции Project properties установим подключение к базе данных VisionClinic и заполним следующие поля:




  • Database type — Microsoft SQL Server 2012, здесь укажите вашу установленную версию (1)

  • Database URL — localhost, или доменное имя/IP компьютера с установленным SQL Server (2)

  • Database name — visionclinic, это имя было создано при выполнении скрипта create-db.sql в шаге 1, пункт 3 (3)

  • Database user и Database password — используйте свои параметры входа для sa в SQL server, или другого пользователя с правами администратора (4)





4. Нажмём Test connection (1) и проверим, подключилась ли Studio к нужной нам базе. Получив сообщение Connection successful (2), нажмём OK (3) для сохранения введённых параметров.





Шаг 3. Мигратор — Модель данных и генерация CRUD UI



На этом шаге мы сгенерируем модель данных (т.е. сущности), основанную на структуре существующей БД, и простой пользовательский интерфейс для манипуляций с данными.



1. Перейдём на вкладку DATA MODEL и нажмём на ссылку Generate model.



2. CUBA Studio запросит обновление базы данных, чтобы создать свои таблицы, необходимые для работы платформы, как то: настройки безопасности, аудит состояния, динамические атрибуты, и т.д. Подтвердим обновление.





3. Появляется окно GENERATE MODEL FROM DATABASE. Здесь мы настроим маппинг таблиц базы данных и их столбцов к сущностям и их полям.



4. Нажмём на кнопку Settings в верхней панели, чтобы настроить общие параметры маппинга. В нашем демо-приложении оба фреймворка позволяют отслеживать, кто и когда создал или изменил запись в базе, поэтому мы связываем предопределённые кубинские поля createTs, createdBy, updateTs, updatedBy с соответствующими системными столбцами в существующей базе, которые есть почти во всех её таблицах: Created (1), CreatedBy (2), Modified (3), ModifiedBy (4).



RowVersion (5) — ещё один системный столбец, который в Lightswitch используется для оптимистической блокировки. К сожалению, его нельзя привязать к столбцу version, используемому в платформе для той же цели, так как типы этих полей не совпадают. Для поддержки оптимистической блокировки в приложении CUBA можно создать другое поле, но раз мы решили ничего не менять в структуре таблиц, то просто удалим этот столбец из маппинга и нажмём ОК.





5, Нажмём на кнопку Show tables в верхней панели и выберем таблицы, по которым будут созданы сущности в новом приложении. Выберем все кнопкой Select all и снимем выделение с sysdiagrams (1), — это служебная таблица MS SQL, в которой хранится схема базы данных, для миграции нам она не нужна. Нажмём Next (2).





6. На этом этапе можно настроить маппинг более тонко, на уровне отдельных сущностей и их полей. К примеру, CUBA предусматривает, что имена всех сущностей должны быть в единственном числе, так как Studio на их основе генерирует основные строки UI, такие как имена экранов и заголовки пунктов меню. Поэтому переименуем сущности, убрав отовсюду наследие множественного числа. Выберем таблицу Appointments, нажмём на Edit mapping button, отредактируем имя класса и сохраним изменения нажатием OK.





7. Повторим этот шаг для других сущностей, имеющих множественное число в имени: InvoiceDetails, Invoices и Patients.



8. Также на этом этапе можно указать для сущности её Instance Name — строковое представление сущности (записи), которым она будет представлена в UI, например, в ячейках таблиц или выпадающих списках. Выберем таблицу Product, нажмём Edit mapping, выберем атрибут ProductName в поле Instance name и нажмём OK.





9. Все эти параметры можно будет исправить и позднее, поэтому не страшно, если мы что-то пропустили или где-то ошиблись. Нажимаем Next.



10. Теперь можно автоматически создать CRUD-экраны для сущностей, обеспечивающие доступ к данным. Платформа CUBA предлагает несколько стандартных экранных шаблонов:




  • Browser — экран просмотра списка записей из базы, в него по умолчанию входит фильтр данных и поддерживается пагинация;

  • Editor — экран просмотра и редактирования отдельной записи;

  • Browser and Editor — всего лишь означает, что будут созданы оба экрана;

  • Single screen — встраивает редактор в экран браузера, таким образом, слева отображается список всех записей, а справа — детали выбранной записи.



Примечание: Начиная с версии 6.4, платформа поддерживает создание пользовательских шаблонов экранов. Также любой экран можно впоследствии отредактировать в Studio и IDE.



11. Выберем следующие шаблоны и нажмём Next:





На экране скриптов обновления БД никаких скриптов мы не видим. Всё как мы и планировали, исходный таблицы остаются без изменений. Нажмите Save и подождите, пока платформа сгенерирует модель данных и экраны CRUD.



Шаг 4. Первый запуск



Начиная с этого этапа, мы уже можем запустить приложение и посмотреть на свежесозданный стандартный UI.



1. Запустим приложение CUBA командой Run — Start application в основном меню.





2. По ссылке Web client в левом нижнем углу Studio перейдём в приложение, которое откроется в новой вкладке браузера.





3. Логин и пароль по умолчанию — admin/admin, подтверждаем и входим в приложение.





4. Созданные нами экраны доступны из меню Application в верхней части приложения.





5. Откроем экран Products и посмотрим, как он выглядит со стандартной разметкой шаблона single screen.





Конечно, понадобится ещё какое-то время на перенос бизнес-логики и оформление интерфейса, но на этом этапе у нас уже есть первая версия кросс-платформенного приложения, полностью основанная на опен-сорс технологиях.



Дальнейшая разработка



Что делать с приложением дальше, тут у вас есть полная свобода выбора. Платформа CUBA Platform не запрещает изменять генерированный код приложения, равно как и исходный код самой платформы. Вы также можете интегрировать в приложение любые сторонние технологии из мира Java.



К примеру, вы могли заметить, что на стандартном экране просмотра Product не хватает информации о скидках, таблицы похожих продуктов и изображений. С помощью платформы и CUBA Studio можно легко привести экран к тому виду, который он имел в исходном приложении LightSwitch.



Исходники дополненной сущности Product и дескриптора экрана Product можно посмотреть в проекте на GitHub. Хочу отметить, что все изменения в декларации сущности и дескрипторе экрана были созданы в визуальном редакторе CUBA Studio.





Все ручные изменения в коде прописаны в контроллере экрана. Также вы можете посмотреть пример создания вычисляемых атрибутов в исходном коде сущности Product (см. метод Product#getCurrentPrice).



Можно ли запустить приложение без MSSQL?



По умолчанию, наше приложение на GitHub настроено для использования с легковесной HyperSQL Database. Поэтому, просто склонировав его из репозитория, вы можете его запустить, и при старте Studio сама создаст требуемую базу данных. Правда, эта база будет пустой, и вам придётся наполнить её самостоятельно через пользовательский интерфейс приложения.



Если же вы хотите изменить тип используемой БД, в CUBA Studio это реализовано весьма прямолинейно: для миграции на другую DBMS откройте настройки проекта в секции Project Properties и выберите тип базы данных из выпадающего списка. Studio создаст новые скрипты создания и обновления базы данных, и вы сможете запустить приложение с любой выбранной БД.





Резюмируя, если у вас есть необходимость или опыт в модернизации ПО, приглашаем вас оценить инструмент и, возможно, предложить варианты по улучшению механизма миграции. Будем рады получить обратную связи на нашем форуме поддержки.


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/324226/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Пишем Java Stream API на коленке за пару минут

Пятница, 17 Марта 2017 г. 16:12 (ссылка)

Stream API — замечательная вещь быстро завоевавшая популярность у джава программистов. Лаконичные однострочники обрабатывающие коллекции данных посредством цепочек простых операций map, filter, forEach, collect оказались очень удобны. Операции над парами ключ-значение, конечно, тоже не помешали бы, но увы.



В целом примерно понятно как это всё устроено, но все же зачастую ответ на вопрос «А как бы это написал я?» здорово помогает понять внутренние механизмы той или иной технологии. Так получилось, что внезапно для себя я ответил на этот вопрос применительно к Stream API, историей изобретения этого велосипеда и спешу с вами поделиться.



Пока я спокойно себе писал на свинге компоненты IDE, мир менялся — javascript захватывал сферу разработки UI. И захватил. Как ни крути, качественный рантайм на абсолютно каждой машине — сильный аргумент. Ничего не поделаешь, пришлось разбираться. В джава скрипте пользовательский код выполняется в одном потоке, поэтому все сколько нибудь длительные операции асинхронны. И если наша бизнес логика предполагает последовательность таких операций, то первой из них надо передать колбэк, который запустит вторую, которой передать колбэк, который выполнит третью и так далее. В общем, читается ужасно, поддерживается мучительно, js разработчики как-то пытаются с этим жить и придумывают обходные подходы, один из встретившихся мне головоломных вариантов — использование генераторов. Так я про них и узнал.



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



И так, если бы джава разработчик захотел сделать некое подобие генератора, что бы у него получилось? У меня получилось так:



    public interface Generator {
void generate(GeneratorContext context);
}

public interface GeneratorContext {
void emit(T value);
}


Идея понятна, генерацией занимается метод generate(...), ему передаётся параметром некий контекст и последовательно вызывая его метод emit(...) можно возвращать множественные значения.



Определенно, данные генерируемые данным генератором образуют сущность, назовём её Dataset:



public class Dataset {

private final Generator generator;

private Dataset(Generator generator) {
this.generator = generator;
}
}


И если в наличии есть набор данных, то неплохо бы иметь возможность что-нибудь сделать с каждым их элементом. Напечатать там или ещё что. Добавляем в класс Dataset метод forEach:



    public void forEach(Consumer consumer) {
generator.generate(value -> consumer.accept(value));
}


Мы сформировали такой контекст генератора, что на каждый вызов метода еmit, он передаёт эмитированное значение в consumer, и запустили генерацию.



Осталось откуда-нибудь добыть инстанс датасета и можно испытывать. Добавляем фабричный метод, который создаёт генератор из коллекции и оборачивает его в датасет:



    public static  Dataset of(Collection collection) {
return new Dataset<>(generatorContext ->
collection.forEach(item -> generatorContext.emit(item))
);
}


То же самое с помощью старого доброго цикла:



    public static  Dataset of(Collection collection) {
return new Dataset<>(generatorContext -> {
for (T item : collection) {
generatorContext.emit(item);
}
});
}


Попросту пробежали по коллекции и эмитировали каждый элемент. Уже можно запускать:



        Dataset.of(Array.asList(“foo”, “bar”)).forEach(System.out::println);


Вывод:

foo

bar


А теперь, по сути, та самая популярная задача с собеседований: дополним набор данных элементами ещё одной коллекции. Добавляем метод:



    public Dataset union(Collection collection) {
return new Dataset<>(generatorContext -> {
generator.generate(generatorContext);
iterable.forEach(item -> generatorContext.emit(item));
});
}


Создали новый датасет с таким генератором, который сначала эмитирует все значения текущего датасета, а потом все значения присоединяемой коллекции.



Фильтруем:



    public Dataset filter(Predicate predicate) {
return new Dataset<>(generatorContext -> generator.generate(value -> {
if (predicate.test(value)) {
generatorContext.emit(value);
}
}));
}


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



И, наконец, преобразуем каждый элемент множества данных:



    public  Dataset map(Function function) {
return new Dataset<>(generatorContext -> generator.generate(
value -> generatorContext.emit(function.apply(value))
));
}


Создали новый датасет, каждый элемент которого будет сгенерирован посредством преобразования элементов данного датасета. Но элементы данного датасета как таковые не существует, они ещё должны быть сгенерированы. Да, это ленивые вычисления.



Теперь запускаем всё вместе.



         Dataset.of(Arrays.asList("шла", "саша", "по", "шоссе"))
.union(Arrays.asList("и", "сосала", "сушку"))
.filter(s -> s.length() > 4)
.map(s -> s + ", length=" + s.length())
.forEach(System.out::println);


Вывод:

шоссе, length=5

сосала, length=6

сушку, length=5


Пора рефакторить. Первое, что бросается в глаза: интерфейс GeneratorContext можно заменить стандартным Consumer-ом. Заменяем. Местами и код сократится, так как ранее нам приходилось оборачивать Consumer-ы в GeneratorContext.



Тут стоит остановиться и обратить внимание на определенную схожесть нашего Dataset и java.util.stream.Stream, что наводит на мысли и о родстве нашего Generator-а с загадочными Spliterator-ами из платформы джава.



Велосипед готов. Надеюсь, вам тоже стали немного понятнее внутренние механизмы Stream API, роль Spliterator-а и способ организации ленивых вычислений.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/324228/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Диагностика утечек памяти в Java

Четверг, 16 Марта 2017 г. 17:35 (ссылка)

В данной заметке я хочу показать каким образом можно определять и устранять утечки памяти в Java на примере из моей повседневной работы. Мы не будем здесь рассматривать возможные причины появления утечек, об этом будет отдельная статья, так как тема достаточно обширная. Стоит заметить, что речь пойдет о диагностике именно Heap Memory, об утечках в других областях памяти будет отдельная статья.



Инструменты



Для успешной диагностики нам понадобятся два инструмента: Java Mission Control (jmc) и Eclipse Memory Analyzer. Вобщем-то можно обойтись только Memory Analyzer, но с JMC картина будет более полной.


  • JMC входит в состав JDK (начиная с 1.7)

  • Memory Analyzer может быть загружен отсюда: MAT



Анализ использования памяти



Прежде всего, нужно запустить приложение со следующими флагами JVM:

-XX:+UnlockCommercialFeatures

-XX:+FlightRecorder




Эти опции позволят запустить Flight Recorder – утилита, которая поможет собрать информацию об использовании памяти (и много другой важной информации) во время выполнения программы. Я не буду описывать здесь как запустить Flight Recorder, эта информация легко гуглится. В моем случае было достаточно запустить FR на 10-11 минут.



Рассмотрим следующий рисунок, на котором показана классическая «пила» памяти, а так же важный сигнал, что что-то не так с использованием памяти:



Запись Fight recorder



Можно увидеть, что после каждого цикла очистки памяти, heap все больше заполняется, я выделил это желтым треугольником. «Пила» все время как бы ползет вверх. Это значит, что какие-то объекты не достижимы для очистки и накапливаются в old space, что со временем приведет к переполнению этой области памяти.



Выявление утечки



Следующим шагом нужно выявить, что именно не доступно для очистки и в этом нам поможет Memory Analyzer. Прежде всего, нужно загрузить в программу heap dump работающего приложения с предполагаемой утечкой памяти. Это можно сделать с помощью «File -> Acquire Heap Dump». После загрузки в диалоге «Getting Started Wizard» выбрать «Leak Suspects Report» после этого откроется краткий обзор возможных утечек памяти:



Leak suspects report



Если вернуться на вкладку «Overview» и выбрать «Denominator Tree», то можно увидеть более подробную картину:



Overview



Denominator tree



Дерево показывает структуру «тяжелого» объекта, а так же размер его полей (по типу). Можно видеть, что одно из полей объекта MasterTenant занимает более 45% памяти.



Устранение утечки



Имея результат анализа из предыдущего пункта, следующим шагом идет устранение накапливания объектом памяти. Тут все сильно зависит от конкретного кода. Общая рекоменация – нужно найти и проанализировать все места, где происходит инициализация или изменение соответствующего поля или полей, чтобы понять механизм накапливания памяти. В моем случае в коллекцию постоянно добавлялись записи из множества (около 150) потоков при определенных условиях.



После находжения и устранения утечки, не лишним будет пройти все шаги снова, проанализировать память и отчет Memory Analyzer, чтобы убедиться что фикс помог.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/324144/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Перевод] Тёмный путь

Четверг, 16 Марта 2017 г. 16:06 (ссылка)

Предлагаю вашему вниманию перевод оригинальной статьи Роберта С. Мартина.



За последние несколько месяцев я попробовал два новых языка. Swift и Kotlin. У этих двух языков есть ряд общих особенностей. Действительно, сходство настолько сильное, что мне стало интересно, не является ли это новой тенденцией в нашей языкомешалке. Если это действительно так, то это тёмный путь.



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



Проблема в том, что оба языка сделали ставку на сильную статическую типизацию. Кажется, оба намерены заткнуть каждую дыру в своём родном языке. В случае со Swift – это странный гибрид C и Smalltalk, который называется Objective-C; поэтому, возможно, упор на типизацию понятен. Что касается Kotlin – его предком является уже довольно строго типизированная Java.



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



Дело не в том, что меня беспокоит, что Swift и Kotlin статически типизированы. Скорее меня беспокоит глубина статической типизации.



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



Swift и Kotlin, с другой стороны, становятся абсолютно непреклонными, когда дело доходит до их правил типов. Например, в Swift, если вы объявите функцию, которая бросает исключение, то каждый вызов этой функции, вплоть до начала древа вызовов, должен быть обёрнут в блок do-try, или try!, или try?. В этом языке нет способа тихо выбросить исключение вплоть до верхнего уровня, без прокидывания через все древо вызовов. (Вы можете посмотреть, как Джастин и я боремся с этим, в наших видеоматериалах Mobile Application Case Study)



Возможно, вы думаете, что это хорошо. Возможно, вы думаете, что в системах было много ошибок, вызванных некорректными исключениями. Возможно, вы считаете, что исключения, которые не сопровождаются, шаг за шагом, вверх по стеку вызовов, являются рискованными и подвержены ошибкам. И, конечно, вы были бы правы. Необъявленные и неуправляемые исключения очень рискованны.



А теперь вопрос. Кто должен разруливать все эти риски? Язык? Или это работа программиста?


В Kotlin, вы не можете наследоваться от класса, или переопределить функцию, пока вы не отметите этот класс или функцию ключевым словом open. Вы также не можете переопределить функцию, если она не отмечена ключевым словом override. Если вы не объявите класс как открытый для наследования, язык не позволит вам наследоваться от такого класса.



Возможно, вы думаете, что это хорошо. Возможно, вы считаете, что иерархии наследования, которым разрешено расти без ограничений, являются источником ошибок и риска. Возможно, вы думаете, что можно устранить целые классы ошибок, заставив программистов явно объявлять их классы открытыми для наследования. И вы можете быть правы. Наследование — рискованная вещь. Что-то может пойти не так, при переопределении функции в классе-наследнике.



А теперь вопрос. Кто должен разруливать все эти риски? Язык? Или это работа программиста?


Оба языка, Swift и Kotlin, включают в себя концепцию обнуляемых типов (nullable). Тот факт, что переменная может содержать null, становится частью типа этой переменной. Переменная типа String не может содержать значение null, она может содержать только конкретную строку. С другой стороны, переменная типа String? имеет обнуляемый тип и может содержать null.



Правила языка настаивают на том, что когда вы используете переменную, допускающую значение null, вы должны сначала проверить эту переменную на null. Так что если s это String? тогда var l = s.length() не будет компилироваться. Вместо этого вам следует писать так: var l = s.length() ?: 0 или var l = if (s!=null) s.length() else 0.



Возможно, вы думаете, что это хорошо. Возможно, вы видели довольно много NPE в вашей жизни. Возможно, вы знаете, без тени сомнения, что непроверенные null`ы являются причиной сбоев программного обеспечения на миллиарды и миллиарды долларов. (Действительно, документация Kotlin называет NPE «Billion Dollar Bug»). И, конечно, вы правы. Очень рискованно иметь неконтролируемые null`ы повсюду.



А теперь вопрос. Кто должен разруливать все эти null`ы? Язык? Или это работа программиста?


Эти языки подобны маленькому голландскому мальчику, затыкающему пальцами дырки в дамбе. Каждый раз, когда появляется новая ошибка – в язык добавляется новая функция для предотвращения такой ошибки. И поэтому эти языки накапливают все больше и больше пальцев в дырах в дамбах. Проблема в том, что в итоге закончатся пальцы рук и ног.



Но пока пальцы на руках и ногах не закончились, мы создаём языки, которые содержат десятки ключевых слов, сотни ограничений, извилистый синтаксис и справочное руководство, которое читается как книга закона. Действительно, чтобы стать экспертом в этих языках, вы должны стать юристом по языку (термин, который был изобретен в эпоху C++).



Это неверный путь!


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



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



И что же программисты должны делать для предотвращения дефектов? Я загадаю вам загадку. Вот пара подсказок. Это глагол. Он начинается на букву «Т». Да. Вы поняли. ТЕСТИРОВАТЬ!



Вы пишете тесты, чтобы ваша система не возвращала неожиданные значения null. Вы пишете тесты, чтобы ваша система обрабатывала null во всех входных данных. Вы пишете тесты, чтобы каждое исключение, которое вы можете выбросить, было где-то обработано.



Почему эти языки используют все эти функции? Потому что программисты не покрывают тестами свой код. И поскольку программисты не тестируют свой код, у нас теперь есть языки, которые заставляют нас ставить слово open перед каждым классом, от которого мы хотим наследоваться. Теперь у нас есть языки, которые заставляют нас оборачивать каждую функцию, сквозь всё древо вызовов, в блок try!. Теперь у нас есть языки, которые настолько ограничены и настолько переобусловлены, что нужно проектировать всю систему заранее, прежде чем начать кодить.



Рассмотрим пример. Как узнать, открыт ли класс для наследования или нет? Как я узнаю, что где-то вниз по древу вызовов кто-то может выбросить исключение? Сколько кода мне придется изменить, когда я наконец узнаю, что кто-то действительно должен вернуть null в древе вызовов?



Все эти ограничения, налагаемые этими языками, предполагают, что программист обладает совершенным знанием системы, до того как начать писать её. Они предполагают, что вы знаете, какие классы должны быть открыты для наследования, а какие нет. Они предполагают, что вы знаете, какие вызовы будут генерировать исключения, а какие — нет. Они предполагают, что вы знаете, какие функции будут возвращать null, а какие нет.



И из-за всего этого есть основания полагать, что они наказывают вас, когда вы неправы. Они заставляют вас вернуться назад и изменить огромное количество кода, добавив try! или ?: или open сквозь всё древо вызовов.



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



И поэтому вы объявляете все свои классы и все свои функции открытыми для наследования. Вы никогда не используете исключения. И вы привыкаете к использованию большого количества символов ! для переопределения проверок на null и позволяете NPE расплодиться в своих системах.






Почему атомная станция в Чернобыле загорелась, расплавилась, разрушила небольшой город и оставила большую территорию непригодной для жизни? Они переопределили все меры предосторожности. Так вот, не надо полагаться на безопасность, чтобы предотвратить катастрофу. Вместо этого лучше привыкнуть к написанию большого количества тестов, независимо от того, какой язык вы используете!


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/324122/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Насколько гибкими являются наши знания в области операторов Rx?

Четверг, 16 Марта 2017 г. 14:37 (ссылка)

RxJava набирает все большую популярность в нынешнее время. На ней написаны многие библиотеки для Java и Android, а обладание знаниями в этой области стало синонимом избранности. В резюме строка с описанием того что вы спец в реактивном программировании поднимает вашу привлекательность для работодателей перед вашими конкурентами.



И вот я тоже решил присоединиться к этому течению, освоить и начать применять в своих проектах RxJava. В процессе чтения нескольких книг и статей все было абсолютно понятно. «Эти знания и правда в разы укоротят код и придадут читабельности» — думал я. Но как только книги были закончены и я сел переписывать свой проект на реактивщину — стало понятно что я не понимаю даже с чего начинать…



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



Если вы считаете себя специалистом в этой области — открывайте IDE и попробуйте решить пять задачек. Предполагается что вы обладаете начальными знаниями и с легкостью напишете простой Observable. Только давайте условимся — не подсматривайте в решение сразу! Ведь уверенные знания рождаются только в усердной практике. В данных задачах не буду проводить описание операторов, которые были использованы. Если какие то задачи вызывают затруднения — просто остановитесь — загляните сюда и попробуйте выбрать тот оператор, который поможет вам решить нужную задачу.



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



Задача 1:



У вас есть список городов, уложенный в контейнер List. Необходимо вывести его на печать.



Исходные данные:



List cities = Arrays.asList("Париж", "Лондон", "Рим", "Москва");


Что в результате должны увидеть в консоли:



город Париж

город Лондон

город Рим

город Москва



Решение:
Observable.from(list)
.map(s -> "город "+s)
.subscribe(System.out::println);




Задача проще простого! Уверен вы решили еще мгновенно. Давайте попробуем что нибудь посложнее.



Задача 2:



У вас есть список городов и стран, уложенных в контейнеры List. Необходимо вывести список, упорядоченный по алфавиту, в формате <название города> — столица <название страны> на печать.



Исходные данные:



List cities = Arrays.asList("Париж", "Лондон", "Рим", "Москва");
List countries = Arrays.asList("Франция", "Англия", "Италия", "Россия");


Что в результате ожидаем увидеть в консоли:



Лондон — столица Англии

Москва — столица России

Париж — столица Франции

Рим — столица Италии



Решение:
Observable.from(countries)
.map(s -> s.substring(0, s.length() - 1).concat("и"))
.zipWith(cities, (country, city) -> city+" - столица "+country)
.toSortedList()
.flatMap(Observable::from)
.subscribe(System.out::println);


Задача 3:



Вывести на печать все простые числа меньше ста.



Для справки:



Вдруг кто забыл — простые числа те, что делятся без остатка только на себя и на единицу. 0 и 1 за простые числа принято не считать



Исходные данные:



Для облегчения задачи — метод для определения простого числа:



private boolean isPrime(int n) {
for(int i=2; icode>


Что в результате должны увидеть в консоли:



2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97



Решение
Observable.range(0, Integer.MAX_VALUE)
.skip(2)
.filter(this::isPrime)
.takeWhile(integer -> integer < 100)
.map(String::valueOf)
.reduce((s1, s2) -> s1+" "+s2)
.subscribe(System.out::println);




Задача 4:



Определить какие числа, в промежутку от 1 до 20 делятся без остатка на 3 и 7 соответственно.



Исходные данные:



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



private int whatDivider(int n) {
if (n % 3 == 0) return 3;
if (n % 7 == 0) return 7;
return 0;
}


Что в результате должны увидеть в консоли:



Делится без остатка на 3: 3 6 9 12 15 18

Делится без остатка на 7: 7 14



Решение:
Observable.range(1, 20)
.groupBy(this::whatDivider)
.filter(observable -> observable.getKey() != 0)
.subscribe(observable -> observable
.map(String::valueOf)
.reduce((s1, s2) -> s1+" "+s2)
.map(s -> "Делится без остатка на "+observable.getKey()+": "+s)
.subscribe(System.out::println));




Задача 5:



Необходимо найти сумму первых десяти членов ряда Фибоначчи.



Для справки:



Ряд Фибоначчи — каждое последующее число равно сумме двух предыдущих чисел, и первые десять членов ряда его — 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, и их сумма равна 88.



Исходные данные:



Для решения задачи я воспользовался написанным рядом классом Pair:



private static class Pair {
final int n1;
final int n2;

Pair(int n1, int n2) {
this.n1 = n1;
this.n2 = n2;
}
}


Если получится обойтись без него — будет рад поучиться у вас!



Что в результате должны увидеть в консоли:



88



Решение:
Observable.range(0, 9)
.map(integer -> new Pair(0, 1))
.scan((source, result) -> new Pair(source.n2, source.n1 + source.n2))
.map(pair -> pair.n2)
.reduce((integer, integer2) -> integer + integer2)
.subscribe(System.out::println);




Если ваши решения получились лаконичнее, чем приведены здесь — пишите в комментариях, буду рад подчерпнуть новые способы, так как я не претендую на абсолютные знания в области Rx и сам только учусь. Пишите если вам понравилось, на примете есть еще цикл задачек с использованием кастомных операторов, и если вам будет интересно — попробую продолжить идею в следующих статьях.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/324106/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<java - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda