Модель ввода-вывода серверного сетевого программирования

задняя часть
Модель ввода-вывода серверного сетевого программирования

введение

   После перехода с T на A мой язык программирования также изменился с C++ на Java. Серверная разработка в T больше ориентирована на развитие бизнеса. На прошлой неделе я поделился «Программирование высокопроизводительной сети на стороне сервера» в группе компании А. Я был удивлен, что ни один из десяти человек в группе не занимался разработкой непосредственно на основе протокола TCP/IP. развитие бизнеса. Даже Netty, самая мощная сетевая библиотека на Java, использовалась только одним человеком. Но понять это несложно: промежуточная платформа компании А изолирует бизнес от нижнего уровня, позволяя программистам сосредоточиться на развитии бизнеса.

   Что лучше или хуже? Я не могу обобщать, я до сих пор помню, что брал интервью у компании N, прежде чем присоединиться к компании A. Интервьюер спросил меня о протоколе HTTP, структуре RPC и других знаниях, но я мало что знал. Какой протокол HTTP и какой RPC требуется, мы напрямую используем TCP/IP. На самом деле это тоже проявление неадекватности инфраструктуры компании Т. Они описаны в другой моей статье "Мой опыт работы с Tencent и Ali», вы можете двигаться дальше, если вам интересно.

  Как веб-программист Java, задумывались ли вы когда-нибудь, как веб-сервер (Nginx, Tomcat, Jetty и т. д.) принимает ваши HTTP-запросы? Знаете ли вы, что HTTP на самом деле является текстовым протоколом, основанным на TCP/IP? Причина, по которой я написал эту статью, состоит в том, чтобы надеяться, что веб-программисты на Java имеют некоторое простое представление о некоторых принципах работы серверной части и не будут задавать три вопроса во время интервью.

некоторые концепции

  При написании сетевых программ на стороне сервера мы чаще всего встречаем слова блокирующий, неблокирующий, синхронный и асинхронный. Их объяснения таковы:

  • Блокировка: блокирующий вызов означает, что текущий поток будет приостановлен до возврата вызова и вернется только после того, как вызов получит результат.
  • Неблокирующий: в отличие от блокировки, неблокирующий вызов означает, что функция не блокирует текущий поток до тех пор, пока результат не будет немедленно доступен, а немедленно возвращается.
  • Синхронизация: так называемая синхронизация означает, что при вызове функции вызов не возвращается до тех пор, пока не будет получен результат. Подождите, пока предыдущее не будет сделано, прежде чем вы сможете сделать следующее.
  • Асинхронность: понятие асинхронности связано с синхронизацией. При вызове асинхронной процедуры вызывающая сторона не может получить результат немедленно. Компонент, который фактически обрабатывает вызов, уведомляет вызывающего абонента о состоянии, уведомлении и обратном вызове, когда это делается.

   Часто люди не понимают связи между блокирующим/неблокирующим и синхронным/асинхронным, и их легко спутать. Блокирующий/неблокирующий больше используется для описания атрибутов вызова (например, является ли read(), write() блокирующим/неблокирующим), поэтому область применения относительно узка; в то время как синхронный/асинхронный более высокий уровень, обычно относящийся к каждой функции. Отношения между потоками (например, независимо от того, выполняются ли потоки Thread1 и Thread2 синхронно или асинхронно).

Пять моделей ввода-вывода

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

Эта картина относительно проста, но многие люди должны думать, что каждое сетевое чтение (recvfrom()) или запись (sendto()) является операцией между сетевой картой и пользовательским процессом, прежде чем увидеть эту картину, но это не так. Как видно из приведенного выше рисунка, данные должны проходить через ядро, независимо от того, идут ли они из сетевой карты в пространство пользователя или из пространства пользователя в сетевую карту. То же самое касается чтения и записи данных с диска. Так что есть технология mmap, и те, кто заинтересован, могут самостоятельно использовать Baidu. Прикладной процесс (веб-сервер также относится к прикладному процессу, здесь нам нужно объединить несколько понятий: пользовательский процесс, прикладная программа, веб-серверная программа, все они являются прикладными процессами по отношению к ядру, поэтому в следующей статье они объединены в прикладной процесс. ) необходимо передать системные вызовы (такие как recvfrom/sendto) для чтения и записи данных в ядро, а ядро ​​далее управляет сетевой картой.

  В соответствии с блокирующими и неблокирующими методами обработки системных вызовов приложений, а также синхронными и асинхронными методами обработки операционной системы при обработке запросов приложений см. «Сетевое программирование UNIX, том I», который можно разделить на 5 моделей ввода-вывода:

