Оптимизация Dubbo 2.7.5 для многопоточной модели

Java

Это 30-я оригинальная статья о том, почему технологии

Это может быть первая статья во всей сети, в которой анализируется одно из улучшений в промежуточной версии Dubbo 2.7.5: оптимизация модели клиентских потоков.

Сначала уговорите бросить: Текст состоит из 8190 слов и 54 картинок. Перед чтением вам необходимо иметь определенную базу знаний, связанных с Dubbo. Содержание относительно хардкорное, и я советую вам прочитать его внимательно.

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

Содержание этой статьи

Раздел 1: Официальный релиз

Этот раздел в основном использует официальную статью под названием «Dubbo выпускает версию Milestone, повышение производительности на 30%» в качестве введения и ведет к содержимому, которое будет опубликовано в этой статье: оптимизация модели клиентского потока.

Раздел 2: Введение на официальном сайте

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

Раздел 3. Проблемы с моделями потоков до версии 2.7.5.

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

Раздел 4: Что такое безрезьбовой

Третий раздел ведет к новой версии решения, без потоков. и краткое введение в него.

Раздел 5: Воспроизведение сцены

Из-за ограниченных условий воспроизвести сцену сложнее, но я нашел хорошую концовку в выпуске № 890, поэтому я перешел.

Раздел 6: Сравнение старых и новых моделей потоковой передачи

Этот раздел служит руководством для сравнения процесса вызова старой и новой моделей многопоточности и сравнения кодов клавиш версии 2.7.4.1 и версии 2.7.5.

Раздел VII: Введение в версию Dubbo.

Воспользовавшись этим обновлением версии, я также пользуюсь возможностью представить две основные версии Dubbo: 2.6.X и 2.7.X.

Официальный релиз

9 января 2020 года промежуточное ПО Alibaba выпустило«Dubbo выпускает промежуточную версию с улучшением производительности на 30%»статья:

В статье сказано, что этоЗнаковый выпуск Dubbo.

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

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

Дух исследования отражен в исследовании Dubbo многоязычности и проникновения протоколов.

В статье перечислены девять основных точек преобразования.В этой статье представлена ​​только одна точка преобразования в версии 2.7.5:Оптимизированная потребительская модель потоковой передачи.

Большая часть исходного кода в этой статье относится к версии 2.7.5, для сравнения также будет исходный код версии 2.7.4.1.

Введение на официальном сайте

Прежде чем представить оптимизированную модель потребительских потоков, давайте кратко представим, что такое модель потоков Dubbo.

Просто посмотрите на описание в официальном документе. Официальный документ Dubbo — очень хороший учебный документ начального уровня. Многие пункты знаний написаны очень подробно.

К сожалению, в модели резьбы она далека от удовлетворительной, слов всего несколько, а картинка неудовлетворительна:

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

Поэтому я объединил официальную документацию и исходный код версии 2.7.5, чтобы дать краткое введение.В процессе чтения исходного кода вы найдете:

на стороне клиента, в дополнение к пользовательскому потоку будет пул потоков с именем DubboClientHandler-ip:port, реализацией по умолчанию которого является пул потоков кэша.

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

Метод setThreadName на приведенном выше рисунке предназначен для установки имени потока:

org.apache.dubbo.common.utils.ExecutorUtil#setThreadName

Хорошо видно, что если имя потока не указано, по умолчанию используется DubboClientHandler-ip:port.

на стороне сервера, в дополнение к потокам босса, рабочим потокам (потокам ввода-вывода) и пулу потоков с именем DubboServerHandler-ip:port, реализация которого по умолчанию является фиксированным пулом потоков.

Конфигурация dubbo.xml для включения пула потоков выглядит следующим образом:

<dubbo:protocol name="dubbo" threadpool="xxx"/>

Вышеупомянутое xxx может быть фиксированным, кэшированным, ограниченным, нетерпеливым, где fixed является реализацией по умолчанию. Конечно, поскольку это SPI, его также можно расширить самостоятельно:

Поэтому, исходя из последней версии 2.7.5, описание этого места в красном поле под официальным документом вводит в заблуждение:

В интерфейсе SPI фиксированное значение действительно является значением по умолчанию.

Но поскольку клиент добавил строку кода (93 строки, упомянутые ранее) перед инициализацией пула потоков,Таким образом, реализация клиента по умолчанию кэшируется, а реализация сервера по умолчанию фиксируется.

