Интервьюер: Скажите, сколько времени?

задняя часть
Интервьюер: Скажите, сколько времени?

Здравствуйте, я криворукий.

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

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

Когда большинство людей говорят о колесах времени, они начинают с нетти.

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

Более того, колесо времени Dubbo также взято из исходного кода Netty, что в основном то же самое.

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

Позвольте мне начать с этого класса в Dubbo:

org.apache.dubbo.rpc.cluster.support.FailbackClusterInvoker

Отказоустойчивость относится к политике ошибок кластера:

Неважно, что вы не знаете Dubbo, вам просто нужно знать, как он представлен на официальном сайте:

Момент, который я хочу подчеркнуть, заключается в том, что "синхронизированная ретрансляция"Эти четыре слова.

Давайте не будем сначала смотреть на исходный код, что вы думаете о времени повторной передачи?

Есть ли что-нибудь думать о временной задаче?

Итак, как реализовать запланированные задачи?

Как правило, все могут думать о классах ScheduledExecutorService и Timer, предоставляемых в JDK.

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

ScheduledExecutorService используется относительно больше, в основном он имеет три типа методов:

Кратко расскажем о двух методах scheduleAtFixedRate и scheduleWithFixedDelay.

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

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

Первый подчеркивает время начала предыдущей задачи, а второй подчеркивает время окончания предыдущей задачи.

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

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

Сериализация всей функции повтора.

Так как же Dubbo достигает этого требования по времени повторной попытки?

Получите исходный код, под исходным кодом нет никаких секретов.

Готов идти.

исходный код

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

Не торопитесь, я не могу сделать это шаг за шагом.

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

Кроме того, я прямо, огрызнувшись, брошу решение вам в лицо, и вы его не примете.

Мне нравится мягкий способ преподавания.

Хорошо, посмотрите на исходный код ниже.

Не имеет значения, если вы не понимаете эти строки кода, вы в основном сосредоточены на логике в catch.

Я сопоставил код с введением на официальном сайте для вас.

Это означает, что вызов не удался, и есть addFailed, чтобы добраться до сути.

Что делает addFailed?

Что он делает, так это «регулярно отправляет» это:

org.apache.dubbo.rpc.cluster.support.FailbackClusterInvoker#addFailed

Этот метод может ответить на вопрос, который мы подняли ранее: как допустимая неисправность Cluster Dubbo достигает этого требования повторной попытки времени?

Как видно из места, помеченного ①, используется ScheduledExecutorService, а конкретной точкой является метод scheduleWithFixedDelay.

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

RETRY_FAILED_PERIODСколько это стоит?

Посмотрите на строку 52, это 5 секунд.

Кроме того, вы можете увидеть место, помеченное ③, в предыдущем методе addFailed, который помещает что-то в failed.

Что опять не получилось?

Глядя на предыдущую строку 61, это ConcurrentHashMap.

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

не удалось Когда эта карта используется?

См. метод retryFailed, отмеченный ②:

В этом методе будет пройдена неудачная карта, и все они будут извлечены и вызваны снова.

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

На данный момент мы даже раскрыли тайну класса FailbackClusterInvoker Dubbo.

Под завесой скрыта карта плюс ScheduledExecutorService.

Кажется, это не сложно, это очень традиционное решение, я могу его придумать.

Итак, вы медленно печатаете один на экране:

Но, друзья мои, посидите, пора "но", пора поворачивать.

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

Хорошо, тогда проблема, как предотвратить переполнение памяти?

Очень просто, сначала мы можем ограничить размер карты, правильно.

Например, ограничьте его вместимость до 1000.

Что делать после заполнения?

Можете ли вы использовать стратегию исключения: «первым пришел — первым вышел» (FIFO) или «последним пришел — первым ушел» (LIFO).

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

Переполнения памяти и упомянутые выше решения - не только мой бред.

У меня есть доказательства, потому что я видел его эволюцию из записи отправки FailbackClusterInvoker, Код на предыдущем снимке экрана также является кодом предыдущей версии, а не последним кодом:

В этом представлении упоминается проблема под номером 2425.

GitHub.com/Apache/Belly Daddy…

Упомянутые здесь проблемы и решения — это то, о чем я говорил ранее.

