netty源码浅析-服务端启动流程

SpringBoot搭建Netty+Socket+Tcp服务端和客户端 这里为了测试,写了Main方法,可以参考服务端,配置启动类 ,实现跟随项目启动。有测试的,,,但是忘记截图了......................想写就参考服务端...... 阅读详情

服务端启动流程

我们以下面的demo作为本次netty源码分析的入口

public class Server {
    public static void main(String[] args) throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            //p.addLast(new LoggingHandler(LogLevel.INFO));
                            p.addLast(new EchoServerHandler());
                        }
                    });

            // Start the server.
            ChannelFuture f = b.bind(10001).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

我们先来看下首先创建的两个NioEventLoopGroup,这两个其实就是netty的线程池。服务端分为两个线程组,一个是boss线程组,主要负责客户端接入,另一个work线程组主要是为了处理客户端的读写请求。我们先来分析一下NioEventLoop的创建

public NioEventLoopGroup() {
	     this(0);
	}
public NioEventLoopGroup(int nThreads) {
	   	this(nThreads, (Executor) null);
	}
public NioEventLoopGroup(int nThreads, Executor executor) {
        //线程数        null      nio底层provider
        this(nThreads, executor, SelectorProvider.provider());
    }
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
                             final SelectStrategyFactory selectStrategyFactory) {
        //线程数        null      nio底层provider  默认选择策略            拒绝策略
        super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
    }
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
        //判断传入的线程数如果为0则nThreads=core * 2                 executor=null
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        //校验
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }
        //默认为null,重新创建executor
        if (executor == null) {
            //1.newDefaultThreadFactory默认创建FastThreadLocalThread的线程工厂
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
        //创建EventExecutor线程组,大小为core*2,用来存放NioEventLoop
        children = new EventExecutor[nThreads];

        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                //初始化每个事件线程
                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 ++) {
                        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);
    }

首先调用NioEventLoopGroup的构造函数,然后逐级调用重载的构造函数,其中如果创建NioEventLoopGroup时传入的线程数为0,则默认线程数为core*2,然后调用父类MultithreadEventExecutorGroup的构造函数,首先会判断executor是不是null,如果是就创建一个ThreadPeerTaskExecutor,这里传入了一个newDefaultThreadFactory,这是一个创建线程的线程工厂,我们跟进到它的构造函数中

 public DefaultThreadFactory(String poolName, boolean daemon, int priority, ThreadGroup threadGroup) {
        if (poolName == null) {
            throw new NullPointerException("poolName");
        }
        if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
            throw new IllegalArgumentException(
                    "priority: " + priority + " (expected: Thread.MIN_PRIORITY <= priority <= Thread.MAX_PRIORITY)");
        }
        //线程前缀
        prefix = poolName + '-' + poolId.incrementAndGet() + '-';
        //是否为daemon线程
        this.daemon = daemon;
        //线程级别
        this.priority = priority;
        this.threadGroup = threadGroup;
    }

根据线程池的类型创建线程池的名称和线程的优先级,默认优先级为5,这里的线程池名称就是启动的时候我们会看到的以NioEventLoopGroup开头的线程池,比如:NioEventLoopGroup-2-1,这里的2就是poolId,而1就是线程的id,我们创建线程就是依赖于这个线程池工厂。继续回到eventLoop的构造函数中,这里创建了一个nThreads个数的EventExecutor数组,然后循环初始化每个事件线程,我们跟着到newChild方法

 //初始化eventExecutor
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        //this指向NioEventLoopGroup
        return new NioEventLoop(this, executor, (SelectorProvider) args[0],
            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }

这里直接创建了NioEventLoop,我们进入它的构造函数中

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
        //this指向NioEventLoopGroup DEFAULT_MAX_PENDING_TASKS默认可以接收的最大长度个数为16
        super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
       	//校验代码已省略
        provider = selectorProvider;
        //根据是否优化nio的selectedKeys创建selectorTuple
        final SelectorTuple selectorTuple = openSelector();
        selector = selectorTuple.selector;
        unwrappedSelector = selectorTuple.unwrappedSelector;
        selectStrategy = strategy;
    }

首先会调用父类的构造方法,赋值parent属性,parent属性就是我们创建NioEventLoop时传入的NioEventLoopGroup

protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                        boolean addTaskWakesUp, int maxPendingTasks,
                                        RejectedExecutionHandler rejectedHandler) {
        super(parent);//赋值所属的NioEventLoopGroup
        this.addTaskWakesUp = addTaskWakesUp;//false
        this.maxPendingTasks = Math.max(16, maxPendingTasks);//16
        this.executor = ObjectUtil.checkNotNull(executor, "executor");
        taskQueue = newTaskQueue(this.maxPendingTasks);//LinkedBlockingQueue(16)
        //拒绝策略
        rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
    }

进入到SingleThreadEventExecutor的构造函数,这里赋值最大接收消息条数为16次,并且创建了一个长度为16的阻塞队列
继续回到NioEventLoop的构造函数,这里的openSelector()方法会创建nio底层的selector对象,熟悉nio的同学应该知道这个就是多路复用的选择器,我们进入openSelector方法