Я также смотрел предыдущие версии, по крайней мере, в 2.6.0 (более ранние версии не смотрел), реализация пула потоков клиента по умолчанию кэшируется.

Описание части Dispatcher в порядке:

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

Вот чуть более подробноПотоковая модель до версии 2.7.5,Для справки:

图片来源:https://github.com/apache/dubbo/issues/890

Проблемы с моделями потоков до 2.7.5

Итак, какие проблемы с моделью потоков до улучшения?

В статье «Dubbo выпускает версию Milestone, производительность увеличивается на 30%» это описано следующим образом:

对 2.7.5 版本之前的 Dubbo 应用,尤其是一些消费端应用,当面临需要消费大量服务且并发数比较大的大流量场景时(典型如网关类场景),经常会出现消费端线程数分配过多的问题。

При этом в статье дана ссылка на выпуск:

https://github.com/apache/dubbo/issues/2013

В этом разделе я буду следить за выпуском № 2013, чтобы рассказать вам о проблемах с потоковой моделью до версии Dubbo 2.7.5, а точнее, о проблеме с клиентской потоковой моделью:

Во-первых, Джаски, анализируя issue #1932, сказал, что в некоторых случаях будет создаваться очень большое количество потоков, поэтому у процесса будут проблемы с OOM.

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

Теперь давайте перейдем к выпуску № 1932 и посмотрим, что там написано:

https://github.com/apache/dubbo/issues/1932

Вы можете видеть, что проблема № 1932 также была предложена Джаски, и он в основном передал смысл:Почему я ставлю actives=20, но есть более 10 000 тредов с именем треда DubboClientHandler со статусом заблокированным на стороне клиента? Это ошибка?

Только для этой проблемы позвольте мне сначала ответить на это: не ошибка!

Давайте сначала посмотрим, что означает actives=20:

Согласно пояснению на официальном сайте:Значение actives=20 заключается в том, что максимальное количество одновременных вызовов на метод для одного потребителя службы равно 20.

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

Активы настроены в«Одна статья объясняет алгоритм минимального активного количества балансировки нагрузки Dubbo»Это также объясняется в этой статье. Это вступает в силу с фильтром ActiveLimitFilter.Значение actives по умолчанию равно 0, что означает отсутствие ограничений. Когда actives>0, ActiveLimitFilter вступает в силу автоматически. Поскольку это не является предметом данной статьи, здесь подробно объясняться не будем, а те, кому интересно, могут прочитать предыдущие статьи.

После выпуска № 2013 мы видим проблему, упомянутую в выпуске № 1896:

Вопрос 1 Как я уже объяснял ранее, его догадка здесь верна в первой половине предложения и неверна во второй половине. Больше ни слова.

Здесь мы в основном рассматриваем проблему 2 (вы можете щелкнуть по большой картинке, чтобы увидеть ее): существует больше поставщиков услуг и больше пулов потоков, поддерживаемых потребителем. В результате, несмотря на то, что производительность поставщика услуг велика, на стороне потребителя потребляется огромное количество потоков. Он и приятели из задачи № 4467 ниже выражают одно и то же значение: ему нужен общий пул потоков.

Затем мы прокручиваем вниз и можем найти выпуск № 4467 и выпуск № 5490.

Для вопроса № 4467 CodingSinger сказал:Почему Dubbo создает пул потоков для каждой ссылки?

Из исходного кода Dubbo 2.7.4.1 мы также можем видеть, что пул потоков действительно создается для каждого соединения в конструкторе WarppedChannelHandler:

Что хочет выразить проблема № 4467?

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

Я также лично считаю, что это место не должно выполнять изоляцию потоков. Вариант использования для изоляции потоков должен быть для некоторых особенно важных методов или методов, которые особенно медленны, или методов с большими функциональными различиями. Очевидно, что клиент Dubbo не различает, даже если метод имеет несколько соединений (настроенных с помощью параметра connection), что не соответствует сценарию использования изоляции потоков.

Тогда цыплений ответил на этот вопрос 24 июля 2019 года:

Существующая конструкция:Сторона поставщика по умолчанию совместно использует пул потоков. На стороне потребителя каждая ссылка совместно использует пул потоков.

Он также сказал:Для потребительского пула потоков он в настоящее время пытается оптимизировать.

Подразумевается, что он также считает, что существующая модель многопоточности на стороне потребителя также имеет возможности для оптимизации.

Вставьте сюда предложение: Who is Chickenlj?

刘军,GitHub账号Chickenlj,Apache Dubbo PMC,项目核心维护者,见证了Dubbo从重启开源到Apache毕业的整个流程。现任职阿里云云原生应用平台团队,参与服务框架、微服务相关工作,目前主要在推动Dubbo开源的云原生化。

Автор этой статьи, его слова до сих пор имеют большой вес.

Я уже слышал его рассказ на Dubbo Developer Day Chengdu Station:

Если вас интересует содержание его выступления, вы можетеОтвет в фоне официального аккаунта: 1026. Получите PPT лектора и адрес записи.

Хорошо, давайте перейдем к проблеме № 5490, упомянутой ранее.16 декабря 2019 года Лю Цзюнь сообщил, что в версии 2.7.5 будет представлен механизм безпотокового исполнителя для оптимизации и улучшения модели клиентских потоков.

Что такое безрезьбовой?

По описанию класса мы можем знать:

Самое важное различие между этим Исполнителем и другими обычными Исполнителями заключается в том, чтоЭтот Executor не управляет потоками.

Задачи, переданные этому исполнителю через метод execute(Runnable), не будут планироваться для конкретного потока, а другие исполнители будут передавать Runnable потоку для выполнения.

Эти задачи хранятся в очереди блокировки и будут выполняться только тогда, когда thead вызовет метод waitAndDrain(). Проще говоря,Объявление, выполняющее задачу, точно такое же, как и объявление, вызывающее метод waitAndDrain().

Упомянутый в нем метод waitAndDrain() выглядит следующим образом:

Метод execute(Runnable) выглядит следующим образом:

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

воспроизведение сцены

Выше упоминалось так много проблем с потоковой моделью до версии 2.7.5.Как мы можем воспроизвести это?

У меня здесь ограниченные условия, и воспроизвести сцену сложнее, но я нашел хорошую концовку в issue#890, могу переместить сюда:

Согласно его следующему описанию, составьте ментальную карту следующим образом:

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

Сравнение старой и новой моделей резьбы

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

старая модель резьбы

Старая модель пула потоков выглядит следующим образом, обратите внимание на цвет линии:

1. Бизнес-поток отправляет запрос и получает экземпляр Future.

2. Затем бизнес-поток вызывает future.get для блокировки и ожидания возврата бизнес-результата. 3. Когда бизнес-данные возвращаются, они передаются в независимый пул потребительских потоков для десериализации и другой обработки, и вызывается future.set для возврата десериализованных бизнес-результатов. 4. Бизнес-поток возвращает результат напрямую.

новая модель потоков

Новая модель пула потоков выглядит следующим образом, обратите внимание на цвет линии:

1. Бизнес-поток отправляет запрос и получает экземпляр Future. 2. Прежде чем вызывать future.get(), сначала вызовите ThreadlessExecutor.wait().Wait заставит бизнес-поток ожидать в блокирующей очереди, пока в очередь не будет добавлен элемент. 3. Когда бизнес-данные возвращаются, выполнимая задача создается и помещается в очередь ThreadlessExecutor. 4. Бизнес-поток берет Task и выполняет десериализованные бизнес-данные в этом потоке и устанавливает для него значение Future. 5. Бизнес-поток возвращает результат напрямую.

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

сравнение кода

Далее мыСравните код версии 2.7.4.1 и версии 2.7.5, чтобы проиллюстрировать вышеуказанные изменения.

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

Первый шаг одинаков для обеих версий:Бизнес-поток делает запрос и получает экземпляр Future.

Однако код реализации отличается, в версии 2.7.4.1 показан следующий код:

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

Исходный код метода newFuture выглядит следующим образом:Пожалуйста, имейте в виду этот метод, мы сравним позже:

В то же время из исходного кода видно, что после получения экземпляра Future сразу вызывается метод subscribeTo, а метод реализации выглядит следующим образом:

Использование CompletableFuture из Java 8 для реализации асинхронного программирования.

Но в версии 2.7.5, как показано в следующем коде:

В методе запроса есть параметр исполнителя, и класс реализации этого параметра — ThreadlessExecutor.

Далее, как и в предыдущем варианте, через метод newFuture будет получен объект DefaultFuture:

Сравнив с методом newFuture версии 2.7.4.1, вы обнаружите, что это место сильно отличается.Хотя все они получают Будущее, содержание в Будущем разное.

Непосредственно к предыдущей таблице сравнения кодов с первого взгляда становится ясно:

Шаг 2: Затем бизнес-поток вызывает future.get для блокировки и ожидания возврата бизнес-результата.

