Directorio de artículos
Prefacio
En el análisis del código fuente de Netty (2): el análisis del proceso de inicio del servidorServerSocketChannel
al final del proceso de enlace del puerto de escucha, ya hemos mencionado, después de que se complete la operación de enlace, Netty enviará pipeline.fireChannelActive()
notificaciones de llamadas de tareas asincrónicas. El canal se ha activado, el channelActive()
método del procesador de devolución de llamada. , Y el evento monitoreado por el servidor Channel se actualizará a SelectionKey.OP_ACCEPT
. Por tanto, este artículo divide el establecimiento de una nueva conexión al servidor en dos pasos, el proceso se muestra en la figura
- Configuración del evento de supervisión SelectionKey.OP_ACCEPT
- Registro de la nueva conexión establecida por MainReactor en SubReactor
1. Configuración del evento de supervisión SelectionKey.OP_ACCEPT
-
AbstractUnsafe#bind()
Al finalizar,ServerSocketChannel
vincular el puerto del servidor de escucha, esta vez el canal ya es unActive
estado, enviará una notificación de tareas asíncronas activadas por el canal al hilo del bucle de eventospublic final void bind(final SocketAddress localAddress, final ChannelPromise promise) { assertEventLoop(); ...... boolean wasActive = isActive(); try { doBind(localAddress); } catch (Throwable t) { safeSetFailure(promise, t); closeIfClosed(); return; } if (!wasActive && isActive()) { invokeLater(new Runnable() { @Override public void run() { pipeline.fireChannelActive(); } }); } safeSetSuccess(promise); }
-
Consulte el diagrama de flujo para conocer el proceso de ingresar a la cola y programar la ejecución de tareas asincrónicas, y no se realizará ningún análisis específico aquí. Sabemos que el tratamiento de datos en Netty depende deComponente empresarialPara completar esta tarea se activaron las llamadas asincrónicas al aviso del
DefaultChannelPipeline#fireChannelActive()
método finalpublic final ChannelPipeline fireChannelActive() { AbstractChannelHandlerContext.invokeChannelActive(head); return this; }
-
Finalmente, los pasos anteriores para llamar a
HeadContext#channelActive()
un método, ingresaron al enlace bidireccional del procesador del controlador. Puede ver que hay dos cosas principales que se hacen en este método:- ctx.fireChannelActive () llama al método de notificación para notificar al siguiente procesador de los eventos de activación del canal, por lo que vuelve a llamar al método channelActive () del siguiente procesador
- readIfIsAutoRead () decide si comenzar a leer datos automáticamente de acuerdo con la configuración de lectura automática, el valor predeterminado es lectura automática, llamará al método de lectura del canal
public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelActive(); readIfIsAutoRead(); } private void readIfIsAutoRead() { if (channel.config().isAutoRead()) { channel.read(); } }
-
Channel
Los datos leídos son realmente dependientesDefaultChannelPipeline#read()
y, finalmente, se tomará el nodo de cola de la lista enlazada del procesador bidireccional en la canalización para iniciar la operación de lectura.public final ChannelPipeline read() { tail.read(); return this; }
-
TailContext#read()
Lógica principal delfindContextOutbound()
método a través de un método para encontrar un proceso Contexto de evento de salida desde la cola de la lista vinculada, es decirHeadContext
, las llamadas de paquete dentro delread()
Método del controladorpublic ChannelHandlerContext read() { final AbstractChannelHandlerContext next = findContextOutbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeRead(); } else { Runnable task = next.invokeReadTask; if (task == null) { next.invokeReadTask = task = new Runnable() { @Override public void run() { next.invokeRead(); } }; } executor.execute(task); } return this; }
-
HeadContext#read()
La lógica de métodos rara vez, de hecho, llama alUnsafe#beginRead()
método, el método para lograr esto esAbstractUnsafe#beginRead()
la última llamada alAbstractNioChannel#doBeginRead()
método. Se puede observar que laAbstractNioChannel#doBeginRead()
lógica interna es relativamente concisa, lo principal es modificar el bit de operación del evento de escucha a través de lasAbstractNioChannel
propiedades internas guardadasreadInterestOp
, y el valor de esta propiedad se pone a cuando arranca el servidorSelectionKey.OP_ACCEPT
, es decirA través del paso actual, el servidor comienza a monitorear el nuevo evento de conexión.protected void doBeginRead() throws Exception { // Channel.read() or ChannelHandlerContext.read() was called final SelectionKey selectionKey = this.selectionKey; if (!selectionKey.isValid()) { return; } readPending = true; final int interestOps = selectionKey.interestOps(); if ((interestOps & readInterestOp) == 0) { selectionKey.interestOps(interestOps | readInterestOp); } }
2. Registro de nueva conexión en SubReactor
-
Procesamiento central del evento Eevnt que
NioEventLoop
,NioEventLoop#run()
abrió un espacio de circulación, queNioEventLoop#processSelectedKeys()
maneja todos los eventos relacionados con el Canal, el evento final correspondiente es un método de procesamientoNioEventLoop#processSelectedKey()
para elSelectionKey.OP_ACCEPT
evento será llamadounsafe.read()
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) { final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe(); ...... try { int readyOps = k.readyOps(); // We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise // the NIO JDK channel implementation may throw a NotYetConnectedException. if ((readyOps & SelectionKey.OP_CONNECT) != 0) { // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking // See https://github.com/netty/netty/issues/924 int ops = k.interestOps(); ops &= ~SelectionKey.OP_CONNECT; k.interestOps(ops); unsafe.finishConnect(); } // Process OP_WRITE first as we may be able to write some queued buffers and so free memory. if ((readyOps & SelectionKey.OP_WRITE) != 0) { // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write ch.unsafe().forceFlush(); } // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead // to a spin loop if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) { unsafe.read(); } } catch (CancelledKeyException ignored) { unsafe.close(unsafe.voidPromise()); } }
-
unsafe.read()
Es una llamada de interfaz, y el servidor se implementa comoNioMessageUnsafe#read()
, las cosas más importantes que hace este método son las siguientes:- doReadMessages () que implementa la llamada interfaz NioServerSocketChannel # doReadMessages () Aceptar JDK incorporado conexión de interfaz, y se envasa en condiciones de controlar el evento operación del objeto devuelto,
SelectionKey.OP_READ
NioSocketChannel
A partir de esta nueva conexión se establece - pipeline.fireChannelRead (readBuf.get (i)) toma el NioSocketChannel recién establecido en parámetros, llama al componente de procesamiento empresarial para procesarlo y finalmente llama al procesador integrado del servidor
ServerBootstrapAcceptor#channelRead()
Registre la nueva conexión de MainReactor a SubReactor
public void read() { assert eventLoop().inEventLoop(); ...... try { try { do { int localRead = doReadMessages(readBuf); if (localRead == 0) { break; } if (localRead < 0) { closed = true; break; } allocHandle.incMessagesRead(localRead); } while (allocHandle.continueReading()); } catch (Throwable t) { exception = t; } int size = readBuf.size(); for (int i = 0; i < size; i ++) { readPending = false; pipeline.fireChannelRead(readBuf.get(i)); } readBuf.clear(); allocHandle.readComplete(); pipeline.fireChannelReadComplete(); if (exception != null) { closed = closeOnReadError(exception); pipeline.fireExceptionCaught(exception); } if (closed) { inputShutdown = true; if (isOpen()) { close(voidPromise()); } } } finally { // Check if there is a readPending which was not processed yet. // This could be for two reasons: // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method // // See https://github.com/netty/netty/issues/2254 if (!readPending && !config.isAutoRead()) { removeReadOp(); } } }
- doReadMessages () que implementa la llamada interfaz NioServerSocketChannel # doReadMessages () Aceptar JDK incorporado conexión de interfaz, y se envasa en condiciones de controlar el evento operación del objeto devuelto,
-
El establecimiento de una nueva conexión y los componentes de procesamiento comercial en el proceso de llamar a la nueva conexión se pueden entender de acuerdo con el diagrama de flujo anterior. No lo analizaremos aquí, solo nos enfocaremos en el núcleo
ServerBootstrapAcceptor#channelRead()
. Podemos ver este método utilizado en laServerBootstrap
preservación de las opciones de configuración de SubReactor y laNioEventLoopGroup
instancia del procesador , porchildGroup
referencia al canal recién creado registrado en SubReactorpublic void channelRead(ChannelHandlerContext ctx, Object msg) { final Channel child = (Channel) msg; child.pipeline().addLast(childHandler); setChannelOptions(child, childOptions, logger); for (Entry<AttributeKey<?>, Object> e: childAttrs) { child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue()); } try { childGroup.register(child).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { forceClose(child, future.cause()); } } }); } catch (Throwable t) { forceClose(child, t); } }
-
childGroup.register(child)
Procesos de registro y análisis del código fuente de Netty (2): el proceso de registro del proceso de inicio del servidor mencionó algunos pasos, el proceso incluirá el bucle de eventos SubReactor de creación y lanzamiento de subprocesos, así como la creación y configuración del canal en Pipleline. Cuando se complete el registro, el canal llamará apipeline.fireChannelActive()
la notificación del evento de activación del canal,Luego, en la primera parte de este artículo, se activa la modificación del bit de operación del evento de monitoreo en el canal, pero el bit de operación del evento de monitoreo establecido en este momento esSelectionKey.OP_READ