Основы, связанные с NIO 2

Java Java EE
Основы, связанные с NIO 2

Пожалуйста, указывайте первоисточник при перепечатке, спасибо!

Часть 1Основы, связанные с NIO 1, в основном вводит некоторые основные понятия, буфер (Buffer) и канал (Channel), эта статья продолжает темы, связанные с NIO, в основном блокировки файлов и более важныеSelector, и в будущем у нас будет одна или две статьи, связанные с контентом NIO.

Блокировка файла

Глядя на исходный код RocketMQ, я обнаружил, что есть импорт о блокировках файлов, но настраивается конкретное использование комментариев в коде [оглянитесь назад и посмотрите, почему, поймите, а затем объясните это в определенной статье] (Там есть много способов добиться чего-то, так что это не обязательно один вид), но для целостности знаний я все же расскажу о блокировках файлов, которые могут быть использованы в будущем или в этом месте, или вы можете продолжить чтобы оставить сообщение и обсудить, когда вы используете его там.

Блокировки файлов аналогичны другим блокировкам, которые мы знаем о параллелизме: когда несколько человек работают с файлом одновременно, только первый человек может его редактировать, а остальные либо закрыты (первый человек может работать после завершения операции) или Открыть только для чтения.

В java nio предусмотрена новая функция блокировки файла. Когда поток блокирует файл, другие потоки не могут работать с файлом. Операция блокировки файла выполняется с использованием класса FileLock, и такие объекты должны полагаться на FileChannel для создания экземпляров.

Метод блокировки файла:

  • Общая блокировка: позволяет нескольким потокам читать файлы.
  • Эксклюзивная блокировка: только одному потоку разрешено читать и записывать файлы.

Примечание:Блокировка файлов поддерживается во всей виртуальной машине Java. но онине применяетсяУправляет доступом к файлам несколькими потоками на одной виртуальной машине. Несколько параллельных потоков могут безопасно использовать объекты блокировки файлов.

Файлы Java зависят от FileChannel, в основном, включают следующие четыре метода:

метод инструкция
lock() Получает эксклюзивную блокировку файла этого канала.
lock(long position, long size, boolean shared) Получает блокировку данной области файла для этого канала.
tryLock() throws IOException Предпринята попытка получить монопольную блокировку файла на этом канале.
tryLock(long position, long size, boolean shared) throws IOException Предпринята попытка получить блокировку данной области файла для этого канала.
никто lock() эквивалентно lock(0L, Long.MAX_VALUE, false)
никто tryLock() эквивалентно tryLock(0L, Long.MAX_VALUE, false)

lock()

tryLock()

Разница между lock() и tryLock():

  • lock(), диапазон блокировки может быть увеличен по мере роста файла. Никакой параметр lock() по умолчанию не является эксклюзивной блокировкой, параметр lock(0L, Long.MAX_VALUE, true) является разделяемой блокировкой.
  • tryLock() не блокирует и возвращает null, если блокировка не получена. Никакой параметр tryLock() по умолчанию не является эксклюзивной блокировкой, параметр tryLock(0L, Long.MAX_VALUE, true) является разделяемой блокировкой.

Простой пример кода:

File file = new File("d:" + File.separator + "test.txt") ;
FileOutputStream output = null ;
FileChannel fout = null ;
try {
    output = new FileOutputStream(file,true) ;
    fout = output.getChannel() ;// 得到通道
    FileLock lock = fout.tryLock() ; // 进行独占锁的操作
    if(lock!=null){
	System.out.println(file.getName() + "文件锁定") ;
	Thread.sleep(5000) ;
	lock.release() ;	// 释放
	System.out.println(file.getName() + "文件解除锁定。") ;
    }
} catch (IOException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    if(fout!=null){
	try {
	    fout.close();
	} catch (IOException e) {
	    e.printStackTrace();
	}
    }
    if(output!=null){
	try {
	    output.close();
	} catch (IOException e) {
	    e.printStackTrace();
	}
    }
}

результат операции:

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

Selector

инструкция:

  • FileChannel — это доступный для чтения и записи канал, он должен блокироваться,Нельзя использовать в неблокирующем режиме.
  • SocketChannel отличается от FileChannel: новый Socket Channel можетв неблокирующем режимезапустить и является необязательным. Больше нет необходимости назначать поток для каждого соединения сокета. Используя новые классы NIO, один или несколько потоков могут управлять сотнями или тысячами активных соединений сокетов, используяSelectorОбъекты могут выбирать из доступных каналов сокетов.

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

Примечание. На самом деле основная функция NIO заключается в решении проблемы производительности связи сервера.Часть 1Основы, связанные с NIO 1знания, и этот кусок нужно использовать немедленно.

Некоторые основные методы Selector:

метод инструкция
open() Откройте селектор.
select() Выбирает группу клавиш, соответствующий канал которой готов к операции ввода/вывода.
selectedKeys() Возвращает выбранный набор ключей для этого селектора.

Четыре важные константы SelectionKey:

поле инструкция
OP_ACCEPT Биты набора операций для операций принятия сокета.
OP_CONNECT Биты набора операций для операций подключения к сокету.
OP_READ Биты набора операций для операций чтения.
OP_WRITE Биты набора операций для операций записи.

**Описание:** На самом деле четыре константы — это четыре разных типа событий, которые Selector прослушивает SocketChannel.

Если вас интересует более одного типа событий, вы можете использовать оператор «побитовое ИЛИ» для объединения констант, например: intinterestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

#NIO простой пример кодаКод сервера:

int port = 8000;
// 通过open()方法找到Selector
Selector selector = Selector.open();
// 打开服务器的通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 服务器配置为非阻塞
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
// 进行服务的绑定
serverSocket.bind(address);
// 注册到selector,等待连接
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器运行,端口:" + 8000);

// 数据缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

while (true) {
if ((selector.select()) > 0) { // 选择一组键,并且相应的通道已经准备就绪
	Set<SelectionKey> selectedKeys = selector.selectedKeys();// 取出全部生成的key
	Iterator<SelectionKey> iter = selectedKeys.iterator();
	while (iter.hasNext()) {
		SelectionKey key = iter.next(); // 取出每一个key
		if (key.isAcceptable()) {
			ServerSocketChannel server = (ServerSocketChannel) key.channel();
			// 接收新连接 和BIO写法类是都是accept
			SocketChannel client = server.accept();
			// 配置为非阻塞
			client.configureBlocking(false);
			byteBuffer.clear();
			// 向缓冲区中设置内容
			byteBuffer.put(("当前的时间为:" + new Date()).getBytes());
			byteBuffer.flip();
			// 输出内容
			client.write(byteBuffer);
			client.register(selector, SelectionKey.OP_READ);
		} else if (key.isReadable() && key.isValid()) {
			SocketChannel client = (SocketChannel) key.channel();
			byteBuffer.clear();
			// 读取内容到缓冲区中
			int readSize = client.read(byteBuffer);
			if (readSize > 0) {
				System.out.println("服务器端接受客户端数据:" + new String(byteBuffer.array(), 0, readSize));
				client.register(selector, SelectionKey.OP_WRITE);
			}
		} else if (key.isWritable() && key.isValid()) {
			SocketChannel client = (SocketChannel) key.channel();
			byteBuffer.clear();
			// 向缓冲区中设置内容
			byteBuffer.put(("欢迎关注匠心零度,已经收到您的反馈,会第一时间回复您。感谢支持!!!").getBytes());
			byteBuffer.flip();
			// 输出内容
			client.write(byteBuffer);
			client.register(selector, SelectionKey.OP_READ);
		}
	}
	selectedKeys.clear(); // 清楚全部的key
}
}

Код клиента:

// 打开socket通道
SocketChannel socketChannel = SocketChannel.open();
// 设置为非阻塞方式
socketChannel.configureBlocking(false);
// 通过open()方法找到Selector
Selector selector = Selector.open();
// 注册连接服务端socket动作
socketChannel.register(selector, SelectionKey.OP_CONNECT);
// 连接
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8000));

/* 数据缓冲区 */
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

while (true) {
if ((selector.select()) > 0) { // 选择一组键,并且相应的通道已经准备就绪
	Set<SelectionKey> selectedKeys = selector.selectedKeys();// 取出全部生成的key
	Iterator<SelectionKey> iter = selectedKeys.iterator();
	while (iter.hasNext()) {
		SelectionKey key = iter.next(); // 取出每一个key
		if (key.isConnectable()) {
			SocketChannel client = (SocketChannel) key.channel();
			if (client.isConnectionPending()) {
				client.finishConnect();
				byteBuffer.clear();
				// 向缓冲区中设置内容
				byteBuffer.put(("isConnect,当前的时间为:" + new Date()).getBytes());
				byteBuffer.flip();
				// 输出内容
				client.write(byteBuffer);
			}
			client.register(selector, SelectionKey.OP_READ);
		} else if (key.isReadable() && key.isValid()) {
			SocketChannel client = (SocketChannel) key.channel();
			byteBuffer.clear();
			// 读取内容到缓冲区中
			int readSize = client.read(byteBuffer);
			if (readSize > 0) {
				System.out.println("客户端接受服务器端数据:" + new String(byteBuffer.array(), 0, readSize));
				client.register(selector, SelectionKey.OP_WRITE);
			}
		} else if (key.isWritable() && key.isValid()) {
			SocketChannel client = (SocketChannel) key.channel();
			byteBuffer.clear();
			// 向缓冲区中设置内容
			byteBuffer.put(("nio文章学习很多!").getBytes());
			byteBuffer.flip();
			// 输出内容
			client.write(byteBuffer);
			client.register(selector, SelectionKey.OP_READ);
		}
	}
	selectedKeys.clear(); // 清楚全部的key
}
}

Скриншот результата работы программы:

**Примечание: **Вышеупомянутое просто демонстрация.На самом деле разработка использования nio очень сложна.Вы должны учитывать: механизм проверки валидности ссылки (аутентификация безопасности, полупакетный и липкий пакет и т.д. ), отключение механизма повторного подключения канала (мигание сети и повторное подключение), дизайн надежности (обнаружение пульса, повторная передача сообщений, черный и белый список), соображения масштабируемости и т. д., это очень сложно, и легко сделать ошибки если нехорошо смотри netty Когда я был в авторитетном гайде я вспомнил что нету у автора в то время не было.Часто возникали какие-то необъяснимые проблемы которые нужно было решить и многие проблемы за нас решали netty, так что надо хорошенько присмотреться к нетти (автор смотрит и авторитетный гайд по нетти, Единственный раз мне плохо, кода в нем много, привык пользоваться инструментами для просмотра код (редактор смотрит на цвет кода, можно прыгать и т.д.),Спросите адрес кода авторитетного руководства netty, код в книге особенно искажен!,Благодарность)

Несколько слов об AIO

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

AIO — это сокращение от асинхронного ввода-вывода. По сравнению с NIO это шаг вперед. Он не уведомляет поток о готовности ввода-вывода, но уведомляет поток после завершения операции ввода-вывода. Таким образом, AIO полностью неблокирует. Наше дело Логика похожа на callback-функцию.

**Примечания: **AIO просто для упоминания, NIO еще не разобрался в этом.В последующем будет одна или две статьи, связанные с контентом NIO, в основном говорящие о некотором контенте, таком как select, poll, epoll, нулевая копия и т.д.Если у вас есть более подробная информация о нулевом копировании, оставьте сообщение в области сообщений, чтобы Zero также мог изучить и понять систему, спасибо! ! !

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


Личный публичный аккаунт, добро пожаловать, чтобы обратить внимание и проверить более замечательную историю! ! !

匠心零度公众号