Андрей — эксперт в системном программировании, занимался системами хранения и системами передачи информации. Складывал кирпичики, которые лежат в основе портала Одноклассники и обеспечивают надежность и быстродействие сервисов.
Вот о чем мы поговорили с Андреем:
- чего стоит переезд с Java 7 на Java 8;
- sun.misc.Unsafe
- архитектуре Одноклассников
- инженерные компромиссы, шардинг и сборка мусора
- системы хранения данных и Cassandra;
- в чем Одноклассники впереди планеты всей, а чему еще стоит поучиться у Google;
- как стать крутым системным программистом
Для тех, кому опять некогда смотреть видео, под катом расшифровка интервью.
Про переезд с Java 7 на Java 8
— Работая в недрах Sun, ты смотрел на Java изнутри, а перейдя в Одноклассники, стал смотреть с клиентской стороны. Ты повидал (как прикладной разработчик) Java 6, 7, 8. Твое ощущение: стало лучше, хуже? Нет ли у тебя, например, ощущения, что JDK 8 — это продукт, который явно качественней, чем JDK 7, или, может, наоборот?
— Баги есть во всех версиях, к этому уже привыкли. Понятно, что JVM — это очень сложная система. И порой даже нельзя предсказать заранее, как сработает JIT-компилятор в том или ином сценарии. Самое главное, что у нас 99% портала на этом всем работает, и крупных падений с 2013 года не было. Поэтому мы довольны Java.
— А какой процент Одноклассников, если не секрет, работает на Java 7, а какой — на Java 8?
— У нас до сих пор остались системы даже на Java 6. К счастью их, совсем мало. Есть такие сервисы, которые годами не перезапускаются. Какой смысл их апгрейдить? Работают и работают. А так, все что запускаем сейчас новое, сразу же запускаем на JDK 8. Те службы, те сервисы, которые часто деплоятся, тоже уже почти все переехали на JDK 8. Сейчас уже, наверно, процентов 60 на «восьмерке» работает. И каждую неделю что-то новое переводится.
— Какие сложности есть при миграции с Java 7 на Java 8?
— На удивление, проблем при переходе с Java 7 на Java 8 мы видели гораздо меньше, чем когда мигрировал с Java 6 на Java 7. Там некоторые вещи вообще по-разному работали: например, сортировка начинала выкидывать исключения там, где этого раньше не было, были изменениями в поддержке Юникода и т.д.
При переходе на Java 8 таких проблем уже не было. Но мы напоролись на баги в JIT -компиляторе.
Вообще, порой создается впечатление, что если есть какой-то баг, то мы на него обязательно напоремся: видимо, что у нас столько серверов и такие нагрузки, что мы можем лучше протестировать Java, чем сами Oracle.
Например, сейчас мы отключили у себя в продакшене многоуровневую компиляцию, которую так старательно делали ребята из Oracle: в С1-компиляторе есть критичный баг, поэтому мы сразу компилируем в С2.
— А этот баг уже пофиксили?
— Да, вроде как в JDK 8u60 решили проблему. Надо отдать должное, в JIT-компиляторе парни из Oracle довольно оперативно подчищают проблемы. Но там были серьезные уязвимости: можно было написать простейший Java-код, который с крашем уронит всю виртуальную машину. К слову, в версии 8u40 были пофикшены два бага в компиляторе, зафайленные (заведенные — прим. автора) мной.
Про виртуализацию, Unsafe и Одноклассники
— А насколько вообще большой, как система, проект Одноклассники? Сколько там подсистем, слоев?
— Очень много. Модулей около двухсот, и самые крупные насчитывают по десять тысяч файлов Java-классов. Можно встретить в исходниках комментарии, датированные даже 2004-м годом. Но, как известно, Одноклассники начинались на С#, а потом команда из нескольких человек буквально за пару месяцев напряженных дней и бессонных ночей переписала все. То есть от прежней версии Одноклассников не осталось ничего.
— Сейчас Одноклассники – это высоконагруженный проект на Java, самый большой Java-проект в России. Много ли в мире похожих проектов?
— Знаю, что по трафику мы входим во всемирный Top-100 по версии Alexa. А в России сейчас седьмые, насколько я помню. Наверное, из Java-проектов – мы одни из крупнейших.
— ВКонтакте и Facebook, у которых каких-то больших частях было много PHP-кода, выпустили свои виртуальные машины под PHP. Откуда вообще этот тренд на виртуализацию?
— Виртуализация — это удобно! В широком смысле, она дает тебе работать с тем слоем, с которым привычнее. Конечного разработчика не должно волновать, в какие микроинструкции, архитектуры это выливается и как на процессе обрабатывается.
— Помню, в 2012 году ты много рассказывал про Unsafe. А сейчас, наверное, про Unsafe в Java-тусовке не говорит только ленивый. Я тебя хочу обвинить в том, что ты пропагандируешь небезопасные техники программирования.
— Я их не пропагандирую — я их использую! Потому что не все, что нам нужно, есть в Java.
Java изначально задумывалась как аппаратно-независимая платформа: написал один раз, запускаешь, где хочешь. Но вот мы в Одноклассниках все запускаем на 64-битном Linux’e, на интеловских процессорах: и нам такая портируемость Java не нужна, но зато мы хотим использовать по-максимуму возможности операционной системы и железа, на котором запускаемся. И для этого приходится проковыривать лазейки вглубь операционной системы.
Вот, навскидку, пример. У нас есть куча кэшей – вообще, из 8 тысяч серверов, что есть у Одноклассников, чуть ли не половина – это всевозможные кэши. И тогда, когда пишутся снэпшоты, данные из оперативной памяти скидываются на диск. Если это делать чисто средствами Java, дико возрастает расход оперативной памяти. И только Linux-специфичным хинтом можно сказать ей: «Не надо, не кэшируй эти данные, они нам в ближайшее время не понадобятся».
— Это нельзя сделать просто настройкой Linux’a? Я просто не очень понял, зачем лезть именно в приложение, почему эта ручка находится на его стороне?
— Для Linux все одинаково: что Java файлы пишет, что другие процессы в системе. Сам программист должен различать, что он хочет кэшировать, а это – нет. Поэтому и инициатива должна исходить со стороны приложения.
Про высокие нагрузки
— Какие части Одноклассников — самыми высоконагруженные?
— Один из самых нагруженных – это сервис Ленты, та страничка, что открывается как новостная лента, когда пользователь заходит на главную. Тут под каждого собираются данные из разных источников.
Другой пример — мгновенная доставка push-оповещений о «дружбах», новостях, сообщениях, подарках. Там на один сервер приходится до 50000 запросов в секунду.
Часть push-нотификаций реализована фактически на long polling — со стороны клиента остаются длинные http-запросы, которые подвисают либо на 10 минут, либо до тех пор, пока не придут данные. А на стороне бэкэнда есть отдельный сервис, который в каждый момент времени знает, где, на каком фронтэнде или фронтэндах сидит какой пользователь. То есть, у человека может быть открыто несколько клиентов: на мобильном устройстве, на веб-версии портала. Всего таких машин, чтобы вы понимали, около тысячи.
— Получается, что это существенный процент от всех восьми тысяч серверов. А почему не используете WebSocket’ы, например?
— в отличие от WebSocket’ов Long polling поддерживается почти везде. Конечно, есть устаревшие, древние браузеры. Internet Explorer 8, например. Но от него мы в скором времени хотим отказаться.
Про нормализацию и денормалицацию
— В мире Enterprise’a обычно много Java EE, много Hibernate’a, Spring’a и так далее. А у вас какие технологии?
— Один из наших крупнейших модулей, который отвечает за большую часть бизнес-логики, традиционно называется odnoklassniki-ejb. Но на самом деле, на сегодня от EJB там не осталось ровным счетом ничего. Я себе сам такую задачу поставил в прошлом году — выпилить EJM из проекта. Был у нас трехдневный хакатон, где разработчики могли выбирать себе любой проект, на который обычно не хватает времени. И вот я решил полностью избавиться от Enterprise’a в нашем главном модуле. И сейчас это обычное Java-приложение. Вот так.
А насчет Spring — Spring у нас много.
— А что с Hibernate?
— Нет-нет, только не он! Нам нужен очень точный, полный контроль над тем, какие запросы и как мы исполняем. У нас часть систем, часть хранилищ, остались традиционно на Microsoft SQL Server, но сейчас все больше и больше переходим в сторону новых SQL-решений.
Чтобы SQL-базы справлялись с нагрузкой, приходится отказываться от ряда возможностей. В частности, мы не используем джойны, мы не используем триггеры, хранимые процедуры.
— То есть студентов учат «нормализуйте», потом они приходят к вам, и вы говорите «денормализуйте»?
— Примерно так, да. Конечно, там, где есть возможность перенести нагрузку с SQL-серверов на сервер бизнес-логики, мы ее переносим: просто потому, что платим за процессорные мощности, на которых работают наши SQL-серверы.
— Почему? И что с join'ами не так?
— У нас базы распределенные и партриционированные. Как ты сделаешь join, если у тебя на каждом сервере лежит 1/16 часть данных?
— А современные системы с шардингом плохо работают? Казалось бы, наверняка какой-нибудь хороший Enterprise…
— Все, что называется энтерпрайзом, по факту не работает на наших нагрузках. Да, оно хорошо себя показывает в банковской сфере, еще где-то, где нагрузки ниже, но более серьезные требования к надежности. Нас же во многих системах устраивает и eventual consistency: какая разница, например, секундой раньше или позже увидит твой друг, что ты сменил аватарку?
Про инженерные компромиссы, шардинг и хороший сборщик мусора
— Я вообще очень люблю тему про трейд-оффы. Давай про это поговорим? Какие они в Одноклассниках? Я так понимаю, что производительность поставлена во главу угла.
— Так или иначе, даже если ты фронтэнд-разработчик, тебе приходится думать о производительности.
Вот приходят два списка, нужно их как-то объединить в один.
Да, проще написать простейший квадратичный алгоритм. Но когда знаешь, что твой сервис потом наберет популярность, разрастется, списки будут приходить и по 10 тысяч, и по 100 тысяч элементов… начинаешь задумываться. Ведь квадратичный алгоритм будет тормозить.
— Но у других-то команд, наверное, другие приоритеты: производительность, например? И, опять же, то, что нет join'ов: если данные денормализованы, значит, куча места на диске тратится?
— Где как. Это, опять же, трейд-офф, как ты говоришь. Если мы рассчитываем, что займем ненамного больше места, если денормализуем и храним прямо в полях той же таблицы. Либо делаем join именно на стороне бизнес-логики. Достаем ID, и потом по ним лезем в другие подсистемы.
Я люблю приводить простой и понятный пример: как достать список всех твоих друзей — с именами, аватарками и прочим? Сначала выполняется запрос в отдельную подсистему графа связей, и по твоему ID достаются ID твоих друзей. Граф связей никакой информацией, кроме ID, больше не обладает. Получив массив ID, уже выполняем второй запрос на пользовательские кэши, которые как раз по этим ID достают информацию о пользователе.
Эти данные, конечно, шардятся. У нас в модуле remoting'а реализовано некое подобие MapReduce, то есть система сама умеет распределять запросы по шардам, выполнять их параллельно, а потом собирать. По ключам известно, на каком шарде они хранятся.
— В описанном тобой сценарии мы сделали два запроса минимум, это же все должно как-то очень быстро бегать внутри. Это же обращение во внутренню сеть, а сеть — это сразу много прыжков, потому что одна подсистема обращается к другой… В такой цепочке вызовов будет много сетевых запросов!
— Безусловно. Но, на самом деле, средний период ожидания Ajax-запроса составляет 5 миллисекунд. Это не считая канала, который идет от пользователя до наших фронтэндов. То есть, это время внутри портала. Понятно, что 5 миллисекунд — это среднее по больнице. Есть запросы и по 50 миллисекунд, есть и совсем короткие.
— То есть, какой персентиль мы считаем? Девяностый?
— Девяностый. Чтобы вы понимали, один удаленный запрос на сервер в том же дата-центре у нас за 300 микросекунд уходит, а на сервер в другом — за одну миллисекунду. Понятное дело, что случаются сборки мусора: там может быть и 100 и 200 миллисекунд. Но они редкие. Мы всячески боремся с длинными GC-паузами. Пауза в одну секунду для большей части нашей системы уже критична.
— Какой GC используется?
— По большей части, хорошо оттюненный CMS. Он, в целом, работает лучше, чем даже Garbage-First. Но G1 у нас тоже используется — например, в нашем NewSQL самописном, решении, которое пришло на замену SQL-серверу, чтобы гарантировать нужно время отклика.
То есть, в среднем G1 работает чуть хуже, чем затюненный CMS, но при этом гарантий дает больше. У CMS, допустим, по 50 миллисекунд в среднем задержка, но иногда там выстреливает до 300–400 миллисекунд. А G1 собирает, может, и чаще, и у него пауза в среднем до 150 миллисекунд, но ему задали ограничение в 200 миллисекунд, и он старается его действительно выдерживать.
— Примерно представляя, что такое Java, я понимаю, что если мы командуем: «Пожалуйста, G1, пауза 200 миллисекунд», — то в реальности там не будет 200 миллисекунд. Точнее, будет, но с какой-то вероятностью. Как часто сборки мусора вылезают из этих условных 200 миллисекунд?
— Очень редко. На удивление. Начиная с JDK 8u40, G1 стал хорошим коллектором. Если раньше он даже не мог неиспользуемые классы выгрузить без полной сборки, то с этой версии он уже вполне себе production quality.
— В JDK9 Garbage-First будет дефолтным коллектором. JDK9 выходит через год, в сентябре 2016-го. Будете переходить на Java 9?
— Скорее всего, будем, чтобы получать апдейты, когда поддержка «восьмерки» прекратится. Собственно так произошло и с Java 7: мы начали с нее переезжать, потому что хотим получать новые апдейты. Сам сталкивался с тем, что приходилось патчить нашу версию JDK7, чтобы забэкпортировать довольно критичные фиксы в JIT-компиляторе, которые только в JDK8 фиксились.
— А почему не взять какой-нибудь условный Azul, который сделал большую часть бизнеса на том, что они занимаются бэкпортами и саппортом старой Java’ы?
— Дать возможность за счет нас заработать Azul’у? :) А смысл? Сейчас у нас есть специалисты, которые с этим справляются.
О производительности Java
— Давно не было новостей о прорывах в производительности в JDK, JRE. А у тебя было такое, что после переключения на какую-нибудь версию, существенно увеличилась производительность?
— Такого, что просто с заменой версии — нет, не было.
— А с чем это связано? Каковы шансы, что завтра выйдет какая-то крутая оптимизация в JIT’e, которая +20% производительности даст?
— Такая вероятность близка к нулю. Хотя интересные оптимизации случаются — недавно вот векторизацию сделали в циклах.
— Сложение и умножение на целых числах? Казалось бы, простая вещь...
— Да, речь о целочисленных операциях с массивом в цикле. Даже сейчас это не во всех случаях работает. Но, по крайней мере, оптимизация такая уже есть. Я к тому, что сейчас все эти оптимизации дают проценты считанные, доли процентов. Ни о каких резких скачках речи не идет.
— Вопрос о безопасности. В какой-то момент, до 2012 года девизом Java было «Compatibility-First». С 2012 года это стало «Security-First». Вот вы, как портал, ощутили это как-то?
— В первую очередь, для нас проблемой являются баги, которые приводят к крашам JVM. В остальном, пока еще ни разу не было случаев, когда нас хакнули, зная какие-то уязвимости Java. Гораздо проще стало найти какую-то дырку в API.
Про Storages и Cassandra
— Отойдем от Java и поговорим про системы хранения. Как оно у вас устроено? Какие файловые системы используются?
— Много самых разных: и самописных, и стандартных. Сегодня, наверное, больше всего Cassandra-based хранилищ.
— Почему именно Cassandra?
— Другие просто не работают: фэйловера нет, возможность репликации чуть ли не вручную. А репликация — это важное бизнес-требование. Сейчас мы стремимся к тому, чтобы вся функциональность портала целиком работала даже при отказе целиком одного из дата-центров.
— И сколько всего дата-центров?
— Три. И если один вылетел, пользователи все равно смогут зайти, но часть сервисов может быть недоступна. Работу по обеспечению надежности мы начали с ключевых сервисов, а сейчас подчищаем хвосты.
— Расскажи про процесс перехода на распределенную систему с тремя дата-центрами, пожалуйста. Как это устроено, кто этим занимается?
— Сами библиотеки, хранилища и инструменты делает команда платформы. Например, для тяжелого контента — фото, видео, музыки — используются самописные хранилища. Недавно к нам приходили в офис в гости коллеги из EMC — они известны своими решениями в области хранения информации. Рассказывали про свои решения, делились опытом. Но, как оказалось, они не могут предложить нам ничего нового по сравнению с тем, что уже есть у нас.
— Да, EMC — интересные ребята. А в чем, кстати, на твой взгляд, техническая экспертиза Одноклассников? Есть ли опыт мирового уровня?
— Первое, что приходит на ум — Cassandra. Мы контрибьютим в нее, и постоянно находимся впереди по сравнению с тем, что есть в мастер-ветке Cassandra сейчас. Глобальные индексы, которые там только-только еще делаются, — у нас уже есть и вовсю используются.
Еще у нас сильная экспертиза в системах рекомендаций. Изначально у нас появилась система рекомендаций для музыки, теперь она уже ко многим другим сервисам прикручена: видео, группы. Главная задача портала – показывать людям тот контент, которым им интересен, и не показывать тот, который им не интересен. Это стимулирует пользователя дольше зависать в Одноклассниках.
Ну и у нас сильная экспертиза по JVM, конечно.
— А где хочется экспертизы, чего сегодня не хватает, на твой взгляд?
— Может, в плане распознавания изображений. Здесь есть, чему у Goolge поучиться.
— Насколько я понял, есть некая команда платформы, которая делает все, что связано с хранением, распределением, с кэшами. И вы представляете свою работу другим командам в виде библиотек, API, сервисов, так?
— Именно. Мы даем уже готовые решения. Но разработчик из другой команды, безусловно, должен знать какие-то особенности: понимать, какие запросы легкие, какие тяжелые, что можно делать, что нельзя.
— Вы это ему описываете в виде javadoc или как-то иначе передаете эти знания?
— Есть такая практика, когда авторы этих решений проводят лекции, семинары. Хотя и не очень часто: преимущественно для новичков, но и опытные разработчики, как правило, могут что-то новое узнать.
И у нас ведь есть еще процедура ревью кода: если человек взял мое решение и на базе него что-то построил, то он, скорее всего, придет ко мне потом и спросит: «Андрей, тут все нормально?» Если что, я укажу, где и что поправить.
— А что ты делаешь, для того, чтобы твой API нельзя было использовать неправильно?
— Лично я в этом плане поступаю не очень хорошо: просто стараюсь сделать код максимально лаконичным, простым. А если используешь его неправильно — ты «сам себе злобный буратино» — иди и посмотри документацию или запись лекции, чтобы понять, как что делать.
— Но документация, в принципе, в актуальном состоянии?
— Да, я очень рад, что в свое время написал подробный FAQ по нашему ремоутингу и сериализации.
У нас просто используется своя система: в свое время перешли на нее с JBoss Remoting, чтобы иметь возможность делать онлайн-апдейты без даунтайма сервисов, и разруливать ситуации, когда в одной части портала работает версия с новыми версиями классов, а с другой – со старыми.
И до сих пор, когда ко мне приходят и спрашивают: «А как будет сериализоваться так?», «А можно ли такое преобразование делать» – я просто отправляю на страницу в нашей внутренней Wiki.
— А JBoss Remoting этого не умеет? Или справляется хуже?
— У него еще хуже, чем даже в стандартной сериализации. Она какие-то изменения поддерживает, но в ограниченном объеме: там можно удалять поля, можно добавлять новые, но они будут инициализироваться дефолтным значением.
А вот, допустим, изменить тип с int на long. Было поле флагов, стало не хватать 32 – изменили на 64-битное поле. Это типичный кейс, однако, стандартная сериализация его не поддерживает.
Как стать крутым системным программистом?
— Что посоветуешь почитать, куда посмотреть человеку, который интересуется именно самой платформой Java и ее низкоуровневыми деталями?
— Про JVM и внутренности HotSpot не так много написано в каких-то сторонних источниках. Благо, что это OpenSource-продукт: можно скачать, посмотреть. Порой в коде HotSpot комментариев больше, чем самого кода.
— Это C++ в основном, да?
— Да. Ну и есть общие концепции того, как виртуальные машины JVM работают. Тут я советую посмотреть отличный цикл лекций Олега Плисса на питерском JUG.
— Может быть, посоветуешь еще книжки какие-то?
— Книжек не посоветую. Лучше на StackOverflow задавать вопросы. Я сейчас тоже там сижу, и меня можно спрашивать.
Я там подписался на вопросы, связанные с JVM и Java Performance, и периодически отвечаю на нетривиальные вопросы. А иногда и сам узнаю оттуда что-то новое.
Там, конечно, полно и неадекватных вопросов и ответов, но на это есть разные механизмы, репутация та же. Зато там можно встретить ведущих мировых экспертов: периодически сам Brian Goetz заглядывает туда и что-то комментирует.
— А Хабр? Сейчас, я смотрю, ты туда редко уже пишешь.
— Мне кажется, потихоньку падает качество контента на Хабре… И я не только про Java-хаб.
Во-первых, сказывается то, что Хабр разделили на несколько подпроектов. Cоответственно, аудитория каждого уменьшилась. Во-вторых, когда какой-нибудь неадекват всего лишь за один комментарий тебя может заминусовать — конечно, пропадает мотивация писать дальше.
— Твоя последняя статья — про то, как при параллельной работе класслоадеров возникает дедлок. Cчитаешь, что автор этого кода из Гугла должны были это фиксить, или это «баг и баг, у всех бывают баги»?
— Они его пофиксили, и надо отдать должное, довольно оперативно. Просто он принадлежит к такому классу багов, которые совсем неочевидны. Его просто так не найдешь, глядя на код.
— Неужто это статический анализатор не может отследить? Казалось бы, простое правило, что вот этот класс – это суперкласс, а вот этот подкласс, довольно легко делается с помощью статического анализа. В принципе, это правило, которое может поймать кто угодно.
— Согласен. Просто не было такого правила до сих пор. Наш общий знакомый lany уже пообещал добавить в FindBugs соответствующие warning-и.
— Но творческие планы — доклады, статьи, книги -— у тебя все же есть?
— Есть много идей, о многом хочется написать, но не хватает времени. Есть масса интересностей про внутренности JIT-компилятора, как он себя совершенно непредсказуемым образом ведет, и за какие ниточки там можно подергать.
Также хочу написать, как работать в Linux с сигналами. Традиционно сигнал перехватывают только в случае каких-то ошибок, но вот HotSpot JVM использует для своих внутренних целей очень много интересных моментов, связанных с сигналами. Это вполне себе хороший способ для системного программного обеспечения. Такого, как Java Runtime, например.
— В заключении, как всегда, полезные ссылки:
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий