Всем привет, меня зовут да.
Все мы знаем, что сообщения RocketMQ и Kafka хранятся на диске, так почему же сообщения могут быть прочитаны и записаны так быстро, если они хранятся на диске? Делали ли вы какие-либо оптимизации? Есть ли разница между реализацией обоих из них? Каковы преимущества и недостатки каждого из них?
Сегодня мы узнаем.
Носитель данных — диск
Вообще говоря, сообщения промежуточного программного обеспечения сообщений хранятся в локальных файлах, потому что непосредственное размещение локальных файлов является самым быстрым и стабильным с точки зрения эффективности. В конце концов, если его разместить в стороннем хранилище, например в базе данных, будет на одну зависимость больше и меньше безопасности, и будут накладные расходы на сеть.
Узким местом процесса хранения сообщений в файлах на диске является запись и чтение на диск. Мы знаем, что диски относительно медленны для чтения и записи, так как же можно достичь высокой пропускной способности, используя диски в качестве носителя данных?
последовательное чтение и запись
ответпоследовательное чтение и запись.
Узнайте первымкеш страницы, кеш страниц — это своего рода кеш, используемый операционной системой в качестве диска для сокращения операций дискового ввода-вывода.
При записи на диск он фактически записывается в кэш страниц, превращая запись на диск в запись в память. Записанные страницы становятся грязными, и операционная система при необходимости записывает грязные страницы на диск.
При чтении, если кеш страницы попадает, он возвращается напрямую.Если кеш страницы промахивается, генерируется прерывание ошибки страницы, и данные загружаются с диска в кеш страницы, а затем данные возвращаются.
и при чтениичитать вперед, по принципу локальности, при чтении в страничный кеш считываются соседние блоки диска. когда пишешьписать после, кэш страницы также записывается, так что некоторые небольшие операции записи могут быть объединены в большие операции записи, а затем диск сбрасывается.
Более того, согласно структуре диска, при последовательном вводе-выводе магнитная головка практически не нуждается в смене дорожки, либо время на смену дорожки очень мало.
Согласно некоторым результатам тестов в Интернете, последовательная запись на диск выполняется быстрее, чем случайная запись в память.
Конечно, при такой записи есть риск потери данных, например, если машину внезапно отключить, те грязные страницы, которые не были сброшены, будут потеряны. Но вы можете позвонитьfsync
Вынужден почистить диск, но при этом большая потеря производительности.
следовательноОбычно рекомендуется использовать механизм многократного копирования для обеспечения надежности сообщения, а не синхронно очищать диск..
Вы можете видеть, что последовательный ввод-вывод адаптируется к структуре диска, и есть упреждающее чтение и отложенная запись. И RocketMQ, и Kafka поддерживают последовательную запись и приблизительно последовательное чтение. Все они используют добавление файлов для записи сообщений.В конец файла журнала можно записывать только новые сообщения, а старые сообщения нельзя изменить.
mmap - карта памяти файлов
Из приведенного выше видно, что доступ к файлу на диске загрузит данные в кеш страниц, но кеш страниц принадлежит пространству ядра и не может быть доступен для пространства пользователя, поэтому данные необходимо скопировать в пространство пользователя. буфер.
Видно, что к данным нужно обращаться из кэша страниц через программу копирования, поэтому доступ к ним можно получить и черезmmap
Чтобы провести волну оптимизаций, используйте файлы с отображением памяти, чтобы избежать копирования.
Проще говоряСопоставление файлов заключается в прямом сопоставлении виртуальных страниц программы с кэшем страниц, чтобы не было необходимости копировать режим ядра в пользовательский режим, а также это позволяло избежать создания дубликатов данных.. и больше не нужно звонитьread
илиwrite
способ чтения и записи файлов,Им можно управлять напрямую, отображая адрес плюс смещение..
sendfile - нулевая копия
Поскольку сообщение хранится на диске, когда потребитель извлекает сообщение, оно должно быть взято с диска. Давайте сначала посмотрим на общий процесс отправки файлов.
просто скажиDMA
Каково полное название прямого доступа к памяти, он можетНезависимое чтение и запись системной памяти напрямую, не требует вмешательства ЦП, например видеокарты, сетевые карты и т.д.DMA
.
Вы можете видеть, что данные на самом деле избыточны, поэтому давайте посмотримmmap
Каков процесс отправки файлов после этого?
Видно, что количество переключений контекста не изменилось, но на одну копию данных меньше, что то же самое, о чем мы говорили выше.mmap
Эффект, которого можно добиться, тот же.
Но данные все равно избыточны, не лучше ли копировать данные прямо из кеша страниц на сетевую карту?sendfile
иметь этот эффект. Давайте сначала взглянем на версию Linux 2.1.sendfile
.
Поскольку только один системный вызов удовлетворяет требованиям отправки, по сравнению сread + write
илиmmap + write
Переключения контекста определенно меньше, но кажется, что данные все равно избыточны. Да так линукс 2.4 версия sendfile + DMA с Scatter-gather. Реализовано отсутствие избыточности.
Это то, что мы часто называем нулевым копированием в Java.FileChannal.transferTo()
Нижний слойsendfile
.
Далее давайте посмотрим, как вышеуказанные пункты применяются в RocketMQ и Kafka.
Применение RocketMQ и Kafka
RocketMQ
использоватьTopic混合追加方式
, то есть файл CommitLog будет содержать все сообщения, назначенные этому Брокеру, независимо от того, к какой Очереди какой темы относится сообщение.
Итак, все новости приходятПоследовательно добавлять записи в CommitLogи установить CosumerQueue, соответствующую сообщению, а затем потребитель получает реальный физический адрес сообщения через CosumerQueue, а затем переходит к CommitLog, чтобы получить сообщение. CosumerQueue можно понимать как индекс сообщения.
В RocketMQ и CommitLog, и CosumerQueue используют mmap.
Используется по умолчанию при отправке сообщениязаключается в том, чтобы скопировать данные в динамическую память, а затем отправить их. Смотрим код.
Вы можете увидеть эту конфигурациюtransferMsgByHeap
Значение по умолчанию — true , тогда давайте посмотрим на код, когда потребитель извлекает сообщение.
Вы можете видеть, что RocketMQ по умолчанию копирует сообщение в буфер в куче, а затем вставляет его в тело ответа для отправки. Однако его можно настраивать по параметрам без прохождения кучи, но он не использует истинную нулевую копию, а отправляется в SocketBuffer через mapedBuffer.
Итак, RocketMQ использует последовательную запись на диск и mmap. Sendfile не используется, и есть копия буфера страницы в SocketBuffer.
Тогда при вытягивании сообщений, строго говоря, чтение для CommitLog происходит случайно, т.к. сообщения CommitLog хранятся в смешанном порядке, но в целом из CommitLog сообщения читаются последовательно, от старых данных к новым данным читаются по порядку. ** И, вообще говоря, сообщение будет использовано сразу после его сохранения, поэтому в это время сообщение должно все еще находиться в кэше страниц, поэтому нет необходимости читать диск.
И мы упоминали выше,Кэш страниц будет регулярно обновляться, что не поддается контролю, а память ограничена, и будут такие ситуации, как своп, а **mmap на самом деле выполняет только сопоставление. Если при фактическом чтении страницы происходит ошибка страницы, данные будут загружены в память. ** Это может вызвать сбои в мониторинге очереди сообщений.
Поэтому RocketMQ сделал некоторые оптимизации, в том числе:Предварительное размещение файла и прогрев файла.
предварительное размещение файла
Размер CommitLog по умолчанию составляет 1G.При превышении ограничения размера необходимо подготовить новый файл, и RocketMQ запускает фоновый поток.AllocateMappedFileService
, непрерывная обработкаAllocateRequest
, AllocateRequest на самом деле является запросом предварительного выделения, который заранее подготовит выделение следующего файла, чтобы предотвратить выделение файла в процессе записи сообщения, что приводит к дрожанию.
разогрев файла
есть одинwarmMappedFile
метод, он будет проходить каждую страницу текущего сопоставленного файла, записывать 0 байт, а затем вызыватьmlock
а такжеmadvise(MADV_WILLNEED)
.
давайте посмотрим еще разthis.mlock
, который на самом деле называется внутреннеmlock
а такжеmadvise(MADV_WILLNEED)
.
mlock: часть или все адресное пространство, используемое процессом, может быть заблокировано в физической памяти, чтобы предотвратить его обмен в пространство подкачки.
madvise: Сообщите ОС, что этот файл будет доступен в ближайшем будущем, поэтому было бы неплохо прочитать несколько страниц вперед.
Резюме RocketMQ
Последовательная запись на диск, в целом, является последовательным чтением диска, и используется mmap, а не настоящая нулевая копия. А из-за неопределенности страничного кеша и ленивой загрузки mmap (данные фактически загружаются, когда страница прерывается во время доступа), используется предварительное выделение файла и предварительный нагрев файла для записи 0 байт на страницу, а затем вызовmlock
а такжеmadvise(MADV_WILLNEED)
.
Kafka
Хранилище журнала Kafka отличается от RocketMQ, это раздел и файл.
Запись сообщений Kafka также является последовательной записью для одного раздела.Если разделов немного, это считается последовательной записью в целом.Его файл журнала не использует mmap, а индексный файл использует mmap. Но для отправки сообщений Kafka использует нулевое копирование.
mmap не очень полезен для написания сообщений, потому что сообщение приходит из сети. Для отправки сообщений я думаю, что sendfile более эффективен, чем mmap+write, потому что в SocketBuffer на одну копию кэша страниц меньше.
Давайте взглянем на исходный код сообщения, отправленного Kafka.Последний вызовFileChannel.transferTo
, нижний слой — sendfile.
Из исходников Kafka я не вижу никаких операций типа mlock похожих на RocketMQ.Я думаю причина в том, что во-первых лог не использует mmap, а потом swap может реально передать системные параметры Linux.vm.swappiness
Для настройки рекомендуется установить его на 1 вместо 0.
Предполагая, что памяти действительно не хватает, если он установлен в 0, если память исчерпана, она не может быть заменена, и некоторые процессы будут резко завершены. Если вы поставите 1, вы можете хотя бы тянуть его некоторое время.Если у вас есть хорошие методы мониторинга, вы также можете дать шанс узнать, чтобы он не остановился внезапно.
Сравнение RocketMQ и Kafka
первыйОни все пишутся последовательно, но RocketMQ хранит все сообщения в одном файле, а Kafka — по одному файлу на раздел.
Один файл на разделБолее гибкий с точки зрения миграции или репликации данных.
ноЕсли разделов слишком много, запись должна часто переключаться между несколькими файлами.Для каждого файла он записывается последовательно, но с глобальной точки зрения это на самом деле случайная запись, и то же самое верно и при чтении. читать. И RocketMQ с одним файлом не имеет этой проблемы.
Что касается отправки сообщений, RocketMQ использует метод mmap + write и выполняет предварительный нагрев, чтобы уменьшить проблемы с производительностью mmap больших файлов из-за сбоев страниц. И Kafka использует sendfile.Условно говоря, я думаю, что kafka более эффективна для отправки, потому что меньше копии кеша страниц в SocketBuffer.
И проблему свопа тоже можно задать через системные параметры.
наконец
В середине этой статьи застрял RocketMQ, а исходный код до сих пор не знаком, и это немного сбивает с толку. Благодаря совету Дин Вэй. Иначе я застряну в тупике и не смогу выбраться.
Наконец, я рекомендую «Инсайдер технологии RocketMQ: принципы проектирования и реализации архитектуры RocketMQ» Дина Вея и Чжоу Цзифэна. Студенты, интересующиеся RocketMQ, могут взглянуть.
Если в статье есть какие-либо ошибки, пожалуйста, свяжитесь со мной как можно скорее, спасибо!
Я да, от мала до миллиарда, увидимся в следующей статье.
Прошлые рекомендации:
Горячие точки интервью очереди сообщений
Моего двоюродного брата обругали в интервью, я научила его тайничать даже трюки
Интервьюер: Расскажите мне обо всем процессе обработки запросов Kafka.