Так как Dubbo по умолчанию является синхронным вызовом, а разница между синхронным и асинхронным вызовом есть в первой статье«Асинхронное преобразование новых функций Dubbo 2.7»Подробно анализируется в:

Находим место где асинхронно синхронно, сначала посмотрим на следующий код в версии 2.7.4.1:

Исходный код, соответствующий asyncResult.get(), здесь CompletableFuture.get():

В версии 2.7.5 изменилось соответствующее место:

Изменение в этом методе asyncResult.get.

В версии 2.7.5 исходный код реализации этого метода:

Давайте поговорим о месте с пометкой ②, которое совпадает с версией 2.7.4.1 и вызывает CompletableFuture.get(). Но есть еще логика кода, помеченная ①. иЭтот код является местом, где раньше отражалась новая модель многопоточности, часть, заключенная в красную рамку ниже:

Перед вызовом future.get() (то есть перед вызовом кода, помеченного ②), вызовите ThreadlessExecutor.wait() (то есть логику, помеченную ①), ожидание заставит бизнес-поток ожидать в блокирующей очереди до тех пор, пока очередь не добавляются элементы.

Далее сравните два места:

Первое место: упомянутый ранее WrappedChannelHandler, вы можете видеть, что версия 2.7.5 имеет очень большую трансформацию своего конструктора:

Второе место: Диспетчеру, упомянутому ранее, нужно написать еще одну статью, чтобы было понятно. Я просто делаю введение. Упомяну:

AllChannelHandler — это стратегия по умолчанию, а код подтверждения выглядит следующим образом:

В первую очередь обратите внимание на место с пометкой ②, оно вроде сильно изменилось, но на самом деле код извлечен и инкапсулирован. Метод sendFeedback выглядит следующим образом и совпадает с кодом в месте, помеченном ② в версии 2.7.4.1:

Итак, давайте сосредоточимся на сравнении двух мест, отмеченных ①, их методы получения исполнителей изменились:

2.7.4.1版本的方法是getExecutorService()
2.7.5版本的方法是getPreferredExecutorService()

Код выглядит следующим образом, разницу между двумя версиями Yipin пробует каждый:

В основном переводите комментарии к методу getPreferredExecutorService:

Currently, this method is mainly customized to facilitate the thread model on consumer side.
1. Use ThreadlessExecutor, aka., delegate callback directly to the thread initiating the call.   
2. Use shared executor to execute the callback.

В настоящее время использование этого подхода в основном адаптировано к модели многопоточности клиента.

1. Используйте ThreadlessExceutor, также известный как, чтобы делегировать обратный вызов непосредственно потоку, который инициировал вызов. 2. Используйте общий исполнитель для выполнения обратного вызова.

小声说一句:这里这个aka怎么翻译,我实在是不知道了。难道是嘻哈里面的AKA?大家好,我是宝石GEM,aka(又名) 你的老舅。又画彩虹又画龙的。

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

Введение в версию Dubbo

Воспользовавшись этим обновлением версии, давайте также представим текущую основную версию Dubbo.

Согласно сообщению Лю Цзюня: В настоящее время сообщество Dubbo поддерживает две основные версии, 2.6.x и 2.7.x, среди которых:

2.6.x в основном основан на исправлениях ошибок и небольшом количестве улучшений, поэтому стабильность полностью гарантирована.

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

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

Видно, что рекомендация сообщества по обновлению до последней версии 2.7.5 такова:Не рекомендуется для массового производства.

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

Но я думаю, что версия 2.7.5 — это кульминация в процессе разработки Dubbo.Эта версия — первая попытка Dubbo привести в соответствие всю облачную систему микросервисов. Изучение направлений многоязычной поддержки. Реализована поддержка протокола HTTP/2 и добавлена ​​интеграция с Protobuf.

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

Приветствую открытый исходный код, приветствую инженеров с открытым исходным кодом.

В общем, круто.

Последнее слово

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

Спасибо за чтение,Я придерживаюсь оригинала, добро пожаловать и спасибо за внимание.

выше.

Добро пожаловать в публичный аккаунт [почему технологии]. Здесь я поделюсь некоторыми вещами, связанными с технологиями, сосредоточившись на java, изобретательном наборе кода и неся ответственность за каждую строку кода. Время от времени я буду говорить о жизни, писать рецензии на книги и обзоры фильмов. Пусть мы с тобой прогрессируем вместе.

公众号-why技术
Официальный аккаунт-почему технология