введение
После перехода с 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)
Преимущества: простое программирование, подходит для обучения. Многие примеры в UNIX Network Programming Volume I основаны на этом шаблоне. Недостаток: если в сокете нет данных, процесс продолжит блокироваться. В настоящее время данные на других сокетах не могут быть своевременно обработаны. Если это многопоточный метод, поток будет существовать всегда, если только соединение не будет закрыто, а создание, обслуживание и уничтожение потока потребляют ресурсы, поэтому количество соединений, которые можно установить, очень ограничено.
2. Модель неблокирующего ввода-вывода (неблокирующий ввод-вывод)
Преимущества: код относительно прост в написании, процесс не блокируется, и все соединения могут обрабатываться в одном потоке.
Недостаток: требуется частый опрос, который потребляет ЦП.При большом параллелизме будет тратиться много времени на опрос соединения без каких-либо данных. Так что эта модель появится только в системах, которые специализируются на предоставлении определенной функции.
3. Модель мультиплексирования ввода-вывода (мультиплексирование ввода-вывода)
Преимущества: Единое управление соединениями, не обязательно многопоточное, без опроса. Вам нужно только блокировать выбор, и вы можете управлять несколькими подключениями одновременно.
Недостаток: когда количество подключений, управляемых select/poll/epoll, слишком мало, эта модель вырождается в модель блокирующего ввода-вывода. И еще один системный вызов: select/poll/epoll и recvfrom.
4. Модель управляемого сигналом ввода-вывода (signal-driven IO)
Преимущества: Не блокирует
Недостаток: В случае, если предыдущий сигнал уведомления не был обработан, последний сигнал не может быть обработан. Поэтому, когда громкость сигнала велика, следующие сигналы не могут быть восприняты вовремя.
5. Модель асинхронного ввода-вывода (асинхронный ввод-вывод)
Примечание. Все первые 4 модели имеют блокирующие части, некоторые блоки ожидают готовности данных, а некоторые блокируют копирование данных из пространства ядра в пространство пользователя. В этой модели процесс приложения вызывает aio_read до тех пор, пока данные не будут скопированы в пользовательское пространство без какой-либо блокировки, поэтому эта модель называется моделью асинхронного ввода-вывода. У меня есть сомнения по поводу наименования и сопоставления этих пяти моделей, и я чувствую, что читателей легко запутать.
Преимущества: без каких-либо блокировок можно в полной мере использовать системное ядро для распараллеливания операций ввода-вывода с вычислительной логикой.
Недостатки: сложное программирование, слабая поддержка операционной системы. В настоящее время только iocp под Windows реализует настоящий AIO. Он был представлен только в версии 2.6 под Linux, и в настоящее время он несовершенен, поэтому модель мультиплексирования обычно используется под Linux.
Сравнение различных моделей ввода-вывода
Основное различие между первыми четырьмя моделями заключается в первом этапе, поскольку второй этап у них одинаков: процесс блокируется на вызове recvfrom, пока данные копируются из ядра в буфер процесса приложения. Напротив, модель асинхронного ввода-вывода требует обработки на обеих фазах, что отличается от других четырех моделей.
Суммировать
Связанные с сетевым программированием классы и интерфейсы JDK не зависят напрямую от операционной системы, в отличие от C++, но его модель ввода-вывода неотделима от пяти вышеупомянутых моделей. В конце концов, это модель, и она не имеет ничего общего с языком или операционной системой. Модель ввода-вывода — это только базовая часть высокопроизводительного сетевого программирования.Хорошей модели ввода-вывода недостаточно, нам также нужна хорошая архитектура (потоковая модель). Модель многопоточности является основной частью высокопроизводительного сетевого программирования и должна быть проанализирована в следующих статьях. Не забудьте обратить внимание на официальный аккаунт, в котором записан путь обучения программиста C++ Java.