Compartir tecnología

Alfabetización completa en el modelo de red Java.

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Descripción general

Describe los conocimientos básicos de NIO a nivel ava, para una revisión básica.

1. Descripción general de NIO

En Java, NIO (E/S sin bloqueo o Nueva E/S) es un nuevo conjunto de API de operación de entrada/salida introducido en Java SE 1.4 y versiones posteriores.

En comparación con el modelo IO tradicional, proporciona mayor eficiencia y mejores capacidades de procesamiento concurrente. La característica clave de NIO es su función sin bloqueo, que permite que un solo subproceso administre múltiples canales de E/S, mejorando así en gran medida el rendimiento de la aplicación en escenarios de alta concurrencia.

El escenario de uso de Java NIO es particularmente adecuado para situaciones en las que es necesario procesar una gran cantidad de conexiones simultáneas. Por ejemplo, en un servidor de red, un subproceso puede administrar miles de conexiones de clientes sin la necesidad de asignar un subproceso independiente para cada conexión. Puede reducir significativamente el consumo de recursos del sistema y mejorar las capacidades de procesamiento.

2. Tres componentes principales de NIO en Java

1. Canales

El canal es el medio de flujo de datos en Java NIO. Es bidireccional y puede usarse para leer o escribir datos. La principal ventaja de los canales es que no bloquean, lo que significa que un hilo puede administrar múltiples canales. Cuando no ocurre ningún evento en un canal, el hilo no se bloqueará y, en cambio, podrá manejar otras tareas. Los principales tipos de canales incluyen:

  • FileChannel: Se utiliza para operaciones de lectura y escritura de archivos. Se puede utilizar para escribir datos desde el búfer al archivo o para leer datos del archivo al búfer.
  • SocketChannel: Se utiliza para conexiones TCP en comunicaciones de red y se puede utilizar para leer y escribir datos.
  • ServerSocketChannel: Se utiliza para aceptar nuevas conexiones SocketChannel, similar al ServerSocket tradicional.
  • DatagramChannel: Se utiliza para comunicación UDP, que puede enviar y recibir datagramas.

2. Amortiguadores

Buffer es un objeto utilizado para almacenar datos en NIO. Es una matriz de bytes que puede escribir y leer datos. Un buffer tiene una capacidad específica y tiene dos propiedades importantes: posición y límite.

  • Capacidad: El número máximo de elementos que el búfer puede almacenar.
  • Posición: El índice del elemento que se está operando actualmente, que cambia cuando se leen o escriben datos.
  • Límite: En modo lectura, el valor máximo que puede alcanzar la posición; en modo escritura, la posición no puede exceder el valor.

Los principales tipos de amortiguadores sonByteBufferCharBufferShortBufferIntBufferLongBufferFloatBufferyDoubleBuffer, cada tipo corresponde a un tipo de datos primitivo.

3. Selectores

Un selector es un multiplexor en NIO que permite que un solo subproceso monitoree eventos de múltiples canales, como lectura, escritura, conexión y recepción de eventos. El selector notifica a la aplicación cuando uno de los canales está listo para operaciones de E/S. El uso de selectores mejora en gran medida las capacidades de procesamiento concurrente de las aplicaciones de red porque no es necesario crear un hilo para cada conexión.

Los principales métodos de selectores incluyen:

  • select(): Bloquea hasta que al menos un canal esté listo para operaciones de E/S.
  • selectedKeys(): Devuelve un conjunto que contiene los objetos SelectionKey de todos los canales preparados.
  • wakeup(): Interrupción bloqueadaselect()transferir.

SelectionKeyEs la asociación entre selectores y canales. Representa el estado de registro de un canal en el selector, incluidos canales, selectores, colecciones de eventos interesados ​​y colecciones de eventos listos.

3. Programación de red

1. Bloqueo de E/S

Este es el modelo de E/S más tradicional. En Java, las API tradicionales de Socket y ServerSocket se basan en el bloqueo de E/S. En este modo, cuando un subproceso inicia una operación de lectura o escritura, el subproceso se bloquea hasta que se completa la operación de E/S. Si no hay datos para leer para la operación de lectura, o la operación de escritura no se puede completar de inmediato, el hilo esperará hasta que se complete la operación.