Наконец-то предзнаменование завершено, и история о колесе времени официально начнется.

принцип колеса времени

Некоторые друзья начали торопиться.

Хотите, чтобы я быстро загрузил исходный код колеса времени.

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

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

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

Прежде всего, самая основная структура колеса времени — это массив, такой как следующий массив длины 8:

Как стать колесом?

Просто соедините встык:

Если каждый элемент представляет одну секунду, то этоКруг массиваВремя, которое можно выразить, равно 8 секундам, что примерно так:

Обратите внимание, что ранее я выделил один круг, то есть 8 секунд.

Таким образом, 2 круга — это 16 секунд, 3 круга — 24 секунды, а 100 кругов — 800 секунд.

Это может понять, верно?

Я дам вам другую картинку:

Хотя длина массива всего 8, его можно накладывать один за другим, поэтому можно представить больше данных.

Например, я изменил первые три круга на картинке выше на это:

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

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

А колеса?

Английское слово для колеса — это колесо, поэтому теперь у нас есть массив с именем колесо:

Затем заполнение предыдущих данных, вероятно, будет таким.

Для удобства иллюстрации я заполняю только позиции с нижними индексами 0 и 3, а остальные места также имеют такое же значение:

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

800 mod 8 =0, что указывает на то, что его следует повесить там, где индекс равен 0:

Предположим, есть еще одна задача, которую нужно выполнить через 400 секунд?

Таким же образом вы можете продолжить добавлять:

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

Хорошо, теперь есть еще одна задача, которую нужно выполнить через 403 секунды, где она должна висеть?

403 mod 8 = 3, то это:

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

Потому что мне нужно выяснить еще одну вещь: очередь задач, которые нужно назначить.

Когда я нарисовал задачи на 800 секунд, 400 секунд и 403 секунды выше, я тоже пропустил шаг.

На самом деле это должно быть похоже:

Задачи не связаны с колесом времени в реальном времени, а сначала помещаются в очередь для распределения, а затем задачи в очереди для распределения связываются с колесом времени через определенное время.

Когда точно?

Давайте поговорим об исходном коде ниже.

На самом деле помимо очереди на выделение есть еще и очередь на отмену задачи.

Потому что задачи, поставленные в колесо времени, можно отменить.

Например, в Dubbo механизм колеса времени также используется для проверки времени ожидания вызова.

Предположим, что период времени ожидания вызова - 5s. После 5s задача должна быть вызвана, и выдается исключением тайм-аута.

Но если на запрос приходит ответ в течении 2с и таймаута нет, то задачу нужно отменить.

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

org.apache.dubbo.remoting.exchange.support.DefaultFuture#received

Принцип рисунка наверное такой, а тут еще картинка нужна.

Дайте вам имена полей в исходном коде, чтобы они соответствовали картинке выше.

Я в основном привожу вам соответствие этих объектов, и потом не составит большого труда посмотреть исходный код:

Вот так:

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

Наконец, позвольте мне еще раз упомянуть: например, в предыдущем сценарии FailbackClusterInvoker колесо времени запускает задачу повторной попытки, но все равно не удается, что мне делать?

Это очень просто, просто снова поставьте задачу, поэтому, если вы посмотрите на исходный код, там есть метод rePut, который делает это:

org.apache.dubbo.rpc.cluster.support.FailbackClusterInvoker.RetryTimerTask#run

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

Подождите, что еще я могу сделать после того, как узнаю здесь «количество повторных попыток»?

Например, если вы состыковались с WeChat Pay, его уведомление об обратном вызове имеет такой временной интервал:

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

Колесо времени может удовлетворить вышеуказанные требования.

Конечно, очередь задержки MQ тоже можно использовать, но это не тема данной статьи.

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

В дополнение к FailbackClusterInvoker, я действительно думаю, что колесо времени больше подходит для сердцебиения.

Это так уместно, сердцебиение Даббо осуществляется с помощью колеса времени.

org.apache.dubbo.remoting.exchange.support.header.HeartbeatTimerTask#doTask

Как видно из рисунка выше, метод doTask заключается в отправке пакета heartbeat, после завершения каждой передачи вызывается метод reput, после чего задача отправки пакета heartbeat снова возвращается на колесо времени.

