В последнее время я также получил много вопросов от студентов, изучающих бэкенд: почему скорость веб-фреймворка Go не такая высокая, как у Java? Почему многие оригинальные Java-проекты пытаются переписать open source на ходу? Упадет ли Java из-за появления контейнеров? Действительно ли Java, вечнозеленое дерево на протяжении более 20 лет, действительно пойдет под откос? Orange пригласил студентов технологического факультета факультета Сямэнь ответить на вышеуказанные вопросы и приветствовать всех желающих пообщаться друг с другом.
В: Почему веб-фреймворк Go не так быстр, как Java?
**Фэн И: **Хуашань на мече, давайте просто проведем анализ производительности каждого фреймворка, а потом поговорим.
Различные сценарии применения различных фреймворков приводят к разным фокусам оптимизации.Ниже мы проведем подробный анализ.
обзор http сервера
Сначала опишем процесс обработки запросов простого веб-сервера:
После того, как сетевой уровень читает пакет данных, HTTP-декодер анализирует протокол, а затем маршрут находит соответствующий обратный вызов обработчика.После обработки бизнес-логики устанавливается код состояния соответствующего ответа, а затем соответствующий ответ кодируется кодировщик HTTP, и, наконец, данные записываются Net.
Уровень под Net контролируется ядром.Хотя существует множество стратегий оптимизации, здесь в основном сравнивается сам веб-фреймворк, поэтому оптимизация под Net пока не будет рассматриваться.
Глядя на исходный код платформы измерения давления, предоставленной techempower, можно увидеть, что все виды платформ в основном основаны на обработке epoll, поэтому разрыв в производительности различных платформ в основном отражается на производительности вышеуказанных модулей.
Краткое описание различных видов опрессовки
Давайте посмотрим на рейтинг производительности techempower, включая сериализацию JSON, одиночный запрос, несколько запросов, кэшированные запросы, Fortunes, обновления данных и открытый текст.
Где сериализация JSON кодирует фиксированную структуру Json и возвращает (сообщение: слово привет), Одиночный запрос — это один запрос к БД, Несколько запросов — это несколько запросов к БД, Кэшированные запросы — это получение нескольких значений объекта из базы данных в памяти и используйте json Return, Fortunes — для возврата после рендеринга страницы, обновления данных — для записи в БД, Plaintext — самый простой способ вернуть фиксированную строку.
Кодирование json, операция БД, рендеринг страницы и возврат фиксированной строки здесь являются соответствующей бизнес-логикой. Когда бизнес-логика более тяжелая (более трудоемкая), соответствующая бизнес-логика постепенно становится узким местом. Например, операция БД на самом деле основным является тестирование производительности соответствующей библиотеки БД и логики обработки самой БД, а базовое потребление функций самого фреймворка будет становиться все более и более незначительным с тяжелой бизнес-логикой (QPS под Plaintext под физической машиной в раунде 19 находится на уровне 7 миллионов, обновления данных на уровне 10 000, с разницей более чем в 100 раз), поэтому основной анализ здесь — сериализация Json и Plaintext, которые могут относительно отражать рейтинг http производительность самого фреймворка.
В раунде 19 сериализации Json самая высокая производительность у Java-фреймворка — firenio-http-lite (QPS: 1 587 639), а у Go — fasthttp-easyjson-prefork (QPS: 1 336 333). высокая производительность.
Из pprof fasthttp-easyjson-prefork, помимо чтения и записи, на долю json (эквивалент бизнес-логики) приходится 4,5%, а на сам fasthttp (декодер HTTP, кодировщик HTTP, маршрутизатор) приходится 15%. кажется, что у сериализации есть ощущение, что Java более эффективна, чем Go.
Далее продолжаем упрощать бизнес-логику и смотрим на ранжирование Plaintext.Режим Plaintext фактически тестируется в режиме конвейера HTTP.В раунде 19 у Java и Go почти одинаковые QPS.В тесте после раунда 19 gnet занимает второе место во всех языках, но разница в QPS первых нескольких фреймворков на самом деле очень мала.
В настоящее время основным узким местом на самом деле является сетевой уровень, и официальная сетевая библиотека go содержит логику, связанную с обработкой горутин.Прямая работа с epoll, такой как goget, уменьшит это потребление, и Java nio также напрямую работает с epoll.
Я взял тестовый исходный код gnet и запустил пресс-тест, и увидел, что pprof выглядит следующим образом: на самом деле в gnet есть еще место для оптимизации производительности: time.Time.AppendFormat занимает 30% ЦП.
Можно использовать следующий расширенный Формат, который позволяет сильно сократить потребление этой части при снижении точности получения текущего времени.
var timetick atomic.Value
func NowTimeFormat() []byte {
return timetick.Load().([]byte)
}
func tickloop() {
timetick.Store(nowFormat())
for range time.Tick(time.Second) {
timetick.Store(nowFormat())
}
}
func nowFormat() []byte {
return []byte(time.Now().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
}
func init() {
timetick.Store(nowFormat())
go tickloop()
}
После этой оптимизации следующим узким местом является выделение памяти во время выполнения, потому что следующие части кода стресс-теста не используют память повторно:
На самом деле потребление самого gnet было очень небольшим, а ulib в C++ также является очень простой операцией HTTP-кодека для тестирования давлением.
анализировать
Для тестируемой здесь структуры влияющие факторы в основном следующие:
1. Простой http, напрямую основанный на epoll: нет полного декодера http и маршрута (например, gnet, прямое простое сращивание байтов ulib, обратный вызов обработчика фиксированного маршрута)
2. Нулевое копирование и мультиплексирование памяти: внутри обрабатывается 0 копий байт (официальная http-библиотека go не использует нулевое копирование, чтобы уменьшить вероятность ошибок для разработчиков, иначе разработчики могут ненароком обратиться к файлам, которые были помещены обратно в пул баффов, данные создают проблемы параллелизма, о которых вы не подозреваете, и т. д.), а также повторное использование памяти, что в большей или меньшей степени реализовано в большинстве фреймворков.
3. prefork: обратите внимание, что существует способ использования prefork-процессов в фреймворке go (например, fasthttp-prefork), который заключается в разветвлении нескольких подпроцессов, совместно использующих один и тот же listen fd, и каждый процесс использует одно ядро, но одновременно ( 1 P ) логика обработки может избежать конкуренции блокировок внутри среды выполнения go и потребления планирования горутин (но все же будет некоторое потребление «бесполезного» кода, связанного с параллелизмом и планированием горутин в среде выполнения go)
4. Разница в производительности самого языка
По первому пункту, на самом деле после упрощения различных кодеков и роутинга производительность хоть и улучшается, но часто снижает удобство использования фреймворка.Для общего бизнеса такой высокий QPS не появится.Часто надо учитывать простота использования и масштабируемость, и в то же время также необходимо учитывать сложность интеграции фреймворка, используемого оригинальным промежуточным ПО или SDK компании.
По второму пункту, если это сетевой агент, без развития бизнес-стороны часто можно использовать настоящую полную нулевую копию, но если она предоставляется в качестве основы для развития бизнеса, необходимо учитывать определенную вероятность бизнес-ошибок, часто жертвование частью производительности является рентабельным.
Третий пункт, prefork, java netty и т. д., предназначены непосредственно для операций с потоками, которые могут быть более настроены для оптимизации производительности, а go-программа нуждается в общей сопрограмме, цель которой состоит в том, чтобы уменьшить сложность написания параллельных программ на этом уровне. соотношение производительности неизбежно.Не очень хорошая оптимизированная структура на основе потоков Java, но если вы напрямую манипулируете потоками, вам нужно разумно контролировать количество потоков, что является головной болью для настройки (особенно для новичков), и горутины можно игнорировать Размер пула делает код более элегантным и лаконичным, что на самом деле является улучшением для обеспечения качества разработки. Кроме того, здесь есть prefork, потому что go не может напрямую манипулировать потоками, а fasthttp предоставляет возможность prefork, используя многопроцессный метод для оценки многопоточности Java для дальнейшего повышения производительности.
Четвертый момент заключается в том, что Java является более зрелым с точки зрения самого языка, включая возможности Jit JVM, что делает разницу между горячим кодом и языком, скомпилированным Go, не слишком большим, не говоря уже о том, что сам компилятор Go не особенно зрелые, такие как escape-анализ и т. д. С точки зрения проблем, собственная модель памяти Go и зрелость GC несопоставимы с Java. Еще один важный момент заключается в том, что зрелость фреймворка Go не находится на том же уровне, что и Java, но я считаю, что со временем они будут постепенно развиваться.
Короче говоря, значение данных измерения стресса для этой структуры заключается в том, чтобы понять потолок производительности и оценить пространство и рентабельность инвестиций (отношение ввода-вывода) для дальнейшей оптимизации. Конкретный выбор фреймворка по-прежнему зависит от сценария использования, производительности, простоты использования, масштабируемости, стабильности и внутренней экологии компании, язык и производительность — лишь один из факторов.
Различные сценарии применения различных фреймворков приводят к разным приоритетам оптимизации.Например, spring web жертвует производительностью ради простоты использования, масштабируемости и стабильности, но также имеет огромное сообщество и пользователей. Другим примером являются естественные преимущества параллельного программирования Go в сценарии Service Mesh Sidecar, а также небольшой объем памяти, быстрый запуск, скомпилированный язык и другие функции, которые делают его более подходящим, чем Java.
(Приложение: На самом деле, я использую приведенный выше код и файл докера для сборки и использую тот же сценарий стресс-теста. Производительность сериализации Json фреймворка go fasthttp-easyjson-prefork выше, чем у Java wizzardo-http и firenio под Эксклюзивный тест машины Alibaba Cloud с 4 ядрами.-http-lite составляет более 30% и имеет более низкую задержку, что может быть связано с ядром).
В: Почему многие исходные Java-проекты пытаются переписать открытый исходный код на ходу?
Пустой:Java или go core — это экологический вопрос.
Экологическое развитие пройдет несколько стадий старта, развития, расцвета, стагнации и упадка.Ява как минимум в стадии расцвета, а го пока в стадии развития.На разных стадиях есть различия в количестве и качестве разработчики, богатство возможностей с открытым исходным кодом и инженерная поддержка.. Есть огромная разница, Go безумно дополняет эти три части. Кроме того, у разных компаний также есть проблемы со стадией внутренней небольшой экологии компании, что также повлияет на решение о выборе технологии.
На данном этапе Go очень популярен. Важным фактором является то, что облачные технологии продвигают всех вперед. У оператора k8s есть собственный ореол, а различные возможности промежуточного программного обеспечения снижаются и интегрируются с k8s, вызывая волну реализации базовых Возможности промежуточного программного обеспечения Тенденции, но базовые возможности промежуточного программного обеспечения представляют собой относительно ограниченные наборы, такие как RPC, конфигурация, очередь сообщений и т. д. Эти возможности промежуточного программного обеспечения, а также то, что облачный собственный k8s должен делать для бизнеса верхнего уровня, — это нейтральность языка разработки, чтобы бизнес основывался на компании Выбирать между малой экологией всего языка и большой экологией всей языковой технологии, если заставлять бизнес развиваться на языке го, то это будет хулиган.
Таким образом, для интеграции базовых возможностей промежуточного программного обеспечения с k8s требуется мощь языка go, но другие возможности всей экосистемы с открытым исходным кодом не обязательно необходимы; отдел разработки бизнеса выбирает наиболее подходящий язык разработки в соответствии с экологией компании и экологией технологий, а также Не слепое преследование приводит к затруднениям с точки зрения людей, возможностей открытого исходного кода и инженерной поддержки. Для того, чтобы язык го мог проявить свою силу в бизнес-исследованиях и разработках, все еще требуется дальнейшее развитие его экологии.
В: Упадет ли Java из-за появления контейнеров?
Глубокая сила:В последние годы облачные технологии, основанные на контейнерах, значительно улучшили масштабируемость и совместную работу развертываний на стороне сервера. В определенной степени ослабла важность выбора самого исходного языка разработки. Но это не мешает самому языку Java продолжать сохранять свою жизнеспособность.
В конце концов, с точки зрения исследований и разработок, эффективность результатов исследований и разработок также является ключевым фактором. Благодаря совершенной и огромной экосистеме разработчиков Java предоставляет библиотеки классов / платформы, которые богаче, чем большинство языков. Мощные инструменты IDE Java часто дают вдвое больший результат с половиной усилие при разработке.
Более того, у самой Java есть несколько вариантов языков (например, Scala), которые тоже развиваются в более гибком и полезном направлении;
С другой стороны, в сфере больших данных по-прежнему ярко светит Java, мы знаем ES, Kafka, Spark, Hadoop.
Когда мы оцениваем и прогнозируем жизнеспособность технологии, мы часто не только смотрим на саму технологию изолированно, но и объединяем всю экологию, стоящую за ней. Устойчивая жизнеспособность технологии часто поддерживается зрелой экосистемой.Как упоминалось выше, Java имеет полную и огромную экосистему во многих областях.Поэтому мы считаем, что жизнеспособность Java по-прежнему устойчива.
Однако в силу известных причин, объективно говоря, сама Java будет иметь определенные ограничения в ее использовании. Кроме того, в сценарии контейнера конфигурация памяти процесса Java должна быть тщательной.
В целом, статус Java по-прежнему сложно пошатнуть, и на облачной сцене он по-прежнему процветает.