Características:

  • Modelo de programación sencillo: Fácil de entender e implementar.
  • Ocupación de recursos: Cada conexión requiere un subproceso independiente, lo que genera un número limitado de subprocesos y un alto consumo de recursos.
  • Limitaciones de rendimiento: En escenarios de alta concurrencia, el cambio de subprocesos y el cambio de contexto son costosos y pueden convertirse fácilmente en cuellos de botella.

2. E/S sin bloqueo

La E/S sin bloqueo es parte del marco Java NIO (Nueva E/S), que permite que los subprocesos inicien operaciones de lectura y escritura sin ser bloqueados. Si no hay datos para leer para una operación de lectura, o la operación de escritura no se puede completar de inmediato, el hilo no se suspenderá y podrá continuar realizando otras tareas.

Características:

  • flexibilidad: Los subprocesos pueden manejar más conexiones porque no están bloqueados esperando operaciones de E/S.
  • Mayor complejidad: El programador debe verificar explícitamente si la operación se completa, lo que aumenta la dificultad de la programación.
  • Mejoras de rendimiento: En escenarios de alta concurrencia, la sobrecarga causada por el cambio de subprocesos se puede reducir significativamente.

3. Multiplexación

La multiplexación consiste en monitorear varios descriptores de archivos al mismo tiempo a través de un hilo y solo operar en un descriptor cuando esté listo (generalmente significa que los datos son legibles o que se puede escribir en el búfer de escritura). Los selectores se utilizan en Java para implementar la multiplexación.

Características:

  • Alta concurrencia: Un hilo puede manejar miles de conexiones, lo que mejora enormemente el rendimiento del servidor.
  • Eficiente: Solo cuando una operación de E/S esté lista, el subproceso se activará para evitar cambios de subproceso innecesarios.
  • Ahorro de recursos: En comparación con el bloqueo de E/S, los servidores bajo el modelo de multiplexación pueden utilizar los recursos del sistema de manera más eficiente.

Aviso:

En aplicaciones prácticas, las E/S sin bloqueo se utilizan a menudo junto con la multiplexación. Por ejemplo, un servidor puede usar un Selector para monitorear múltiples SocketChannels. Cuando un SocketChannel tiene datos que se pueden leer o escribir, el Selector notificará al servidor y el servidor procesará este SocketChannel específico sin bloqueo.

4.NIO VS BIO

4.1 Transmisión vs canal

En Java, Stream y Channel son dos formas diferentes de procesar flujos de datos. Pertenecen a la biblioteca IO estándar y a la biblioteca NIO de Java, respectivamente. Aquí hay una comparación detallada de los dos conceptos:

Arroyo

Stream es parte del modelo IO (IO de bloqueo) estándar de Java, que proporciona una forma de leer y escribir datos secuencialmente. Stream se divide en dos tipos: InputStream y OutputStream, que se utilizan para leer y escribir datos respectivamente.Estos flujos pueden ser flujos de bytes (comoInputStream, OutputStream) o una secuencia de caracteres (comoReader, Writer)。

Características:

  • Obstructivo: De forma predeterminada, las operaciones de Stream están bloqueando, es decir, si no hay datos para leer para la operación de lectura, o la operación de escritura no se puede completar de inmediato, el hilo de llamada se bloqueará hasta que se complete la operación.
  • Unidireccionalidad: Cada Stream tiene una dirección, ya sea de solo lectura o de solo escritura.
  • apagado automático: Después de usar Stream, generalmente necesitas llamarloclose() método para liberar recursos.A partir de Java 7, la declaración try-with-resources puede cerrar automáticamente la implementaciónAutoCloseableRecursos de interfaz, incluido Stream.

Canal

Channel es parte del modelo Java NIO (New IO), que proporciona un mayor nivel de abstracción que Stream, lo que permite un procesamiento de datos más eficiente. Los canales pueden ser bidireccionales, lo que significa que pueden usarse para leer y escribir datos.Los principales tipos de canales incluyenFileChannelSocketChannelServerSocketChannelyDatagramChannel