Ну, больше не расширяйте сценарий приложений.

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

Размотать!

исходный код колеса времени

Принцип понятен на месте, и тогда мы можем посмотреть на наш исходный код.

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

Еще раз, мы смотрим на использование класса FailbackClusterInvoker в Dubbo по времени.

Во-первых, объект failTimer представляет собой знакомый одноэлементный шаблон с двойной проверкой:

Инициализированный здесь failTimer является ключевой логикой объекта HashedWheelTimer для вызова его конструктора.

Итак, давайте начнем с его метода строительства и приступим к его разрыву.

Давайте поговорим о том, что делают некоторые из его входных параметров:

  • threadFactory: фабрика потоков, вы можете указать имя потока и является ли он процессом демона.
  • tickDuration: интервал времени между двумя тиками.
  • unit: единица времени tickDuration.
  • TicksPerwheel: номер галочки времени внутри колеса.
  • maxPendingTimeouts: максимальное количество отложенных задач в колесе времени.

Следовательно, смысл колеса времени Даббо таков:

Создайте поток демона с именем потока failback-cluster-timer, который выполняет задачу каждую секунду. Размер этого временного колеса — 32, а максимальное количество задач, ожидающих обработки, – failbackTasks. Это значение можно настроить, и значение по умолчанию – 100.

Но во многих других сценариях использования, например, когда Dubbo проверяет время ожидания вызова, значение maxPendingTimeouts не отправляется:

org.apache.dubbo.remoting.exchange.support.DefaultFuture#TIME_OUT_TIMER

Он даже не опубликовал ticksPerWheel.

На самом деле эти два параметра имеют значения по умолчанию. По умолчанию TicksperWheel имеет значение 512. MaxpendingTimeouts по умолчанию имеет значение -1, что означает, что количество задач для ожидания обработки не ограничено:

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

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

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

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

Почему, вы спросите меня, почему?

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

Я считаю, что следующий фрагмент кода не нуждается во мне, чтобы объяснить, если вы не понимаете, просто удвойте текст из восьми частей HashMap:

Но я могу сказать больше об этой строке кодаmask = wheel.length - 1.

Потому что мы уже знаем, что wheel.length равен 2 ^ n.

Итак, предположим, что время отложенного выполнения нашей временной задачи равно x, какая сетка должна быть в колесе времени?

Должны ли мы взять остаток длины за x, который рассчитывается так: x % wheel.length.

Однако эффективность работы остатка на самом деле невысока.

Итак, как мы можем ускорить эту операцию?

длина колеса - 1.

Wheel.length равно 2 в степени n. После вычитания единицы все младшие биты его вторичной системы равны 1. Например, это формула:

Итак, х% длина колеса = х & (длина колеса - 1).

В маске исходного кода =wheel.length - 1.

Так где же используется маска?

Одно из таких мест находится в методе run класса Worker:

org.apache.dubbo.common.timer.HashedWheelTimer.Worker

Вычисленный здесь idx является нижним индексом массива, который необходимо обработать в данный момент.

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

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

У нас уже есть колесо времени раньше, так как же вызвать это время?

По сути, это вызов его метода newTimeout:

Этот метод имеет три дохода женьшеня:

Смысл понятен, то есть указанная задача (задача) начинает срабатывать через указанное время (задержку, единицу).

Далее прочитайте метод newTimeout:

Самый важный код в нем — это метод start, позвольте мне показать вам, что он делает:

Разделите его на две части.

Вышеприведенное фактически поддерживает или оценивает статус текущего HashedWheelTimer.Из исходного кода мы знаем, что статус имеет три значения:

  • 0: инициализировать
  • 1: началось
  • 2: закрыто

Если это инициализация, то посредством операции cas состояние обновляется для запуска, и выполняется операция workerThread.start() для запуска рабочего потока.

Следующая часть немного сбивает с толку.

Если startTime равно 0, то есть он не был инициализирован, вызовите ожидание CountDownLatch, чтобы подождать некоторое время.

И что ждут еще ждут, основной нить на главной ните здесь инициализирован ждет, что является то, что она логика?

Прежде всего, мы должны найти, где начинается инициализируется.

