[Перевод] Теперь поиск Twitter ещё в 3 раза быстрее

Мне всегда был интересен Ruby-on-Rails (RoR) и Twitter как яркий представитель платформы на этом framework. 6 апреля этого года в блоге команды Twitter появилась запись о полной смене поисковой платформы с RoR на Java. Под катом перевод о том, как это было.



«Весной 2010, команда поиска Twitter приступила к переписыванию нашего поискового движка с целью обслуживания постоянно растущего траффика, уменьшения задержки для конечного пользователя и увеличения доступности нашего сервиса, получения возможности быстрой разработки новых поисковых возможностей. Как часть работы, мы запустили новый поисковый движок реального времени, сменив наш back-end с MySQL на версию Lucene. На прошлой неделе, мы запустили замену для нашего Ruby-on-Rails front-end: сервер Java, который мы называем Blender. Мы рады анонсировать, что это изменение уменьшило время поиска в 3 раза и даст нам возможность последовательно наращивать поисковый функционал в следующие месяцы.

Прирост производительности


Поиск Twitter один из самых нагруженных поисковых движков в мире, обслуживающий более одного миллиарда поисковых запросов в день. На этой неделе до того, как мы развернули Blender, #tsunami в Японии способствовало значительному увеличению поисковых запросов и связанных с ними пиками задержек в поиске. После запуска Blender, наша 95-ти процентная задержка уменьшилась в 3 раза, с 800мс до 250мс и загрузка CPU на наших front-end серверах сократилась вдвое. Сейчас мы имеем потенциал для того, чтобы обслужить в 10 раз больше запросов на одну машину. Это означает, что мы можем поддерживать прежнее число запросов с меньшим количеством серверов, уменьшая на порядок расходы на наш front-end сервис.
image
95-ти % задержка поискового API до и после запуска Blender.

Улучшенная поисковая архитектура Twitter


Для лучшего понимания прироста производительности, вам необходимо сначала понять недостатки наших бывших Ruby-on-Rails front-end серверов. Они запускали фиксированное число однопоточных рабочих процессов, каждый из которых делал следующее:
  • Разбирал поисковый запрос;
  • Синхронно запрашивал поисковые сервера;
  • Агрегировал и формировал результаты.

Нам давно было известно, что модель синхронной обработки запросов использует наши CPUs неэффективно. За долгое время, мы также накопили значительный технический долг в нашем основном коде на Ruby, делая его сложным для добавления функциональности и улучшения надёжности нашего поискового движка. Blender решает эти вопросы путем:
  1. Создания полностью асинхронного сервиса агрегирования. Без потоков ожидающих сетевого I/O для завершения;
  2. Агрегируя результаты от back-end сервисов, таких как, сервис реального времени, сервис топового твита (tweet) и сервис географических индексов;
  3. Аккуратнее разрешая зависимости между сервисами. Рабочие процессы автоматически обрабатывают транзитивные зависимости между back-end сервисами.

Следующая диаграмма показывает архитектуру поискового движка Twitter. Запросы с веб-сайта, API или внешние клиенты в Twitter передаются Blender через аппаратный балансировщик нагрузки. Blender разбирает поисковый запрос и передаёт его back-end сервисам, используя рабочие процессы для обработки зависимостей между сервисами. Наконец, результаты от сервисов объединяются и формируются на соответствующем языке клиента.
image
Поисковая архитектура Twitter с Blender.

Обзор Blender


