Эта статья была впервые опубликована вjaychen.cc
Джей Чен
Прежде чем перейти к теме, давайте рассмотрим простой пример сетевого программирования на Java: код очень простой, клиент общается с сервером, и на каждый ввод от клиента сервер отвечает, чтобы получить. Обратите внимание, что сервер может разрешить несколько клиентских подключений одновременно.
Код на стороне сервера:
// 创建服务端 socket
ServerSocket serverSocket = new ServerSocket(20000);
client = serverSocket.accept();
// 客户端连接成功,输出提示
System.out.println("客户端连接成功");
// 启动一个新的线程处理客户端请求
new Thread(new ServerThread(client)).start();
// 子线程中处理客户端的输入
class ServerThread implements Runnable {
.....
@Override
public void run() {
boolean flag = true;
while (flag) {
// 读取客户端发送来的数据
String str = buf.readLine();
// 回复给客户端 get 表示收到数据
out.println("get");
}
}
}
Код клиента:
Socket client = new Socket("127.0.0.1", 20000);
boolean flag = true;
while (flag) {
// 读取用户从键盘的输入
String str = input.readLine();
// 把用户的输入发送给服务端
out.println(str);
// 接受到服务端回传的 get 字符串
String echo = buf.readLine();
System.out.println(echo);
}
}
Учитывая, что полный пример кода Java слишком велик, чтобы повлиять на чтение, он не полностью размещен здесь, Если вам нужно загрузить его непосредственно с github, вотссылка для скачивания.
Видно, что для того, чтобы иметь возможность обрабатывать запросы от нескольких клиентов одновременно, серверу необходимо открывать поток для каждого клиента, а такой режим один поток на клиента очень напряжен для сервера. Предполагая, что есть 1k клиентов и соответствующий сервер должен запускать 1k потоков, память, потребляемая сервером, и время, затрачиваемое на переключение потоков, являются фатальными. Даже если для ограничения количества потоков используется технология пула потоков, эта модель блокирующего ввода-вывода по-прежнему не может поддерживать большое количество соединений.
Каждому клиенту нужен поток для обработки запроса.
NIO
Основная причина, по которой описанный выше режим «один поток на клиента» не может поддерживать большое количество подключений, заключается в том, чтоreadLineвстречаблокировка ввода-вывода, то есть вreadLineКогда данные не могут быть прочитаны, поток будет все время блокироваться, так что поток не может продолжать выполняться, поэтому сервер может одновременно открывать только несколько потоков для одновременной обработки нескольких клиентов.
Поэтому набор интерфейсов NIO был введен после Java 1.4. Одна из наиболее важных функций NIO заключается в том, что он может выполнять неблокирующие операции ввода-вывода:Если данные не могут быть прочитаны, неблокирующий ввод-вывод не блокирует поток, а возвращает 0 напрямую.В этом случае поток определяет, что данные не готовы по возвращаемому значению, и может обрабатывать другие вещи без блокировки.
На картинке выше показана разница между блокирующим вводом-выводом и неблокирующим вводом-выводом.Видно, что хотя неблокирующий ввод-вывод не блокируется, он все равно постоянно вызывает функцию для проверки читаемости данных.В таком виде это явление в Код Показать:
while((str = read()) == 0) {
}
// 继续读取到数据之后的逻辑。
Можно понять, что, хотя неблокирующий ввод-вывод не блокирует поток, поскольку нет данных для чтения, поток не может продолжать выполнять следующую логику и может только непрерывно выносить решение и ждать поступления данных. . В этом случае называетсяСинхронный ввод-вывод. Подводя итог, можно сказать, что NIO — это, по сути,Неблокирующий синхронный ввод-вывод.
мультиплексирование ввода-вывода
Поскольку NIO не будет заблокирован из-за того, что данные еще не поступили, каждому клиенту не нужно выделять поток для непрерывного опроса, чтобы определить, есть ли данные для чтения. Вы можете использовать поток для мониторинга всех клиентских соединений.Этот поток зацикливается, чтобы определить, доступны ли данные клиента для чтения, и если да, он сообщает другим потокам, что клиентское соединение доступно для чтения данных. Такое поведение называетсямультиплексирование ввода-вывода. Предоставляется в НИОSelectorКласс для прослушивания всех клиентских подключений для чтения данных.
использоватьSelectorЧтобы достичь мультиплексирования ввода-вывода, только один поток должен заботиться о том, поступят ли данные, а другие потоки просто ждут уведомления. Таким образом, только прослушивающий поток всегда будет зацикливаться и оценивать, и не будет занимать слишком много ресурсов ЦП. Упоминается в НИОSelector, я должен говорить о мультиплексировании ввода-вывода в Linux-программировании, потому что ввод-вывод в NIOSelectorНижний уровень должен использовать схему мультиплексирования ввода-вывода на системном уровне.
Существует две схемы реализации мультиплексирования ввода-вывода для систем Linux:
selectepoll
В Linux 2.6+ используется базовый NIO:epoll, в версии 2.4.x с помощьюselectфункция.epollфункция с точки зрения производительностиselectГораздо лучше, что здесь вы можете игнорировать специфику программирования под Linux. Стоит отметить, что нижний уровень сетевого фреймворка Java использует технологию NIO.
AIO
Резюме в NIO: вызов с прослушивающим потокомselectФункция для отслеживания поступления данных для всех запросов и уведомления других потоков о чтении данных, если они есть. Здесь, в процессе чтения данных потоком, поток находится в состоянии до того, как данные будут прочитаны.блокироватьсостояние, поток может продолжить выполнение логики только после того, как данные будут прочитаны. Как упоминалось ранее, это называетсяСинхронный ввод-вывод. В JDK 7 добавлен новый набор интерфейсов AIO (асинхронный ввод-вывод).
У AIO есть волшебная особенность: когда инициируется операция ввода-вывода, потоку не нужно ждать, пока она будет прочитана, а можно напрямую вернуться и продолжить выполнение других операций. После того, как данные будут прочитаны, система уведомит поток о том, что данные были прочитаны.Эта операция ввода-вывода, которая инициирует операцию ввода-вывода, но не должна ждать чтения данных, называется асинхронным вводом-выводом.. Если используется AIO, поток может одновременно инициировать несколько операций ввода-вывода, что означает, что поток может обрабатывать несколько запросов одновременно. Известный веб-сервер Nginx использует асинхронный ввод-вывод. Для более подробной информации, вы можете обратиться к моей другой статьеApache — MPM и Nginx, управляемые событиями.
End
До сих пор в статье объяснялась разница между блокирующим/неблокирующим вводом-выводом и синхронным/асинхронным вводом-выводом.Когда дело доходит до моделей ввода-вывода, это неизбежно будет включать 5 моделей ввода-вывода Linux.
- блокировка ввода-вывода
- неблокирующий ввод-вывод
- мультиплексирование ввода-вывода
- Сигнальный ввод-вывод
- Асинхронный ввод-вывод
За исключением того, что ввод-вывод, управляемый сигналом, не упоминается, были объяснены другие четыре основные модели ввода-вывода, и понимание концепций этих моделей ввода-вывода очень полезно для написания кода.