Он находится в методе run Worker, и этот метод запускается в предыдущем workerThread.start():

org.apache.dubbo.common.timer.HashedWheelTimer.Worker

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

После завершения инициализации startTime немедленно выполняется операция startTimeInitialized.countDown().

Здесь не совпадает?

Разве основной поток не может запуститься сразу?

Затем возникает проблема, здесь много проблем с инициализацией startTime, что такого, что основной поток не может продолжать выполняться?

Конечно полезно, вернитесь к методу newTimeout и посмотрите вниз:

Давайте проанализируем приведенное выше уравнение.

Первый System.nanoTime() — это реальное время выполнения кода до этого места.

Поскольку задержка является фиксированным значением, unit.toNanos(delay) также является фиксированным значением.

Затем System.nanoTime()+unit.toNanos(delay) — это количество наносекунд, в течение которого эта задача должна быть запущена.

Например.

Предположим, что System.NANOTIME () = 1000, UNIT.TONANOS (задержка) = 100.

Так что на этот раз задача запускается точка составляет 1000 + 100 = 1100.

Может ли это продолжаться?

Так зачем вычитать startTime ?

Ранее мы анализировали startTime, на самом деле это тоже System.nanoTime() при инициализации, после инициализации это фиксированное значение.

Разве System.nanoTime()-startTime почти не приближается к 0?

Что означает уравнение System.nanoTime()+unit.toNanos(delay)-startTime?

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

Но позже я анализирую, на самом деле только system.nanotime() является переменной во всем уравнении.

В первом расчете System.nanoTime()-startTime действительно близок к 0, но при втором запуске, то есть когда приходит вторая задача, когда вычисляется ее срок, System.nanoTime() равно Much больше, чем фиксированное значение startTime.

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

Анализируется предыдущий метод newTimeout, то есть основной поток выполняет логику, связанную с колесом времени в этом месте.

Что анализировать дальше?

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

Логика рабочего потока находится в методе запуска.

И основная логика находится внутри do-while:

Условием окончания цикла является то, что состояние текущего колеса времени не является начальным состоянием.

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

Далее, давайте рассмотрим логику в цикле построчно.Эта часть логики является основной логикой колеса времени.

прежде всегоfinal long deadline = waitForNextTick()В этой строке есть история:

Прежде всего, если вы посмотрите на название метода, вы поймете, что он делает.

Он ждет здесь до следующего момента.

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

Затем посмотрите на цикл for.Предыдущие части довольно запутаны.Легче понять только место, помеченное ③, которое позволяет текущему потоку спать в течение определенного времени.

Итак, предыдущая часть состоит в том, чтобы рассчитать, что такое указанное время.

Как это рассчитывается?

Место, отмеченное ①, еще можно понять в передней части,

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

Я не могу понять это сразу после этого.

Число 1000000 внутри легко понять: единицей измерения являются наносекунды, что в пересчете равно 1 миллисекунде.

Что это за 999999?

Фактически, 999999 здесь добавляет 1 миллисекунду к вычисляемому значению.

Например, крайний срок - currentTime рассчитывается как 1000123 наносекунды, тогда 1000123/1000000 = 1 мс.

Но (1000123+999999)/1000000=2 мс.

То есть пусть место, отмеченное ③, спит более 1 мс.

Почему это?

Я тоже не знаю, так что пока оставлю в покое, не большая проблема, а потом отпишусь.

Далее следует место, помеченное ②, которое, по-видимому, является специальной обработкой для операционной системы Windows, а значение sleepTimeM должно быть преобразовано в число, кратное 10.

Зачем?

Здесь я должен покритиковать Dubbo.Я взял реализацию Netty и спрятал ключевую информацию.Это неуместно.

Это место выглядит так в исходном коде Netty:

Вот четкое руководство:

GitHub.com/Нетти/Нетти…

И вдоль этой дороги, на всем пути вниз, вы найдете такое место:

Woohoo. Java что x.com/tutorials/he…

Неожиданно был получен неожиданный урожай.

Первое подчеркивание, вероятно, означает, что когда поток вызывает метод Thread.sleep, JVM сделает специальный вызов, чтобы установить период прерывания на 1 мс.