Blender – это Thrift и HTTP сервис построенный на Netty, широко масштабируемой NIO клиент-серверной библиотекой написанной на Java, что позволяет разрабатывать для различных серверов и клиентов быстро и легко. Мы выбрали Netty из нескольких его конкурентов, таких как Mina и Jetty, потому что она имеет более прозрачный API, более хорошую документацию и, самое важное, потому что несколько других проектов Twitter используют этот framework. Для того чтобы Netty работала с Thrift, мы написали простой Thrift кодек, который декодирует входящие запросы Thrift из канального буфера Netty, когда он считывается из сокета и кодирует исходящие ответы Thrift, когда он записывается в сокет. Netty вводит ключевую абстракцию, названную Канал (Channel), для инкапсуляции соединения к сетевому сокету, который предоставляет интерфейс для выполнения множества I/O операций, таких как чтение, запись, соединение и привязка. Все канальные I/O операций асинхронны по своей природе. Это означает, что любой I/O вызов немедленно возвращается с экземпляром ChannelFuture, который сообщает, когда запрошенные операции I/O успешны, неудачны или отменены. Когда сервер Netty принимает новое соединение, он создаёт новый канальный конвейер для обработки соединения. Канальный конвейер – это последовательность канальных обработчиков, которые реализуют бизнес логику необходимую для обработки запроса. В следующей секции мы покажем, как Blender переносит эти конвейеры для запроса, обрабатывающих рабочие процессы.

Библиотека рабочих процессов



В Blender, рабочий процесс – это множество back-end сервисов с зависимостями между ними, которые должны быть обработаны для обслуживания входящего запроса. Blender автоматически определяет зависимости между ними, для примера, если сервис A зависит от сервиса B, A запрашивается первым и результаты его работы передаются B. Это удобно для представления рабочих процессов в виде направленных ацикличных графов (см. рисунок ниже).
image
Пример рабочего процесса Blender с 6 back-end сервисами.

В примере рабочего процесса, мы имеем 6 сервисов {s1, s2, s3, s4, s5, s6} с зависимостями между ними. Прямая грань от s3 в s1 означает, что s3 должен быть вызван до вызова s1, потому что s1 нужны результаты s3. Для такого рабочего проекта, библиотека Blender выполняет топологическую сортировку на DAG для определения конечного порядка сервисов, который также является порядком их вызова. Порядок выполнения для рабочего процесса выше будет {(s3, s4), (s1, s5, s6), (s2)}. Это означает, что s3 и s4 могут быть вызваны параллельно на первом шаге; когда они вернут результаты, на следующем шаге также параллельно вызываются s1, s5, и s6; до завершающего вызова s2. После того как Blender определил порядок выполнения, он отображается на Netty конвейер. Этот конвейер – последовательность обработчиков, которым нужно передать запрос для обработки.

Мультиплексирование входящих запросов


Так как рабочие процессы отображаются на конвейеры Netty в Blender, нам было нужно направить входящие клиентские запросы на соответствующий конвейер. Для этого, мы построили прокси слой, который мультиплексирует и направляет клиентские запросы по следующим правилам:
  • Когда удалённый клиент Thrift открывает постоянное подключение к Blender, прокси слой создаёт карту для локальных клиентов, одну для каждого из локальных серверов, обслуживающих рабочие процессы. Заметьте, что все эти сервера запускаются в процессе Blender среды JVM и создаются, когда процесс Blender стартует;
  • Когда запрос поступает на сокет, прокси слой считывает его, определяет, какой рабочий процесс был запрошен и направляет его соответствующему серверу рабочих процессов;
  • Аналогично, когда ответ поступает от локального сервера, обслуживающего рабочие процесс, прокси считывает его и записывает ответ для удалённого клиента.

Мы использовали модель Netty управляемую событиями для выполнения всех перечисленных задач асинхронно, так что больше нет потоков ожидающих I/O.

Диспетчеризация back-end запросов


Как только поисковый запрос поступает на конвейер рабочего процесса, он проходит через последовательность сервисных обработчиков в последовательности определённой рабочим процессом. Каждый сервисный обработчик создаёт соответствующий back-end запрос для этого поискового запроса и передаёт его на удалённый сервер. Например, сервисный обработчик реального времени создаёт запрос реального времени и отправляет его асинхронно на один или несколько индексов реального времени. Мы используем twitter commons библиотеку (недавно ставшей open source!) для предоставления управления пулом соединений, балансировки нагрузки и определения мёртвых хостов. Поток I/O, который обрабатывает поисковый запрос, освобождается, когда все back-end ответы обработаны. Поток таймера проверяет каждые несколько миллисекунд для того, чтобы увидеть, если любой из back-end ответов вернулся с удалённых серверов и устанавливает флаг, указывающий если запрос успешен, превысил время ожидания или неудачен. Мы поддерживаем один объект на протяжении жизненного цикла поискового запроса для управления этим типом данных. Успешные ответы агрегируются и посылаются на следующий шаг для обработки сервисными обработчиками в конвейере рабочего процесса. Когда все ответы с первого шага прибывают, делается второй шаг асинхронных запросов. Этот процесс повторяется пока мы не завершим рабочий процесс или время ожидания не превысит допустимое. Как вы можете видеть, на протяжении выполнения рабочего процесса, ни один поток не простаивает в ожидании I/O. Это позволяет нам эффективно использовать CPU на наших машинах с Blender и обрабатывать большое число конкурентных запросов. Мы также экономим на задержках, так как выполняем большинство запросов для back-end сервисов параллельно.