1. Модель Blocking IO (блокирующий IO)

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

   Преимущества: простое программирование, подходит для обучения. Многие примеры в UNIX Network Programming Volume I основаны на этом шаблоне.    Недостаток: если в сокете нет данных, процесс продолжит блокироваться. В настоящее время данные на других сокетах не могут быть своевременно обработаны. Если это многопоточный метод, поток будет существовать всегда, если только соединение не будет закрыто, а создание, обслуживание и уничтожение потока потребляют ресурсы, поэтому количество соединений, которые можно установить, очень ограничено.

2. Модель неблокирующего ввода-вывода (неблокирующий ввод-вывод)

  Описание: каждый раз, когда процесс приложения вызывает recvfrom, он не будет блокироваться, даже если данные не готовы, и будет продолжать выполняться, избегая недостатка блокировки процесса при соединении.

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

   Недостаток: требуется частый опрос, который потребляет ЦП.При большом параллелизме будет тратиться много времени на опрос соединения без каких-либо данных. Так что эта модель появится только в системах, которые специализируются на предоставлении определенной функции.

3. Модель мультиплексирования ввода-вывода (мультиплексирование ввода-вывода)

  Описание: процесс приложения блокирует системные функции, такие как select/poll/epoll, ожидая, пока соединение станет доступным для чтения (с поступающими данными), а затем вызывает recvfrom для чтения данных из соединения. Хотя этот режим также будет блокироваться при выборе/опросе/опросе, в отличие от блокирующей модели ввода-вывода, он блокирует ожидание событий чтения (записи) для нескольких подключений, что значительно повышает эффективность и увеличивает количество однопоточных/однопроцессных операций. для параллельной обработки нескольких соединений.

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

   Недостаток: когда количество подключений, управляемых select/poll/epoll, слишком мало, эта модель вырождается в модель блокирующего ввода-вывода. И еще один системный вызов: select/poll/epoll и recvfrom.

4. Модель управляемого сигналом ввода-вывода (signal-driven IO)

  Описание: процесс приложения создает обработчик сигналов SIGIO, который может выполнять чтение и запись данных о соединении и бизнес-обработку. И установить этот сигнал в операционную систему, процесс может пойти вниз. Когда данные ядра будут готовы, оно отправит сигнал процессу приложения, запуская выполнение обработчика сигнала. Затем выполните recvfrom и бизнес-обработку в обработчике сигналов.

  Преимущества: Не блокирует

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

5. Модель асинхронного ввода-вывода (асинхронный ввод-вывод)

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

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

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

   Недостатки: сложное программирование, слабая поддержка операционной системы. В настоящее время только iocp под Windows реализует настоящий AIO. Он был представлен только в версии 2.6 под Linux, и в настоящее время он несовершенен, поэтому модель мультиплексирования обычно используется под Linux.

Сравнение различных моделей ввода-вывода

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

Все прототипы приведенных выше изображений взяты из "UNIX Network Programming Volume I", который содержит множество знаний и примеров, связанных с сетевым программированием. Это обязательная книга для программистов. Даже если вы бизнес-программист, вы должны купить раз. , знаете почему!

Суммировать

  Связанные с сетевым программированием классы и интерфейсы JDK не зависят напрямую от операционной системы, в отличие от C++, но его модель ввода-вывода неотделима от пяти вышеупомянутых моделей. В конце концов, это модель, и она не имеет ничего общего с языком или операционной системой. Модель ввода-вывода — это только базовая часть высокопроизводительного сетевого программирования.Хорошей модели ввода-вывода недостаточно, нам также нужна хорошая архитектура (потоковая модель). Модель многопоточности является основной частью высокопроизводительного сетевого программирования и должна быть проанализирована в следующих статьях. Не забудьте обратить внимание на официальный аккаунт, в котором записан путь обучения программиста C++ Java.