Поскольку реализация метода Thread.sleep опирается на проверку прерываний, предоставляемую операционной системой, то есть операционная система будет проверять, есть ли поток, которому нужно проснуться, и предоставлять ресурсы ЦП при каждом прерывании. Поэтому я думаю, что причина дополнительного сна в 1 мс впереди может быть объяснена этой причиной.

Отверстие, оставшееся впереди, было заполнено так быстро, что это удобно.

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

Следовательно, если это Windows, вам нужно отрегулировать время сна до 10 многократных.

Бесполезное знание для вас.

Предыдущие вопросы ясно поняты, и метод waitForNextTick хорошо понят.Что он делает, так это ждет, ждет временную шкалу и ждет такт времени.

После ожидания?

пришел к этой строке кодаint idx = (int) (tick & mask)

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

Затем код выполняется для этого методаprocessCancelledTasks()

Как видно из названия метода, это очередь для обработки отмененных задач:

Логика очень проста, на первый взгляд, это очистить очередь cancelledTimeouts.

Вот удаление, чистка.

Так где добавить, где добавить?

Именно в этом методе:

org.apache.dubbo.common.timer.HashedWheelTimer.HashedWheelTimeout#cancel

Если вызывается метод отмены HashedWheelTimeout, задача отменяется.

Я упомянул этот метод, когда рисовал картинку ранее, и логика очень ясна, поэтому я не буду много объяснять.

Но обратите внимание, где я подчеркнул: MpscLinkedQueue.

Что это?

Это довольно крутая безблокировочная очередь.

Но структура данных очереди cancelledTimeouts здесь, в Dubbo, явно использует LinkedBlockingQueue?

Что происходит?

Потому что комментарии здесь в Netty, а MpscLinkedQueue используется в Netty.

Вы видите, что я сравню вам разницу между Нетти и Даббо:

Так что здесь вводит в заблуждение.Если у вас есть время, вы можете дать Dubbo PR, чтобы изменить его.

Еще одна маленькая деталь.

Ну, затем мы прокручиваем вниз и приходим к этой строке кодаHashedWheelBucket bucket=wheel[idx]

С первого взгляда сказать нечего.

Получить ведро с указанным индексом из колеса времени.

В основном посмотрите на строку кода под нейtransferTimeoutsToBuckets()

Я все еще добавляю комментарии к каждой строке:

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

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

Это время.

Следующий анализbucket.expireTimeouts(deadline)эта строка кода.

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

Наконец, есть еще одна строка кодаtick++

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

Код ключа анализируется.

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

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

Почему раунд?

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

А, это очень хорошо, а потом ты расскажешь нам о каждом уровне, верно?

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

Да вините меня, я не то написал, в следующий раз, обязательно в следующий раз.

Но я могу показать вам, как kafka оптимизирует циклы времени. Вы будете аплодировать.

Несколько связанных вопросов

Наконец, что касается колеса времени Даббо, есть обсуждение в вопросах:

GitHub.com/Apache/Belly Daddy…

Кому интересно, может съездить посмотреть.

Есть интересный вопрос:

Netty много использует HashedWheelTimer в 3.x, но в 4.1 мы можем обнаружить, что Netty сохраняет HashedWheelTimer, но не использует его в своем исходном коде, а выбирает ScheduledThreadPoolExecutor, я не знаю, какова его цель.

На этот вопрос лично ответили сопровождающие Netty:

GitHub.com/Нетти/Нетти…

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

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

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

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

GitHub.com/Apache/Belly Daddy…

Это тоже оптимизация после того, как Даббо представил колесо времени.

Взгляните на него, выше — после оптимизации, а ниже — предыдущая запись:

В предыдущем методе записи поток запускается в фоновом режиме, а затем создается бесконечный цикл, и вся коллекция сканируется снова и снова:

Это решение также может удовлетворить потребности, но по сравнению с методом записи колеса времени выносится суждение.

Операция эффектная, скорость быстрая, сила высокая.

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

好了,看到了这里了,转发、在看、点赞随便安排一个吧,要是你都安排上我也不介意。 Написание статей утомительно и нуждается в небольшой положительной обратной связи.

Вот один для всех читателей и друзей:

Эта статья была включена в мой личный блог, играть может каждый.

www.whywhy.vip/