Características:

  • sin bloqueo :El canal se puede configurar en modo de bloqueo o sin bloqueo. En el modo sin bloqueo, si una operación de lectura no tiene datos para leer o una operación de escritura no se puede completar de inmediato, la operación regresará inmediatamente en lugar de bloquear el hilo.
  • Operaciones de buffer:Las operaciones del canal siempre se realizan a través del búfer (Buffer). Los datos se leen del canal al búfer o se escriben desde el búfer al canal.
  • Multiplexación: El canal se puede utilizar junto con el Selector para lograr la multiplexación, lo que permite que un subproceso supervise las operaciones de E/S de varios canales y mejore las capacidades de procesamiento concurrente.

Resumir

  • Escena aplicable: Stream es más adecuado para procesar conjuntos de datos más pequeños u operaciones de archivos simples, mientras que Channel es más adecuado para procesar grandes cantidades de datos, especialmente comunicaciones de red y operaciones de archivos grandes, porque proporciona un mayor rendimiento y mejores capacidades de concurrencia.
  • Complejidad de programación: La API de Stream es relativamente simple y fácil de usar, mientras que la API de Channel y NIO son más complejas, pero proporcionan funciones más potentes y mayor eficiencia.
  • Administracion de recursos: Ya sea una transmisión o un canal, los recursos deben administrarse adecuadamente y asegurarse de que estén cerrados cuando ya no sean necesarios para evitar fugas de recursos.

4.2. modelo IO

En Java, los modelos de operación de E/S se pueden clasificar según las dos dimensiones de síncrono/asincrónico y de bloqueo/no bloqueo.

4.2.1 E/S de bloqueo síncrono

definición : El bloqueo de E/S sincrónico es el modelo de E/S más tradicional. Cuando un subproceso llama a una operación de E/S (como lectura o escritura), el subproceso se bloqueará hasta que se complete la operación. Esto significa que el hilo no puede realizar ninguna otra tarea hasta que se complete la operación.

Características

  • Simple y fácil de usar, la lógica del código es clara.
  • Cada operación de E/S requiere un subproceso exclusivo, lo que no es adecuado para escenarios de alta concurrencia y puede provocar el agotamiento de los recursos del subproceso.
  • Comúnmente utilizado enInputStreamOutputStreamSocketyServerSocketescena.

Ejemplo:

Utilice tradicionalInputStreamyOutputStreamPara leer y escribir archivos:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class SyncBlockingIOExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("input.txt");
             FileOutputStream fos = new FileOutputStream("output.txt")) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4.2.2 E/S síncronas sin bloqueo

definición : En E/S sincrónicas sin bloqueo, el subproceso no se bloqueará después de llamar a la operación de E/S. Si la operación no se puede completar de inmediato, el método regresará inmediatamente, generalmente devolviendo un valor especial (como -1 o 0) o lanzando una excepción para indicar que la operación no se completó.

Características

  • Es necesario sondear el estado de la operación hasta que se complete la operación.
  • Mayor utilización de subprocesos porque los subprocesos pueden realizar otras tareas mientras esperan E/S.
  • La implementación es más compleja y necesita manejar sondeos y verificaciones de estado.
  • Basado en Java NIOChannelsyBuffers, llamandoconfigureBlocking(false)Configure el canal en modo sin bloqueo.

Ejemplo:

Usando NIOFileChannelPara lectura y escritura sin bloqueo:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;

public class SyncNonBlockingIOExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("input.txt");
             FileOutputStream fos = new FileOutputStream("output.txt");
             FileChannel inChannel = fis.getChannel();
             FileChannel outChannel = fos.getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            inChannel.configureBlocking(false);
            while (inChannel.read(buffer) > 0) {
                buffer.flip();
                outChannel.write(buffer);
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

4.2.3 E/S de multiplexación síncrona

definición: Uso del modelo de E/S multiplexadas síncronasSelector(selector) para monitorear múltiplesChannel evento.Cuando el hilo llamaSelector.select(), se bloqueará hasta que haya al menos unoChannelSe producen eventos (como legibles, escribibles, solicitudes de conexión, etc.).

Características

  • Permite que un hilo administre múltiplesChannel, mejora la concurrencia.
  • aprobarSelectorySelectionKeyMecanismo que puede manejar eficientemente una gran cantidad de conexiones simultáneas.
  • Adecuado para escenarios de servidores de red, como servidores web y servidores de chat.
  • Basado en el marco Java NIO.

Ejemplo:

Usando NIOFileChannelPara lectura y escritura sin bloqueo:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;

public class SyncMultiplexingIOExample {
    public static void main(String[] args) throws IOException {
        try (Selector selector = Selector.open();
             ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                if (selector.select() > 0) {
                    Set<SelectionKey> keys = selector.selectedKeys();
                    for (SelectionKey key : keys) {
                        if (key.isAcceptable()) {
                            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                            SocketChannel clientChannel = ssc.accept();
                            clientChannel.configureBlocking(false);
                            clientChannel.register(selector, SelectionKey.OP_READ);
                        }
                    }
                    keys.clear();
                }
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

4.2.4 E/S asíncronas sin bloqueo

definición : En el modelo de E/S asíncrono sin bloqueo, el subproceso regresa inmediatamente después de iniciar la operación de E/S sin esperar a que se complete la operación. Cuando se complete la operación, el hilo será notificado de forma asincrónica a través de una función de devolución de llamada o notificación de evento.

Características

  • El modelo de E/S más eficiente: los subprocesos no se bloquean en absoluto y pueden realizar otras tareas inmediatamente.
  • aprobarAsynchronousChannelInterfaz y su implementación de subclase, comoAsynchronousFileChannelyAsynchronousSocketChannel
  • Adecuado para escenarios de alta concurrencia y alto rendimiento, como procesamiento de big data y servidores de red de alta carga.
  • Java 7 introdujo el modelo de E/S asíncrono sin bloqueo.

Ejemplo:

Usando NIOAsynchronousFileChannelRealice lectura y escritura de archivos asincrónica:

import java.io.IOException;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;

public class AsyncNonBlockingIOExample {
    public static void main(String[] args) throws IOException, InterruptedException {
        AsynchronousFileChannel inChannel = AsynchronousFileChannel.open(Paths.get("input.txt"), StandardOpenOption.READ);
        AsynchronousFileChannel outChannel = AsynchronousFileChannel.open(Paths.get("output.txt"), StandardOpenOption.WRITE);
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        CountDownLatch latch = new CountDownLatch(1);
        inChannel.read(buffer, 0, buffer, 0, (channel, result) -> {
            buffer.flip();
            outChannel.write(buffer, 0, buffer, 0, null);
            latch.countDown();
        });
        latch.await();
        inChannel.close();
        outChannel.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

Nota: El modelo asíncrono requiere soporte del sistema operativo subyacente (Kernel)

  • Los sistemas Windows implementan IO asíncrono verdadero a través de IOCP
  • La IO asíncrona en el sistema Linux se introdujo en la versión 2.6, pero su implementación subyacente todavía usa multiplexación para simular IO asíncrona y no hay ninguna ventaja de rendimiento.

4.2.5 E/S de bloqueo asíncrono

definición : En teoría, este modelo no existe, porque "asincrónico" significa que la operación se realiza en segundo plano y el hilo no está bloqueado. Por lo tanto, el bloqueo de E/S asíncrono es un concepto contradictorio y no aparecerá en la práctica.

4.2.6 Resumen

Elegir el modelo de E/S adecuado es fundamental para el rendimiento y la gestión de recursos. En escenarios de alta concurrencia, los modelos de E/S síncronos sin bloqueo o multiplexados síncronos son opciones comunes. En escenarios que requieren rendimiento y velocidad de respuesta extremos, el modelo de E/S asíncrono sin bloqueo es la primera opción.

4.3 Copia cero

Concepto de copia cero

​ La tecnología de copia cero significa que durante el proceso de transmisión de datos de un lugar a otro, no es necesario copiar los datos entre el espacio del usuario y el espacio del kernel, o al menos reduce el número de dichas copias, mejorando así la eficiencia del sistema. En las operaciones de E/S tradicionales, cuando se leen datos de la red o del disco, primero se copian al búfer en el espacio del kernel y luego se copian del espacio del kernel al búfer en el espacio del usuario. Por el contrario, en copia cero, los datos pueden procesarse directamente en el espacio del kernel o transferirse directamente desde el espacio del kernel al dispositivo de red, lo que reduce la operación de copia de la CPU, reduce la sobrecarga del sistema y mejora la eficiencia de la transmisión de datos.

Fuente de copia cero

El concepto de copia cero apareció por primera vez en el diseño de sistemas operativos, con el objetivo de resolver el cuello de botella en el rendimiento causado por la copia de datos en las operaciones de E/S tradicionales. En los primeros sistemas informáticos, todas las operaciones de E/S requerían múltiples copias de datos entre el espacio del usuario y el espacio del kernel. Esto gradualmente se convirtió en un cuello de botella en el rendimiento después de que las redes de alta velocidad y los discos de gran capacidad se volvieron comunes.

Puntos técnicos clave

  • E/S directa: permite que las aplicaciones accedan a los dispositivos de disco directamente, omitiendo el mecanismo de almacenamiento en caché del sistema de archivos.
  • Mapeo de memoria (MMAP): asigne archivos a la memoria para que se pueda acceder a los datos del archivo como si fueran memoria, lo que reduce la copia.
  • Enviar archivo: Llamada al sistema Linux que puede transferir datos directamente de un descriptor de archivo a otro, evitando copias intermedias.
  • DMA (Acceso directo a memoria): Tecnología a nivel de hardware que permite transferir datos directamente entre el dispositivo y la memoria sin la participación de la CPU.

Implementación en Java:

Java admite la tecnología de copia cero a través del marco NIO (New I/O). NIO presentadoFileChannelySocketChannel y otras clases, que proporcionan operaciones de E/S más eficientes. Específicamente,FileChannel.transferTo()yFileChannel.transferFrom()Los métodos pueden transferir datos directamente desde un canal de archivo a un canal de socket o viceversa sin cargar los datos en un búfer, logrando así una copia cero.

Por ejemplo, suponga que necesita enviar el contenido de un archivo grande a la red. El enfoque tradicional es leer primero el contenido del archivo en un búfer y luego escribir el contenido del búfer en la red. Esto implica dos operaciones de copia: una del disco al búfer y otra del búfer a la red.durante el usotransferTo()Cuando se utiliza este método, los datos se pueden transferir directamente desde el disco a la red sin la necesidad de un búfer intermedio, lo que reduce la cantidad de copias y logra una copia cero.

Un ejemplo usando ByteBuffer:

ByteBuffery otraBufferclase (comoCharBufferShortBufferetc.) proporcionan buffers que se pueden llenar o vaciar sin involucrar directamente una copia entre el espacio del usuario y el espacio del kernel.ByteBufferSe puede utilizar directa o indirectamente en tecnología de copia cero:

  1. Tampón directoByteBuffer.allocateDirect(size)CreadoByteBuffer Las instancias se asignan directamente a la memoria física, sin pasar por el montón de Java.Cuando se compara dicho buffer conFileChanneloSocketChannel Cuando se usan juntos, los datos se pueden transferir directamente entre la memoria física y los dispositivos de hardware sin necesidad de copias adicionales a través del montón de Java. Esto permite una verdadera copia cero en algunas plataformas.
  2. usarFileChanneldetransferTo()ytransferFrom(): Estos métodos permiten que los datos se almacenen directamente enFileChannelySocketChanneltransmitido entreByteBuffer como intermediario. Esto significa que los datos se pueden transferir directamente del disco a la red o viceversa sin tener que copiarlos entre el espacio del usuario y el espacio del kernel.
  3. usarByteBufferdewrap()métodowrap()El método le permite envolver una matriz de bytes existente u otro búfer en unByteBuffer , por lo que no es necesario copiar los datos a un nuevo búfer. Esto es útil para evitar copias innecesarias de datos.
  4. usarByteBufferdeslice()métodoslice() El método crea una vista del búfer sin copiar los datos subyacentes.Esto significa que una granByteBufferDividir en varios buffers más pequeños sin copiar datos.

Combine ByteBuffer con FileChannel:

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class ZeroCopyExample {
    public static void main(String[] args) {
        Path path = Path.of("example.txt");
        try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
            ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
            long transferred = fileChannel.transferTo(0, fileChannel.size(), System.out);
            System.out.println("Transferred bytes: " + transferred);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Aviso:

Debemos tener en cuenta que, de hecho, no existe una copia cero completa. La copia cero mencionada aquí es solo para nuestra aplicación y no tiene una copia a nivel de usuario. Pero incluso a nivel de usuario, es imposible no realizar ninguna operación de copia, sino reducir las copias tanto como sea posible. Por lo tanto, podemos entender que el término copia cero en realidad se refiere a la tecnología de reducir el número de copias de datos. y eso no significa que no exista una operación de copia real.