Netty source code analysis (1)-core components and architecture

1. Netty component architecture

Netty frame based event-driven master from Reactor multithreaded implementation shown in work flow process which follows. According to different functions, the components of Netty can be divided into two parts, namelyEvent distribution component and business processing component

Insert picture description here

2. Event Distribution Components

The main event distribution component contains EventLoopGroupand Acceptorcategories, which EventLoopGroupis the event loop group, responsible for the event distribution processing, Acceptorthe processor server process accept the event, will be responsible for registering the main event loop to set the new connection from the event loop group, from To the role of the connection

2.1 Event loop group EventLoopGroup

EventLoopGroup mainManages the life cycle of EventLoop, maintains a set of EventLoop internally, each EventLoop is responsible for processing events on multiple Channels, and one Channel corresponds to only one EventLoop

2.1.1 NioEventLoopGroup instance of event loop group

NioEventLoopGroup The inheritance structure is very deep, and each key attribute is distributed in different parent classes. The more important ones are as follows:

  1. MultithreadEventLoopGroup:
    DEFAULT_EVENT_LOOP_THREADS : Indicates the default number of event loop threads, the default value isNumber of cores available in the machine * 2
  2. MultithreadEventExecutorGroup:
    children: EventExecutor array, which maintains the event loop thread belonging to the current group, which is the NioEventLoop instance object

MultithreadEventExecutorGroupIs the EventLoopGroupmost important thing to realize its main constructor completed the following things:

  1. new ThreadPerTaskExecutor(new DefaultThreadFactory()) Generate an Executor instance and specify its thread factory
  2. Calling newChild()method NioEventLoop new instance of the current group, and specify the parameters for the Executor ThreadPerTaskExecutorobject that is used to create and start a subsequent thread EventLoop
  3. If there is a failure of the new NioEventLoop example, calls each NioEventLoop instance has created shutdownGracefully()ways to start an event loop thread
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
    
    
        if (nThreads <= 0) {
    
    
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }

        if (executor == null) {
    
    
            // #1
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }

        children = new EventExecutor[nThreads];

        for (int i = 0; i < nThreads; i ++) {
    
    
            boolean success = false;
            try {
    
    
                // #2
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
    
    
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
    
    
                if (!success) {
    
    
                    for (int j = 0; j < i; j ++) {
    
    
                        // #3
                        children[j].shutdownGracefully();
                    }

                    for (int j = 0; j < i; j ++) {
    
    
                        EventExecutor e = children[j];
                        try {
    
    
                            while (!e.isTerminated()) {
    
    
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
    
    
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }

        chooser = chooserFactory.newChooser(children);

        final FutureListener<Object> terminationListener = new FutureListener<Object>() {
    
    
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
    
    
                if (terminatedChildren.incrementAndGet() == children.length) {
    
    
                    terminationFuture.setSuccess(null);
                }
            }
        };

        for (EventExecutor e: children) {
    
    
            e.terminationFuture().addListener(terminationListener);
        }

        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }

2.1.2 Event loop instance NioEventLoop

NioEventLoopMaintains a queue of threads and tasks, similar to the single-threaded thread pool that supports asynchronous tasks submission, the main execution I/O 任务and 非 I/O 任务. The execution time of both tasks than the variable ioRatiocontrol, the default is 50,Indicates that the execution time of non-IO tasks is equal to the execution time of IO tasks

  1. I/O 任务
    That selectionKey ready in the event, such as accept, connect, read, write, etc., by a NioEventLoop#processSelectedKeys()trigger method
  2. 非 IO 任务
    Added to the task in the task queue, such as register0, bind0 other tasks, the NioEventLoop#runAllTasks()method for triggering

NioEventLoop The inheritance structure of is also very deep, and its key attributes are as follows:

  1. selector Selector, used to monitor various events on the channel connection registered on it
  2. SingleThreadEventExecutor:
    taskQueue: Queue where tasks are stored
    executor: Thread factories and executors for new threads

NioEventLoop#run()The method is the core logic of event loop processing, and the SingleThreadEventExecutor#execute()method is the entrance to the beginning of the event loop. No further analysis will be done here.

Insert picture description here

2.2 Connect to the Acceptor

Netty is Acceptorin the realization of belonging 业务处理组件, because of its implementation class ServerBootstrapAcceptorinherit ChannelHandler, mainly responsible for the completion of the processing of the Channel Server side

The reason why this component is classified as an event distribution component is because fromFunctional divisionFrom the point of view, it belongs to the connection structure between MainReactor and SubReactor, and is responsible for registering the new connection established by MainReactor receiving client requests to SubReactor

ServerBootstrapAcceptor#channelRead()This is the Acceptormost important method to achieve the logic of the new register connection to SubReactor

       public 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);
            }
        }

Insert picture description here

3. Business processing components

Business processing components mainly include Channel, ChannelHandlerand ChannelHandlerContext. A Channelcontains a ChannelPipelinewhile ChannelPipeline, it also maintains a made ChannelHandlerContextdoubly linked list, each of which ChannelHandlerContextin turn is associated with aChannelHandler

According to ChannelHandlera different implementation of the interface class inheritance, it can be divided ChannelInboundHandler入站处理器and ChannelOutboundHandler出站处理器both read and write operations respectively. 入站事件(read)And 出站事件(write)in ChannelPipelinethe chain processing,If there is no interceptor to terminate the delivery of the event, the inbound event will be passed from the linked list head to the last inbound processor tail, and the outbound event will be passed from the linked list tail to the first outbound processor head. There are two types ChannelHandlers do not interfere with each other

Insert picture description here

3.1 Channel

ChannelIs a pipeline,Is used to connect the byte buffer to the entity at the other endIt can be divided into 服务端 NioServerSocketChanneland 客户端 NioSocketChanneltwo broad categories. In Netty Channelthrough ChannelPipelinethe plurality of the ChannelHandlerprocessor, the final completion of processing the IO data

  • Server NioServerSocketChannel
    class inherits AbstractNioMessageChannelholding a NioMessageUnsafetarget to complete the Chanel IO operations
  • The client NioSocketChannel
    class inherits AbstractNioByteChannelholding a NioByteUnsafetarget to complete the process on Channel events

All inherited AbstractChannelChannel instances will be holding a DefaultChannelPipelineobject for the completion of IO data stream processing, and the other will hold a EventLoopevent loop instance object identifier Channel belongs, it can be used to handle job submission

Insert picture description here

3.2 ChannelHandler

ChannelHandlerIs an interface to process I / O events or intercepting I / O operations, an event may be forwarded to ChannelPipelinethe next service processing chain ChannelHandlerprocessing termination event or delivery

ChannelHandlerMainly divided into 进站处理器ChannelInboundHandlerand 出站处理器ChannelOutboundHandler, respectively read and write operation process. By a user to write the processing logic processor, which will be packaged in the internal Netty AbstractChannelHandlerContextobjects, and then registered to a service processing chain, to complete the logic processing of the data IO

Insert picture description here

3.3 ChannelHandlerContext

ChannelHandlerContextSave Channelrelevant contextual information, which are mainly three kinds of realization DefaultChannelHandlerContext, HeadContextas well as TailContext, for the following key attributes:

  1. AbstractChannelHandlerContext:
    next: The next node of the
    prevcurrent pipeline processor context node: The previous node of the
    inboundcurrent pipeline processor context node: the inbound and outbound identification attribute of the current pipeline processor context node. If true, the node can handle inbound events
    outbound: the current pipeline processor access station identification attributes of the context node to true indicates that the node may process the outbound event
    pipeline: ChannelPipelineexample, implemented as a DefaultChannelPipelineclass
  2. DefaultChannelHandlerContext:
    handler: Encapsulated ChannelHandler handler

ChannelPipelineSubclass DefaultChannelPipelineis generated when a object is instantiated HeadContextand TailContextthe object, and the front and rear pointers to each other, forming a doubly linked list. DefaultChannelPipeline#addLast()The method will add nodes to the doubly linked list. The main processing steps are as follows:

  1. newContext()The ChannelHandlerencapsulated into DefaultChannelHandlerContextobjects
  2. addLast0()The new ChannelHandlerContextobject is added to a doubly linked list
  3. callHandlerAdded0()Callback ChannelHandler#handlerAdded()method so that the relevant logic written is executed when the processor is added to the processing chain
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    
    
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
    
    
            checkMultiplicity(handler);

            newCtx = newContext(group, filterName(name, handler), handler);

            addLast0(newCtx);

            // If the registered is false it means that the channel was not registered on an eventloop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (!registered) {
    
    
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
    
    
                newCtx.setAddPending();
                executor.execute(new Runnable() {
    
    
                    @Override
                    public void run() {
    
    
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }

Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_45505313/article/details/106789671