private SelectorTuple openSelector() {
        final Selector unwrappedSelector;
        try {
            //创建nio的选择器
            unwrappedSelector = provider.openSelector();
        } catch (IOException e) {
            throw new ChannelException("failed to open a new selector", e);
        }
        //如果没有开启优化,则直接返回原生的Selector
        if (DISABLE_KEYSET_OPTIMIZATION) {
            return new SelectorTuple(unwrappedSelector);
        }
        //创建netty优化的替代对象
        final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
        //通过授权操作,反射获取sun.nio.ch.SelectorImpl
        Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                try {
                    return Class.forName(
                            "sun.nio.ch.SelectorImpl",
                            false,
                            PlatformDependent.getSystemClassLoader());
                } catch (Throwable cause) {
                    return cause;
                }
            }
        });
        //如果获取失败了,返回nio原生的selector
        if (!(maybeSelectorImplClass instanceof Class) ||
                // ensure the current selector implementation is what we can instrument.
                !((Class<?>) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
            //反射获取发生异常
            if (maybeSelectorImplClass instanceof Throwable) {
                Throwable t = (Throwable) maybeSelectorImplClass;
                logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
            }
            //不优化返回原生的selector
            return new SelectorTuple(unwrappedSelector);
        }

        final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;
        //通过授权,反射将selectedKeys和publicSelectedKeys属性替换为netty优化后的SelectedSelectionKeySet
        Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                try {
                    Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                    Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

                    Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField);
                    if (cause != null) {
                        return cause;
                    }
                    cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField);
                    if (cause != null) {
                        return cause;
                    }

                    selectedKeysField.set(unwrappedSelector, selectedKeySet);
                    publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
                    return null;
                } catch (NoSuchFieldException e) {
                    return e;
                } catch (IllegalAccessException e) {
                    return e;
                }
            }
        });

        if (maybeException instanceof Exception) {
            selectedKeys = null;
            Exception e = (Exception) maybeException;
            logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, e);
            return new SelectorTuple(unwrappedSelector);
        }
        selectedKeys = selectedKeySet;
        logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
        return new SelectorTuple(unwrappedSelector,
                                 //netty优化重写的selector,继承了Selector
                                 new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
    }

这个方法比较长,其实主要目的就是判断需不需要对selectImpl类中的selectedKeys结构做优化,如果需要优化就会用SelectedSelectionKeySet替换selectedKeys结构,SelectedSelectionKeySet中的属性

SelectionKey[] keys;

selectImpl中的属性

protected Set<SelectionKey> selectedKeys = new HashSet();
protected HashSet<SelectionKey> keys = new HashSet();
private Set<SelectionKey> publicKeys;
private Set<SelectionKey> publicSelectedKeys;
var foo = 'bar';

其实可以看到就是将hashSet结构变成数组结构,数组根据索引遍历的速度会更快,这里是netty对jdk底层nio的优化。每一个NioEventLoop实例中都会有selector
我们回到MultithreadEventExecutorGroup的构造函数中创建线程选择器

//创建线程选择器
chooser = chooserFactory.newChooser(children);
public EventExecutorChooser newChooser(EventExecutor[] executors) {
        //根据线程数是不是2的幂选择不同的线程选择器
        if (isPowerOfTwo(executors.length)) {
            return new PowerOfTwoEventExecutorChooser(executors);
        } else {
            return new GenericEventExecutorChooser(executors);
        }
    }

判断传入的线程数是不是2的幂数,创建不同的线程选择器,如果是则返回PowerOfTwoEventExecutorChooser,如果不是就创建一个GenericEventExecutorChooser,线程选择器主要的方法就是next方法

public EventExecutor next() {
		   //与运算速度更快
		  	return executors[idx.getAndIncrement() & executors.length - 1];
        }
public EventExecutor next() {
            //求余
            return executors[Math.abs(idx.getAndIncrement() % executors.length)];
        }

经过对比我们可以发现,如果是2的幂,就通过与的方式获取余数值速度更快,如果不是就按照求余的方式获取下一个线程,这里可以看到获取线程是一个循环的过程
服务端的启动时通过ServerBootStrap引导类完成的,首先引导类绑定了两个eventLoopGroup,bossGroup主要负责客户端的接入,workGroup主要负责客户端的数据读写,然后通过channel方法绑定了处理的方式,如案例中就是nio模式,服务端绑定的就是NioServerSocketChannel,然后通过childHandler绑定一个channel初始化器,初始化器主要是为了完成channelHandler的添加,启动类的入口方法是bind方法,我们通过该方法分析服务端的启动流程,bind方法一步步执行会走到io.netty.bootstrap.AbstractBootstrap#doBind方法

//服务端绑定操作
    private ChannelFuture doBind(final SocketAddress localAddress) {
        //1.初始化channel并完成注册
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        //2.如果发生了错误返回promise
        if (regFuture.cause() != null) {
            return regFuture;
        }
        //3.注册完成,执行bind方法
        if (regFuture.isDone()) {
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            //4.如果注册还没有完成,添加一个回调,等注册完成了再执行operationComplete中的方法
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

这个方法比较长,我们分开来分析,首先分析initAndRegister方法

final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            //1.通过反射创建对象,channel是socket的抽象
            //NioServerSocketChannel
            channel = channelFactory.newChannel();
            //2.创建完成后初始化
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                channel.unsafe().closeForcibly();
                // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        //3.调用boss线程池的注册方法
        ChannelFuture regFuture = config().group().register(channel);
        //4.如果注册失败了关闭channel
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

首先通过反射创建了我们前面通过启动类的channel绑定的channel

public T newChannel() {
        try {
            //反射创建对象
            return clazz.getConstructor().newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + clazz, t);
        }
    }

这里可以看到通过反射拿到无参构造函数然后调用,我们跟踪NioServerSocketChannel的构造函数中

public NioServerSocketChannel() {
        //创建nio底层的ServerSocketChannel
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }
public NioServerSocketChannel(ServerSocketChannel channel) {
        //1.调用父类构造方法
        super(null, channel, SelectionKey.OP_ACCEPT);
        //2.创建配置类
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

首先调用父类的构造函数,传入上面创建的nio的ServerSocketChannel和关注的连接事件OP_ACCEPT

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;//nio的ServerSocketChannel
        this.readInterestOp = readInterestOp;//连接事件
        try {
            ch.configureBlocking(false);//设置非阻塞模式
        } catch (IOException e) {
            try {
                ch.close();//发生异常就关闭channel
            } catch (IOException e2) {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            "Failed to close a partially initialized socket.", e2);
                }
            }

            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
protected AbstractChannel(Channel parent) {
        this.parent = parent;//null
        //1.channel唯一标识id
        id = newId();
        //2.unsafe是channel的内部类,封装了很多底层nio的操作
        unsafe = newUnsafe();
        //3.每创建一个channel就会创建一个channelPipe用于组织handler
        pipeline = newChannelPipeline();
    }

这里逐级调用父类的构造函数,最终走到AbstractChannel中,我们可以看到为每一个channel创建了一个唯一id,然后创建了一个unsafe对象,Unsafe是AbstractChannel的内部接口,里面封装了大量的nio底层操作。然后创建了一个pipeline对象,每一个channel对应一个pipeline,这是一个流水线,用来组织channelHandler,我们继续跟踪发现返回的是一个DefaultChannelPipeline对象,我们跟踪到这个类的构造方法中

  protected DefaultChannelPipeline(Channel channel) {
        //1.pipeline和channel关联起来
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        //2.关联succeededFuture
        succeededFuture = new SucceededChannelFuture(channel, null);
        //通道连接情况
        voidPromise =  new VoidChannelPromise(channel, true);
        //4.双向链表,pipeline中组织的就是ChannelHandlerContext
        tail = new TailContext(this);
        head = new HeadContext(this);

        head.next = tail;
        tail.prev = head;
    }

这里可以看到首先将channel和pipeline关联起来,然后创建了succeededFuture和voidPromise实例,然后创建了一个TailContext对象和HeadContext对象,熟悉链表的同学应该能看出来,这里面将这个两个ChannelHandlerContext连接起来形成了双向链表,这里我们只需要知道channelPipeline是为了组织channelHandler的就行,后续在做分析。回到AbstractNioChannel构造函数,首先对ch属性进行赋值,传入的ch就是前面通过provider创建的ServerSocketChannel,从这点我们可以看到每个NioServerSocketChannel内部都会持有一个nio底层的ServerSocketChannel,我们先记住这一点对我们理解会有帮助,然后赋值acceprt事件,并且将ServerSocketChannel设置为非阻塞模式,这个就是我们熟悉的nio代码。我们继续跟踪代码,回到NioServerSocketChannel构造函数,上面完成了父构造函数的调用,现在我们看看NioServerSocketChannelConfig的创建

public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {
        super(channel);
        if (javaSocket == null) {
            throw new NullPointerException("javaSocket");
        }
        this.javaSocket = javaSocket;
    }
public DefaultChannelConfig(Channel channel) {
        //byteBuf分配器
        this(channel, new AdaptiveRecvByteBufAllocator());
    }

跟踪到DefaultServerSocketChannelConfig的构造函数,我们可以看到这里创建了一个ByteBuf的分配器AdaptiveRecvByteBufAllocator里面维护了一个数组,数组内存放了需要分配的ByteBuf的大小,这里我们先知道AdaptiveRecvByteBufAllocator是用于分配ByteBuf的。Bytebuf可以理解就是对nio原始的ByteBuffer的封装,后续我们在详细介绍。这里我们可以思考一下为什么通过反射创建NioServerSocketChannel?主要是为了扩展性,可以创建不同的channel完成模式的切换。到这里NioServerSocketChannel已经创建完成,我们继续回到initAndRegister方法。

 final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            //1.通过反射创建对象,channel是socket的抽象
            //NioServerSocketChannel
            channel = channelFactory.newChannel();
            //2.创建完成后初始化options和
            init(channel);
        } catch (Throwable t) {
            if (channel != null) {
                channel.unsafe().closeForcibly();
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        //3.调用boss线程池的注册方法
        ChannelFuture regFuture = config().group().register(channel);
        //4.如果注册失败了关闭channel
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

NioServerSocketChannel创建完成后对其进行初始化,这里我们分析的是服务端,所以进入服务端引导类的init方法

void init(Channel channel) throws Exception {
        //设置配置和属性
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
        //获取pipeline
        ChannelPipeline p = channel.pipeline();
        //work线程池,处理读写事件
        final EventLoopGroup currentChildGroup = childGroup;
        //channelInitializer,初始化的channelHandler
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        //工作eventLoopGroup的配置和属性
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
        }
        //向channelPipeline中添加一个ChannelInitializer,等待register时执行initChannel方法
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                //1.添加handlers
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                //2.通过内部线程添加一个用于处理连接的channelHandler
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

通过源码可以看到首先对bossGroup的属性和配置进行设置子,然后对一些属性进行初始化,最后想pipeline中添加了一个ChannelInitializer其实也是一个channelHandler,我们跟踪到addLast方法中

public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
        if (handlers == null) {
            throw new NullPointerException("handlers");
        }
        //循环添加ChannelHandler
        for (ChannelHandler h: handlers) {
            if (h == null) {
                break;
            }
            addLast(executor, null, h);
        }

        return this;
    };
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {//防止有其他用户线程在执行添加channelHandler操作
            checkMultiplicity(handler);
            //1.将传入的handler包装到DefaultChannelHandlerContext中
            newCtx = newContext(group, filterName(name, handler), handler);
            //2.把DefaultChannelHandlerContext添加到双向链表中
            addLast0(newCtx);
            //3.如果channel还没有完成注册,就添加一个PendingHandlerAddedTask到
            // pendingHandlerCallbackHead单向链表上
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }
            //4.如果channel已经注册,而且不是内部线程在执行addLast操作,将callHandlerAdded0包装成一个任务
            //添加到NioEventLoop的普通任务队列中,等待执行
            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        //5.如果已经完成注册而且是内部线程,直接调用handlerAdded方法
        callHandlerAdded0(newCtx);
        return this;
    }

我们可以看到首先将传入的handler包装到DefaultChannelHandlerContext中,这是一个channelHandler的上下文,第二步将这个ChannelHandlerContext添加到pipeline初始化时候创建的双向链表中,第三步判断当前channel是不是完成注册,如果channel还没有完成注册,就添加一个PendingHandlerAddedTask回调任务到pendingHandlerCallbackHead单向链表上,第四步判断如果channel已经注册,而且不是内部线程在执行addLast操作,将callHandlerAdded0包装成一个任务添加到NioEventLoop的普通任务队列中,等待执行,最后判断如果已经完成注册而且是内部线程,直接调用handlerAdded方法。我们继续回到init方法ChannelInitializer中的init方法在register时会回调执行,在服务端的pipeline中添加了一个ServerBootstrapAcceptor,这个类从名称就可以看出来是一个处理连接接入的类,这个给类其实就是一个channelHandler,这个方法会将创建连接的channel转给workGroup,然后workGroup也会执行register,然后就可以处理客户端的读写事件。完成了NioServerSocketChannel的创建和初始化,开始调用真正的register方法

 //3.调用boss线程池的注册方法
ChannelFuture regFuture = config().group().register(channel);

调用config方法获取引导类配置,然后获取bossGroup,然后调用注册方法,我们继续跟进

 public ChannelFuture register(Channel channel) {
        //1.通过线程选择器选择一个线程执行register
        return next().register(channel);
    }
    }
public EventExecutor next() {
        return chooser.next();
    }

首先调用next方法从线程池中获取一个线程调用register方法,这里的chooser我们在之前已经分析过了,netty针对创建的NioEventLoopGroup中的线程数是不是2的幂创建了普通的线程选择器和优化后的线程选择器,最终都是从线程池中获取一个线程然后执行register,进入到SingleThreadEventLoop中

 public ChannelFuture register(Channel channel) {
        //1.创建一个DefaultChannelPromise用于返回执行结果
        return register(new DefaultChannelPromise(channel, this));
    }

可以看到这里首先创建了一个DefaultChannelPromise,并且传入了channel和this,DefaultChannelPromise是netty对jdk原生的future的扩展,里面扩展了很多方法,这里主要是为了记录register的结构,我们会在后续详细分析,我们继续关注注册方法

public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");//校验
        //调用unsafe的register方法
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

这里通过promise获取channel,通过channel获取其内部类unfase然后调用它的register方法,我们前面已经提到过,unsafe提供了很多底层的方法,我们继续跟踪到AbstractChannel的register方法

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            }
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }
            //关联channel和eventLoop
            AbstractChannel.this.eventLoop = eventLoop;
            //1.当前运行的线程是eventLoopGroup线程,及内部线程
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    //2.调用execute方法将register0方法包装成一个任务放入NioEventLoop的
                    //任务队列中,由NioEventLoop线程执行
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    logger.warn(
                            AbstractChannel.this, t);
                    closeForcibly();
                    closeFuture.setClosed();
                    safeSetFailure(promise, t);
                }
            }
        }

我们跳过上面的验证方法,首先判断当前当前执行的线程是不是内部线程,如果是就直接调用register0方法,这里我们是通过main线程调用的所以不是内部线程,会走到else语句中,调用execute方法把任务放入到eventLoop的普通任务队列中,待后续执行。我们跟踪到eventLoop.execute方法,这个方法会开启NioEventLoop线程,这里走到SingleThreadEventExecutor#execute方法

 public void execute(Runnable task) {
        //校验
        if (task == null) {
            throw new NullPointerException("task");
        }
        //1.判断是不是EventLoop线程
        boolean inEventLoop = inEventLoop();
        //2.如果是eventLoop线程直接添加一个任务到任务队列中
        if (inEventLoop) {
            addTask(task);
        } else {
            //3.如果是外部线程,而且eventLoop线程还没有启动则启动该线程
            startThread();
            addTask(task);
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }
        //4.判断是不是需要将NioEventLoop线程,从阻塞的select操作中唤醒
        if (!addTaskWakesUp && wakesUpForTask(task)) {
            wakeup(inEventLoop);
        }
    }

首先判断是不是EventLoop线程调用,如果是就直接添加一个任务到任务队列中,如果不是就判断当前的NioEventLoop线程是不是已经启动,没有则启动线程,最后判断是不是需要将NioEventLoop线程从阻塞的select方法中唤醒,唤醒后我们加入任务队列的任务就可以被执行,我们来看看启动线程方法

private void startThread() {
        //如果没有启动线程
        if (state == ST_NOT_STARTED) {
            //CAS修改状态为已启动状态
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
                try {
                    //启动线程
                    doStartThread();
                } catch (Throwable cause) {
                    STATE_UPDATER.set(this, ST_NOT_STARTED);
                    PlatformDependent.throwException(cause);
                }
            }
        }
    }

这里可以看到会判断当前线程的状态是不是已经启动,如果没有就通过CAS改变线程状态为启动状态,然后执行启动方法

private void doStartThread() {
        assert thread == null;
        executor.execute(new Runnable() {
            @Override
            public void run() {
                //1.首先将thread赋值为当前线程
                thread = Thread.currentThread();
                if (interrupted) {
                    thread.interrupt();
                }

                boolean success = false;
                //2.更新最后一次执行时间
                updateLastExecutionTime();
                try {
                    //3.执行SingleThreadEventExecutor的run方法
                    SingleThreadEventExecutor.this.run();
                    success = true;
                } catch (Throwable t) {
                    logger.warn("Unexpected exception from an event executor: ", t);
                } finally {
                    for (;;) {
                        int oldState = state;
                        if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
                                SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
                            break;
                        }
                    }

                    // Check if confirmShutdown() was called at the end of the loop.
                    if (success && gracefulShutdownStartTime == 0) {
                        logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
                                SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called " +
                                "before run() implementation terminates.");
                    }

                    try {
                        // Run all remaining tasks and shutdown hooks.
                        for (;;) {
                            if (confirmShutdown()) {
                                break;
                            }
                        }
                    } finally {
                        try {
                            cleanup();
                        } finally {
                            STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
                            threadLock.release();
                            if (!taskQueue.isEmpty()) {
                                logger.warn(
                                        "An event executor terminated with " +
                                                "non-empty task queue (" + taskQueue.size() + ')');
                            }

                            terminationFuture.setSuccess(null);
                        }
                    }
                }
            }
        });
    }

这里又执行了execute方法我们先忽略Runnable内容,我们继续跟踪来到ThreadPerTaskExecutor#execute方法

 public void execute(Runnable command) {
        //通过线程工厂创建线程,启动并执行command
        threadFactory.newThread(command).start();
    }
public Thread newThread(Runnable r) {
        //创建线程,设置线程名
        Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
        try {
            if (t.isDaemon() != daemon) {
                t.setDaemon(daemon);
            }

            if (t.getPriority() != priority) {
                t.setPriority(priority);
            }
        } catch (Exception ignored) {
            // Doesn't matter even if failed to set.
        }
        return t;
    }
    //创建一个FastThreadLocalThread线程
    protected Thread newThread(Runnable r, String name) {
        return new FastThreadLocalThread(threadGroup, r, name);

我们终于找到了正在的start方法,这里我们可以看到通过我们之前分析的线程工厂创建了一个线程然后执行start方法启动,然后执行传入的runnable执行单元,这里从创建的线程其实是一个FastThreadLocalThread,这个类基础了Thread,主要是为了使用FastThreadLocal,这个是netty对ThreadLocal的优化,如果要体现出FastThreadLocal的优势,必须要使用FastThreadLocalThread线程才可以,这个我们后面单独分析。
线程启动后开始执行run方法,首先将启动后的线程赋值当前的thread属性,然后更新最后一次的执行时间,然后开始执行NioEventLoop的run方法,这个run方法是一个循环,当线程状态是isShuttingDown的时候跳出当前线程,然后执行一些退出操作。我们这里只需要知道,run方法主要执行了三个事情:1.执行阻塞或者非阻塞的select方法。2.执行runAllTasks方法,执行我们通过execute方法添加的任务或者是定时时间已经到的定时任务。3.处理select方法已经就绪的selectionKeys的事件。所以这里线程启动后会执行我们的register0方法。

 private void register0(ChannelPromise promise) {
            try {
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                //1.设置第一此执行注册标志
                boolean firstRegistration = neverRegistered;
                //2.完成channel注册,返回连接selectionKey
                doRegister();
                neverRegistered = false;
                registered = true;
                //3.检查是否触发HandlerAdded事件
                pipeline.invokeHandlerAddedIfNeeded();
                //4.设置promise注册成功
                safeSetSuccess(promise);
                //5.触发ChannelRegistered事件
                pipeline.fireChannelRegistered();
                //6.检查channel是不是已经属于连接成功状态,这里channel还没有bind,所以返回false
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        //7.给channel注册读事件
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }

首先可以看到先设置了第一次执行注册方法的标志,然后开始执行真正的注册方法doRegister,我们跟进去会进入到AbstractNioChannel#doRegister方法

protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                //1.执行nio底层的register方法,并且attch了channel,不关系任何事件
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    //2.强行执行一次selectNow,取消了的SelectionKey可能还在缓存中未消除
                    eventLoop().selectNow();
                    selected = true;
                } else {  
                    throw e;
                }
            }
        }
    }

这里我们可以看到获取nio的channel然后调用注册方法,将channel注册到selector上,并且附带了一个this对象,而这个this对象指向的就是NioServerSocketChannel,然后这里不关注任何事件,我们回到register0方法中执行下面的逻辑

 //3.检查是否触发HandlerAdded事件
pipeline.invokeHandlerAddedIfNeeded();
final void invokeHandlerAddedIfNeeded() {
        assert channel.eventLoop().inEventLoop();
        //1.判断是不是第一次执行注册,如果是触发事件
        if (firstRegistration) {
            firstRegistration = false;
            callHandlerAddedForAllHandlers();
        }
    }
private void callHandlerAddedForAllHandlers() {
        final PendingHandlerCallback pendingHandlerCallbackHead;
        synchronized (this) {
            assert !registered;
            // This Channel itself was registered.
            registered = true;
            pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
            // Null out so it can be GC'ed.
            this.pendingHandlerCallbackHead = null;
        }
        //循环遍历PendingHandlerCallback单向链表触发execute方法
        PendingHandlerCallback task = pendingHandlerCallbackHead;
        while (task != null) {
            task.execute();
            task = task.next;
        }
    }

这里会进行判断如果是第一次调用register方法就会调用PendingHandlerCallback单向链表上的task任务,而我们前面分析了向单向链表中添加了一个PendingHandlerAddedTask任务,这里就会调用它的execute方法,而我们在前面分析init方法中,在最后就在pipeline中添加了一个ChannelInitializer,这个方法就包装在PendingHandlerAddedTask中,这时就会回调该方法,就是下面的方法

public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        //1.如果已经注册
        if (ctx.channel().isRegistered()) {
            initChannel(ctx);
        }
    }
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
        //1.防止重复添加
        if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
            try {
                //2.执行重写的初始化操作
                initChannel((C) ctx.channel());
            } catch (Throwable cause) {
                exceptionCaught(ctx, cause);
            } finally {
                //3.移除本身这个channelInitializer
                remove(ctx);
            }
            return true;
        }
        return false;
    }

我们可以看到通过调用handlerAdd方法最终会调用ChannelInitializer中的initChannel方法,而我们在上面已经说了我们在pipeline中添加了一个ChannelInitializer而且重写了它的initChannel方法,这里会调用到下

//向channelPipeline中添加一个ChannelInitializer,等待register时执行initChannel方法
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                //1.添加handlers
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                //2.通过内部线程添加一个用于处理连接的channelHandler
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });

首先判断是不是有handler,如果有就添加到pipeline中,然后通过内部线程向pipeline中添加了一个用于处理连接的handler及ServerBootstrapAcceptor,这个handler会将已经连接成功的channel转给workGroup进行处理,专门处理读写事件。完成初始化后会将ChannelInitializer从pipeline移除。
我们继续回到register0方法中,接着设置promise为sucess,触发ChannelRegistered事件,然后将事件向后面的handler传播,然后判断当前cahnnel是不是已经活跃状态,这里当前channel还没有bind,所以返回false方法执行结束。
走到这里已经完成了channel的初始化和注册,下面开始执行bind操作,我们继续回到AbstractBootstrap的doBind方法,这里首先会判断promise状态是不是已经完成,我们在前面知道register完成对promise设置成了success状态,如果成功这里就会直接调用doBind0方法,如果没有就给promise添加一个listener回调,其实就是观察者模式,如果完成就会触发listener中的事件,完成回调。从代码可以看出,无论是执行if逻辑还是else逻辑最终都会执行doBind0方法,这个才是重点

private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {
        //1.通过包装一个任务交给eventLoop执行
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

这里包装一个任务丢给内部线程执行,继续跟踪会走到AbstractChannelHandlerContext的bind方法

public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        if (isNotValidPromise(promise, false)) {
            // cancelled
            return promise;
        }
        //1.从tail开始遍历双向链表,查询第一个inbound的ChannelHandlerContext,其实就是headContext
        final AbstractChannelHandlerContext next = findContextOutbound();
        //2.获取eventLoop
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeBind(localAddress, promise);
        } else {
            safeExecute(executor, new Runnable() {
                @Override
                public void run() {
                    next.invokeBind(localAddress, promise);
                }
            }, promise, null);
        }
        return promise;
    }

首先判断配置的localAddress不能为null,也就是最少应该配置服务端的port。然后遍历pipeline中的双向链表选择一个inbound的ChannelHandlerContext,其实也就是headContext,由于我们这里是内部线程调用,所以这里会走if中,直接调用invokeBind方法。我们走到headContext方法的bind方法中

public void bind(
                ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
                throws Exception {
            unsafe.bind(localAddress, promise);
        }

这里调用unsafe的bind方法

public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            //一些判断省略
            //1.channel是不是active状态,bind前为false
            boolean wasActive = isActive();
            try {
                doBind(localAddress);
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }
            //2.bind的成功后开始执行ChannelActive事件
            if (!wasActive && isActive()) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireChannelActive();
                    }
                });
            }

            safeSetSuccess(promise);
        }

首先判断是不是active状态,bind前channel连接还没完成,所以wasActive为false,然后开始执行真正的doBind方法

protected void doBind(SocketAddress localAddress) throws Exception {
        //1.判断java版本执行不同的bind操作
        if (PlatformDependent.javaVersion() >= 7) {
            javaChannel().bind(localAddress, config.getBacklog());
        } else {
            javaChannel().socket().bind(localAddress, config.getBacklog());
        }
    }

这里根据不同JDK版本执行不同的绑定方法,绑定成功后则包装一个fireChannelActive方法到eventLoop的普通任务队列中等待执行,唤醒阻塞的select操作,执行新加入的任务,最后尝试将promise设置为success状态。到这里channel已经完成了注册和绑定,但是我们在前面分析过,channel在注册的时候并没有注册任何关心的事件,那是怎么处理的呢?我们继续跟进fireChannelActive方法。最终还是走到headContext的channelActive方法中

public void channelActive(ChannelHandlerContext ctx) throws Exception {
            //1.继续往后触发ChannelActive事件
            ctx.fireChannelActive();
            //2.设置OP_ACCEPT事件
            readIfIsAutoRead();
        }

可以看到首先继续将channel活跃事件传递到后面的channelHandler,然后设置OP_ACCEPT事件,我们继续跟进

private void readIfIsAutoRead() {
            //1.执行read方法
            if (channel.config().isAutoRead()) {
                channel.read();
            }
        }
public boolean isAutoRead() {
        return autoRead == 1;
    }
private volatile int autoRead = 1;

这里继续调用read方法

public ChannelHandlerContext read() {
        //1.从双向链表的查找outbound属性为true的ChannelHandlerContext,这里返回的就是headContext
        final AbstractChannelHandlerContext next = findContextOutbound();
        //2.获取eventLoop
        EventExecutor executor = next.executor();
        //3.由于是线程内部调用直接执行invokeRead
        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方法

public void read(ChannelHandlerContext ctx) {
            unsafe.beginRead();
        }

最终走到了AbstractNioChannel的doBeginRead方法

protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        //1.判断selectionKey是不是有效的
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;
        //2.获取关心的事件,这里其实什么都没关注
        final int interestOps = selectionKey.interestOps();
        //3.如果什么事件都不关系,则设置关注OP_ACCEPT事件
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

这里可以看到首先判断selectionKey是不是有效的,然后获取关心的事件,由于之前注册的时候什么事件都没关注,所以这里添加关注OP_ACCEPT事件。最终将记录结果的promise返回,到这里服务端的启动流程分析就结束了。如果有分析错误的地方还请不吝指正。感谢!!!

Netty源码分析:服务端启动全过程(篇幅很长) Netty源码分析:服务端启动全过程一般服务端的代码如下所示: public final class SimpleServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); 阅读详情

相关推荐

Netty 4源码解析:服务端启动

Netty 4源码解析:服务端启动1.基础知识1.1 Netty 4示例因为Netty 5还处于测试版,所以选择了目前比较稳定的Netty 4作为学习对象。而且5.0的变化也不像4.0这么大,好多网上的例子都已经过时了。 <dependency> <groupId>io.netty</groupId> <artifactId>netty-

西代零零发 1万+

netty+socket实现高并发

2、创建socket服务。

weixin_40725801的博客 985

【Netty】netty启动流程源码解析

【Netty】netty启动流程源码解析

芝兰生于深谷,不以无人而不芳 1319

netty源码分析

netty源码解析

qq_39203337的博客 3916

Netty WebSocket 客户端和服务端

WebSocketServerProtocolHandler 服务端 WebSocketClientProtocolHandler服务端收发消息

原名又逢乱世。不要放下学习的脚步,毕竟比天天打牌、钓鱼、打游戏更能从内在充实自己。 2106

netty服务端启动流程

说明:本文参考的 Netty 源码版本为 4.1.42.Final。 启动服务的过程中我们可以了解到 Netty 各大核心组件的关系,这将是学习 Netty 源码一个非常好的切入点,让我们一起看看 Netty 的每个零件是如何运转起来的吧。 文章目录从 Echo 服务器示例入手服务端启动全过程服务端 Channel 初始化及注册创建服务端 Channel初始化服务端 Channel注册服务端 Channel端口绑定netty服务端如何处理客户端新建连接总结 从 Echo 服务器示例入手 在这里我们实现了.

762

Netty源码一:服务端启动

/ todo 初始化 和 注册 带 Future 字眼的表示异步!!!它本身返回的就是一个ChannelFuture= null) {// TODO 继续绑定端口 doBind0} else {@Overrideif (cause!�先看initAndRegister方法try {

一只小星星 943

基于netty编写的socket服务端

基于netty框架编写的socket服务器

Netty源码—服务端启动流程

用户调用启动辅助类ServerBootstrap的bind()方法时,第一步通过反射创建服务端Channel会执行NioServerSocketChannel的默认构造方法,来创建一个NioServerSocketChannel对象,并且在创建过程中会创建Netty的一系列核心组件:如Channel、ChannelConfig、ChannelId、Unsafe、ChannelPipeline。ServerBootstrap.bind() //用户代码入口。

sdgfafg_25的博客 1053

ANSYS拓扑优化基础实例

唐深51CAE 2018/6/10 拓扑优化在工程设计中,常用于机械结构概念设计和轻量化设计,ANSYS通用有限元分析软件提供了强大的拓扑优化功能,本文将通过一个简单实例进行展示。 一、赵州桥 赵州桥又称安济桥,坐落在河北省赵县洨河上,建于隋朝年间公元595年~605年,由著名匠师李春设计建造,距今已有约1500年的历史,是当今世界上现存最早、保存最完整的古代敞...

TQT_CAE的博客 6026

RK3588平台-ES8388音频驱动调试与设备树配置实战

本文详细解析了在RK3588平台上调试ES8388音频驱动的实战过程。内容涵盖Linux音频驱动的三层架构(Machine、Platform、Codec),并重点提供了设备树(DTS)的完整配置示例,包括I2C节点、声卡节点及音频路由设置。文章还分享了驱动加载验证、amixer调试命令以及常见问题(如无声、变调)的排查思路,旨在帮助开发者快速完成RK3588与ES8388的音频系统集成。

mango的博客 805

Netty源码—1.服务端启动流程一

用户调用启动辅助类ServerBootstrap的bind()方法时,第一步通过反射创建服务端Channel会执行NioServerSocketChannel的默认构造方法,来创建一个NioServerSocketChannel对象,并且在创建过程中会创建Netty的一系列核心组件:如Channel、ChannelConfig、ChannelId、Unsafe、ChannelPipeline。Selector的轮询操作是由其绑定的EventLoop线程run()方法驱动的,在一个循环体内循环执行。

mjunz的博客 1201

CST电子枪仿真实战:从零搭建粒子追踪模型到磁场聚焦优化

本文提供了一份CST电子枪仿真实战指南,详细讲解了从零开始搭建粒子追踪模型的全过程。内容涵盖项目初始化、Track Solver求解器基础、几何建模、粒子源定义,并重点介绍了如何通过引入和优化静磁场(如聚焦线圈配置)来实现电子束的聚焦,帮助工程师高效预测和优化束流品质。

game4的博客 335

Netty源码分析一启动流程剖析

1.initAndRegister()对channel进行初始化、注册,返回regFuture(promise对象)1.1 initmain(主线程中完成)创建NioServerSocketChannel (相当于Nio中的ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();mian往NioServerSocketChannel添加初始化器handlermain。

m0_74229735的博客 1635

机器学习系列(3)_逻辑回归应用之Kaggle泰坦尼克之灾

f作者: 寒小阳 &&龙心尘 时间:2015年10月。 出处: 声明:版权所有,转载请注明出处,谢谢。手把手机器学习之逻辑回归应用——Kaggle泰坦尼克之灾1.引言先说一句,年末双十一什么的一来,真是非(mang)常(cheng)欢(gou)乐(le)!然后push自己抽出时间来写这篇blog的原因也非常简单: 写完前两篇逻辑回归的介绍和各个角度理解之后,小伙伴们纷纷表示『好像很高级的样纸,

寒小阳 43万+

自动驾驶中的多传感器融合实战:用卡尔曼滤波搞定毫米波雷达与摄像头航迹对齐

本文深入探讨了自动驾驶中多传感器融合的实战技术,重点介绍了如何使用卡尔曼滤波实现毫米波雷达与摄像头的航迹对齐。通过分析传感器特性差异、时空同步挑战及工程化实现步骤,提供了包括状态向量定义、观测矩阵设计和故障检测机制在内的完整解决方案,最终实现0.15米以内的高精度航迹对齐。

weixin_28728495的博客 410

Netty源码—1.服务端启动流程二

ServerBootstrap的bind()方法,首先执行AbstractBootstrap的initAndRegister()方法完成了服务端Channel的初始化和注册后,就会调用AbstractBootstrap的doBind0()方法绑定端口。AbstractUnsafe的bind()方法中所调用的doBind()方法是属于AbstractChannel的抽象接口,会由NioServerSocketChannel来进行具体的实现,即调用JDK底层NIO的bind()方法来绑定端口。

mjunz的博客 1219

《派出你的AI同事:WorkBuddy案例实战》062:投研资讯摘要与预警

投研资讯摘要与预警:新闻、公告、行情交给 AI 同事提炼与预警,投研从“刷资讯”变成“读摘要”。

LuckyHanMo的博客 181

Netty Review - 服务端channel注册流程源码解析

当有新的连接接入时,会触发ACCEPT事件,EventLoop会调用相关的ChannelHandler进行处理,如调用ChannelInitializer的initChannel方法,添加用户自定义的ChannelHandler到新的连接的ChannelPipeline中。接着,新的连接就可以接受和处理客户端的请求了。当一个连接被注册到某个 Worker 线程池的 EventLoop 中时,该 EventLoop 将负责处理这个连接的所有事件,包括读取数据、写入数据、处理网络事件等。

小工匠 8125

yolov26改进 | 检测头篇 | 利用渐近特征金字塔网络AFPN改进yolov26检测头融合P2网络结构(让小目标无所遁形,全网独家首发)

本文介绍一种基于AFPN渐近特征金字塔网络改进的YOLOv26四检测头Detect_FPN。AFPN通过逐级融合底层细节、中层结构与高层语义特征,缩小不同尺度特征之间的语义差距,减少直接融合带来的信息冲突,使检测头获得更稳定、更充分的多尺度表达。本文进一步增加高分辨率小目标检测分支,强化对微小目标、密集目标和复杂背景目标的感知能力,适合工业缺陷、遥感及无人机等场景。文中将展示基础模型与改进模型的训练对比结果,并提供完整代码、配置文件、模块注册方法及Codex修改思路,方便直接复现和二次创新。

Snu77的博客 367

【数据分析】基于多组学数据整合的微生物与宿主互作网络构建及可视化分析

综上所述,本教程通过详细的步骤和方法,展示了如何整合微生物群落数据、宿主基因表达数据和代谢组数据,构建微生物与宿主互作网络,并进行可视化分析。这种方法为研究微生物与宿主之间的复杂关系提供了一种有力的工具,有助于推动相关疾病的研究和治疗。然而,在实际应用中,需要注意数据处理和分析过程中的各种问题,并结合实验验证来提高研究结果的可靠性和科学性。未来,随着多组学技术的不断发展和完善,以及数据分析方法的创新,相信我们能够更深入地理解微生物与宿主之间的互作机制,为疾病的预防、诊断和治疗提供更有力的支持。

专注生信领域 366

Netty 核心源码解析

Netty 第一讲:Netty 架构与原理本文是《Netty 三讲》的第二讲:Netty 核心源码解析(部分),大纲如下:前言1. Netty 服务端启动过程源码剖析1.1. 执行 ne...

weixin_41385912的博客 1万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

【Netty 源码】服务端启动流程源码分析 篇一

使用Java NIO创建服务端时,通常我们需要先创建Channel,Selector两个对象,然后将Channel绑定端口并注册到Selector上,最后对事件轮询监听 2.Netty 服务端创建流程 Netty的服务端创建流程都在 ServerBootstrap.bind方法中完成 2.1 initAndRegister() 初始化Channel并将Channel注册到Selector 通过堆栈信息可以看到此方法由main线程进行调用channelFactory.newChannel()通过DEBUG追

纸上得来终觉浅,绝知此事要躬行 1200

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Netty源码—1.服务端启动流程

在AbstractNioChannel的doRegister()方法中,首先获取前面创建的JDK底层NIO的Channel,然后调用JDK底层NIO的register()方法,将this也就是NioServerSocketChannel对象当作attachment绑定到JDK的Selector上。调用NioEventLoopGroup的register()方法时,会先通过next()方法获取一个NioEventLoop对象,然后再调用NioEventLoop的register()方法。

weixin_55010563的博客 727

Netty源码分析(2)-服务端启动流程

文章目录1. 服务端启动步骤2. 服务端启动流程详解 1. 服务端启动步骤 主要步骤为: 创建 ServerBootstrap 实例 设置并绑定 Reactor 线程池 设置并绑定服务端 Channel 创建并初始化 ChannelPipeline 添加并设置 ChannelHandler 绑定并启动监听端口 2. 服务端启动流程详解 ...

谈谈1974 3417

Netty源码阅读:(一)服务端启动流程

Netty服务端启动流程源码分析

qq_45913654的博客 775
下一篇: netty源码浅析-客户端启动流程
胖柯G
博客等级 码龄10年 453粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值