Развёртывание Blender и будущая работа


Для того чтобы убедиться в высоком качестве сервиса пока мы интегрируем Blender в нашу систему, мы используем старые Ruby-on-Rails front-end сервера как прокси для перенаправления thrift запросов для нашего Blender кластера. Использование старых front-end серверов в качестве прокси позволяет нам обеспечивать целостность пользовательского восприятия, делая значительные изменения в базовой технологии. В следующей фазе нашего развёртывания, мы полностью уберём Ruby-on-Rails из нашего поискового стека, соединив пользователей напрямую с Blender и потенциально уменьшив задержки еще больше.»
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 36

    –7
    это фсё мегакруто!
    а заработает то когда?
      0
      Уже работает.
        +2
        не верю.
        search.twitter.com/search?q=например+from%3Aqmaxa
        No results
        0.21 seconds

        blogs.yandex.ru/search.xml?text=например&server=twitter.com&author=qmaxa
        18 найденных.
        3 секунды, со второго раза

        КАК оно работает?
        чтоли только по хэштэгам ищет?
      0
      Позволит держать меньше серверов…

      И всё так же получать очень известное морское животное на каждый пятый запрос.
        +3
        Что ж получается, RoR не катится для high load?
          +2
          Ну для поисковых движков точно не катит. Собственно логику приложения на ROR писать быстро и удобно, а вот всякие специализированные вещи лучше выносить на отдельные движки. В сущности они переключились с тредов на event-фреймворк, что соответственно дало такой профит. Непонятно почему в статье не упоминается EventMachine.
            +1
            Действительно.
            В статье написано чем не удовлетворял старый движок, но переписывать взялись на Java. Почему он не был переписан на ROR? Просим экспертное мнение.
              +1
              Что по вашему хайлоад? Проекты от 37signals, shopify, и т.д., кажутся вам мелкими?

              Высоконагруженные проекты можно писать на чем угодно, но есть некоторые критичные моменты, когда разрыв в стоимости использования технологий велик, например в твиттере RoR не очень прижились, и возможно, это было изначально известно и разработчики использовали RoR скорее для прототипирования, чтобы в дальнейшем все-таки переписать. RoR мог бы быть использован для разработки еще более нагруженых проектов, где, например, реально организовать кэширование и т.д., а в твиттере слишком много действий и алгоритмов, например для поиска людей, которых бы вам было полезно зафоловить и т.д., что в принципе ресурсоемкая задача и может с одинаковым успехом быть реализована на различных ЯП.
                +2
                Да уже ответили. Всё верно, важен баланс между скоростью ввода проекта в эксплуатацию и учетом высоких нагрузок.
              0
              Что-то все равно он частенько притормаживает. И твиты в поиске старые не выдает, а иногда очень нужно.
                +2
                Предсказываю кучу комментариев типа RoR — отстой, а java — круто! Но помните, скорость написания проекта очень важна, особенно для такой, достаточно революционной идеи как твиттер. Проект мог не выстрелить и если бы его изначально на яве писали, то этот «невыстрел» обошелся бы слишком дорого, поэтому и был выбран RoR, а не что-то еще. И еще, Rails — это достаточно высокопроизводительная платформа, просто в случае с Twitter получилось выгоднее переписать код, чем купить еще несколько серверов. Есть инвестиции, есть программисты, теперь, когда людей много — можно писать на чем угодно.
                  –4
                  Рору теперь нужно извлечь из этого урок.
                  Например, в 4-1 мажор версии реализовать эту асинхронность и все будет путем.

                  Сейчас польется комментарии аля: — вот, рор кал, буду делать что-то свое на джаве.
                  Ну делайте, флаг Вам в руки — я, конечно понимаю, что плох тот солдат, который не хочет стать генералом, но, блин Вашим мега-гениальные стартапам до уровня Твиттера — как «рачкы до Киева».
                  Выстрелит — отлично.
                  Не выстрелит? — не беда. Просто с рор«ом время на реализацию было бы в десятки раз меньше и не так обиднее в итоге.
                  • НЛО прилетело и опубликовало эту надпись здесь
                      –1
                      Почему в десятки? Может быть сразу в сотни?
                        0
                        Что Вы так напряглись? Пока ещё никто слюнями не брызгает. В статье же русским по белому написано, что сменилась архитектура на асинхронную модель, это и дало прирост производительности. Про причины смены ЯП ни слова не сказано. Возможно просто свободные java-программисты были под рукой в этот момент.
                          +5
                          Я люблю RoR, но давайте будем реалистами, Java больее производителен, хотя, это еще и от прямоты рук зависит.
                            –2
                            пруфлинк или не было
                              0
                              Какой именно пруфлинк вам надо?
                                0
                                Можно сравнить производительноcть JIT (в частности, какие инструкции он под какими процессорами поддерживает, эвристики при оптимизации и пр), GC, аллокатора памяти, модели блокировок внутри VM и вообще реализацию многопоточности, много чего. Только вопрос — в чем мы именно меряем производительность, какая метрика?
                                  +1
                                  я бы хотел сравнение производительности аналогичных решений на RoR и на каком-нибудь сравнимом фреймворке на Java.
                                  То, что руби медленнее джавы линейно, это понятно, только архитектура в производительности высоконагруженных проектов не менее важна. Меня интересует адекватное сравнение в продакшене, запросы в секунду, траты памяти, процессора при аналогичных задачах. Я ещё ни разу не видел такой информации, и предложения из разряда «RoR vs Java», «RoR vs PHP» (причём обычно даже не Ruby vs Java/PHP) меня изрядно веселят.
                                    0
                                    Хорошо мыслите, здраво. Жаль, что у меня инфы такой нет, так как кроме твиттера не припоминаю сходу чтобы кто-то из хайлоада (топа алексы хотя бы) переходил с руби на яву и публиковал такую инфу, кроме вот твиттера.

                                    Могу только несколько мыслей высказать.

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

                                    2) В продакшене, реальном, все зависит от типа задач. Например, в проектах где чистый энтерпрайз, где мало юзеров, но сложная и быстрая аналитика на больших объемах данных, в итоге часто упирается в базу (у нас Оракл) — а конкретно, в ввод-вывод на сториджах кластера бд. И тогда уж в общем-то все равно, на чем серверсайд написан — Java, JRuby, Jython, Groovy, Scala. Плюс, масштабировать сервера с Java / JBoss-ом намного проще и, главное, несравнимо дешевле, чем расширять кластер Oracle.

                                    По сути, если оставить многопоточность / грид контроллер / низкоуровневые части и вычисления для Java, то все остальное («обычную» бизнес логику) можно было бы, я reasonably confident, переписать на Groovy / JRuvy с минимальной потерей производительности.

                                    3) Если мы возьмем тот же Ruby в виде JRuby, то где и почему он медленнее (объективно), чем Java? Динамическая типизация (особенно когда VM не поддерживает ее на уровне инструкций), MOP, роутинг вызовов через метаклассы, все эти invokeMethod(bla-bla), привязки вызовов методов через call sites, тот факт, что абсолютно все — объект (что мешает ряду оптимизаций). В нативном руби я думаю, вообще говоря, проблем больше, чем в JRuby, ибо у них своя вирт машина, которая, не в обиду будет сказано, менее зрелая и хитрая, чем JVM, особенно в части работы с потоками и JIT-компиляции.
                                      0
                                      1. сравнивать проекты с разной архитектурой и на разных языках можно и нужно. я вам напомню пример одноклассников.ру, которые когда сюда пришли, опубликовали инфу о том, что у них в среднем 80 запросов/сек на сервер. у фейсбука и вконтакта этот показатель 600 и 500 соответственно.

                                      2. я могу заблуждаться, но enterprise-аналитику лучше всего всё-таки писать на NoSQL+Map/Reduce. Когда ставятся приоритеты не на целостность и производительность БД в реальном времени при огромном кол-ве запросов в секунду, а на расчёты большого кол-ва информации, это решение оказывается самым лёгким и масштабируемым. Уважаю Oracle, но он хорош в биллингах и веб-сервисах. Просто по нему специалистов в России (да и везде) найти проще, чем по NoSQL.

                                      3. по поводу JRuby я вам скажу, что это поделка та ещё, хуже только может быть IronRuby:
                                      1) довольно скудная поддержка спеков ruby 1.9.2
                                      2) производительность ниже даже по сравнению с MRI в ветке 1.9.2 (или по сравнению с ree, точно не помню) пруф: shootout.alioth.debian.org/u32/benchmark.php?test=all&lang=yarv&lang2=jruby
                                      в принципе очень часто слышал мнения, что JRuby нужен только тем, кому ruby нужно пользоваться в контексте библиотек/окружения Java.
                                      и у MRI довольно зрелая VM, я вам скажу. языку 15 лет в обед, и последние версии уже совсем сравнимы по скорости PHP. пруф: shootout.alioth.debian.org/u32/benchmark.php?test=all&lang=yarv&lang2=php.
                                        0
                                        1) Сравнивать можно — только какие вы выводы можете сделать? Вон у одноклассников, которые на яве, 80 запросов в секунду, а у контакта, которые на пхп, их 500 (а у википедии так вообще 1000-2500, если верить insight-it и highscalability.com). Но это же не значит, что при разработке нагруженных сайтов пхп держит в в 6-7 раз большую нагрузку чем Java. Нужен какой-то детерминированный подход к оценке и метрике.

                                        2) Приоритеты все же ставятся на целостность и корректность, и крайне важна производительность на сложных запросах (не поиск по ключу, а пересчет агрегированных метрик, потом вычисления по ним результата, и запись его обратно в корректным aggregation level progagation. Возможно, слово аналитика не совсем корректно, так как у многих ассоциируется с каким-то фоновым процессом, который собирает данные для отчетности. Я имею в виду нечто другое. Давайте возьмем систему управления поставками для крупной сети торговых площадок, как пример.

                                        Это не ultra-low latency система, как на бирже, скажем, но важно общее время процессинга. Условно, на пальцах, — если вы обновляете статистику по чартам на last.fm, вы можете ее обновлять час, а можете два, а иногда она может обновляться и 3 часа, и это не очень критично. Но если вам в 6 утра приходит склеенные вместе сырые данные с кучи торговых площадок (их несколько сотен), и там данные по десяткам и сотням тысяч позиций в каталогах, а в 7-30 утра вы должны сгеренировать / пересчитать и разместить заказы, то ситуация немного другая.

                                        У вас есть фиксированный период, не более полутора часов, очевидно, за который надо проанализировать все данные (не только вновь пришедшие), чтобы 1) обновить глобальную статистику по продажам 2) выявить «мгновенные тренды» в определенных регионах, или по определенной категории позиций. Потом с учетом эти данных надо разместить обновленные заказы.

                                        При этом бывают некоторые накладки, например — последние данные приходят не в 6 утра, а в 6-20, что, естественно, не отменяет необходимости для бизнеса закончить процессинг к 7-30.

                                        Тут получается баланс между тем, что внутри оракла можно делать аналитику встроенными средствами и быстро, очень быстро, но и очень дорого. На java-based серверах это тоже можно делать, получается медленнее, но лучше масштабируется и гораздо, гораздо дешевле стоит.

                                        Как-то так.

                                        Теперь по консистентности. Опять же, ввиду того, что я описал, консистентность важна. Потеря консистенции в общем случае, вносит некоторые помехи в выполняемый процесс data mining, описанный выше, т.е. это живые доллары (сотни, тысячи, десятки тысяч) убытков.

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

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

                                        Другой аспект — это надежность и поддержка. Для меня, наткнуться на баг (глюк, странное поведение) в, скажем, FlockDB, Mongo, кассандре, при всем уважении к их авторам, все же выше, чем вероятность наткнуться на явный глюк оракла. И, опять же, по глюкам оракла советы и воркэраунды найти будет проще, если что.

                                        Потом, в случае проблем, у оракла (db2, mssql) есть саппорт, в чем профессионализм крупный бизнес верит больше, чем в саппорт NoSQL баз :)

                                        Кстати, про сами носкл базы — они именно что для наших нужд не слишком подходят. У нас конкретно, реляционная структура (ну хорошо, по большей части :)), а не граф, грубо свернутый клубком и уложенный по полусотне таблиц.
                                          0
                                          Насчет JRuby.

                                          Ну во первых, они достигли существенного прироста скорости, когда переписали рантайм в последней версии (может, она еще не выпущена) так, чтобы использовать инструкцию InvokeDynamic в JVM 7.

                                          Во вторых, с пхп по скорости я бы не стал сравниваться, а с JRuby и ява-бейсд языками интереснее сравниться по работе виртуальных машин. Например, JRuby, использует JVM-ные треда и блокировки, JIT и GC.
                                  +2
                                  А С++ ещё более производителен. Однако я наблюдал случаи когда сервис на плюсах работал медленне PHP-шного аналога. Кривизна рук оказывает гораздо более существенное влияние на результат нежели платформа.
                                    0
                                    свовсем недавно читал статью о ЯП и алгоритмах, там автор привел реализации программы на Ruby, которая работала в несколько раз быстрее версии на erlang'е. Уверен, что на erlang'е можно было переписать ееще еще быстрее, но суть не в этом, а в том, что очень часто алгоритм определяет производительность первым, а уже сам язык стоит на втором месте.
                                0
                                Для тех кто в танке — пишу на Роре. Тащусь от руби.
                                Расстроен тем, что теперь нельзя будет швырнуть в лицо ответ критиканам Рора фразу — твиттер на Рор«е — застрелитесь.

                                  +4
                                  Любите письками меряться?

                                  Если вам скажут, что facebook на PHP, то вы предадите свою религию RoR?

                                  На самом деле такие крупные проекты разрабатываются с использованием целых зоопарков языков, тот же Twitter жил не RoR единым, но и Scala и, уверен, было место для использования, например Си и т.д.
                                    0
                                    При чем здесь религия?
                                    Устаешь от постоянных однотипных вопросов -а зачем? есть же пхп/джава…
                                      +6
                                      А вы не отвечайте на такие запросы, а то, вдруг конкуренция возрастет и будете вы как Rails-разработчик получать зарплату, как сейчас у PHP разработчиков.
                              +1
                              «большое число конкурентных запросов»: concurrent requests переводится, как «параллельные запросы», и никак иначе :)
                                0
                                очевидно, проблема производительности не так серьезна. через 5 лет сервера станут еще мощнее но я сомневаюсь что людей станет в 5 раз больше следовательно рельсы уже не будут так трещать.
                                + в 4ой ветке будет все просто… brilliant
                                  0
                                  А твиттер точно именно на RoR? Как-то по их статьям складывается субъективное впечатление, что он уже давно на просто руби.
                                    –1
                                    Я бы не стал кидаться оценивать пригодность/непригодность той или иной технологии на основании опыта твиттера, потому как они никогда не отличались вдумчивостью в выборе технологий.
                                    Создается впечатление, что они скорее пробуют без предварительного продумывания.

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

                                    Любому, кто хоть раз переносил код с рубийных Thread на EventMachine известна неидеальность реализации тредов в руби и эффект от переноса на EventMachine.

                                    В итоге в твиттере мы видим не интересную и вдумчивую проработку вида «мы перенесли код на evented, было лучше, но на джаве лучше», а достаточно непонятный мне двойной скачок: на другую архитектуру и на другой, заведомо более неудобный язык.
                                      0
                                      Насчет того что Java менее удобен как язык — пожалуй, соглашусь.

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

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое