Netty的EventLoopGroup创建过程源码的解析(一)

4 篇文章 0 订阅

       Netty是由JBOSS提供的一个java开源框架,是一个高性能、高可扩展性的异步事件 驱动的网络应用程序框架,它极大地简化了TCP和UDP客户端和服务器开发等网络编程。Netty具有统一的API,支持多种传输类型,阻塞和非阻塞的,它同时拥有简单而强大的Recator线程模型,支持无连接数据报套接字,基于灵活且可扩展的事件模型,可以清晰地分离关注点,我们可以在github的https://github.com/netty/netty/tree/4.1/example这里看到netty实现的大量例子,包括socket,redis,http等实现,比Java的核心API更高的吞吐量以及更低的延迟,是因为实现了池化和复用机制,也具有完整的SSL / TLS和StartTLS支持。

       我们看下怎么启动一个Netty应用,并分析下它是怎么一步步的启动的,netty使用的是4.1.34.Final版本的,下面看下这个代码片段

public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            NettyServerHandlerTest1 handlerTest1 = new NettyServerHandlerTest1();
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,1024)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(handlerTest1);
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(8010).sync();
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

       在上面这个代码中我们创建的是一个服务端的代码,它首先创建了两个EventLoopGroup对象分别是bossGroup和workGroup,bossGroup是用于和客户端连接作用的,workGroup是用于将bossGroup接收到的客户端请求进行分别处理的线程,在这里就要提到一个Reactor线程模型了,它是由Doug Lee提出的一种线程模型,它的大概原理如下图

       当有客户端连接到服务端mainReactor主线程的时候,在主线程经过一定的处理后将接收到的信息传递给一个Acceptor线程,在Acceptor中有多个subReactor的数组,经过一定的取值方式取出其中一个subReactor线程对在channel中的数据进行读取,如果是业务数据则新建一个线程来进行业务上面的操作,这样就保证了线程的不会阻塞,同时也实现了一个subReactor可以处理多个channel来的业务数据的要求,这个就是大概的Reactor线程模型的含义。我们接着看下EventLoopGroup的创建过程,在创建bossGroup的时候我们传入的参数是1,这个可以理解为1个服务器只有1个入口,通过这个入口在将消息分发给其他workGroup,而workGroup创建的时候我们是没有传入参数的,我们主要看下这个的创建过程,整理了一个如下图所示的图片

       通过构造函数我们可以进入到MultitreadEvnetLoopGroup类

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }

        在这个类的构造函数中判断了我们传入的线程数量,如果传入的线程数量为0,那么就去取默认值,这里有2种默认值的实现,一种是通过系统参数io.netty.eventLoopThreads进行指定,另外一种是在没有系统参数指定的情况下,默认获取系统处理器数量*2的值

private static final int DEFAULT_EVENT_LOOP_THREADS;

    static {
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

        ......
    }

然后继续通过构造函数进入到MultithreadEventExecutorGroup类中

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) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }

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

        ......

       在该构造函数中,如果在Executor没有初始化的情况下,创建了一个默认工厂线程执行类,然后根据线程个数创建一个线程数组children,循环该children,通过newChild()方法创建一个NioEventLoop对象,接下来我们看下NioEventLoop的实现

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
        if (selectorProvider == null) {
            throw new NullPointerException("selectorProvider");
        }
        if (strategy == null) {
            throw new NullPointerException("selectStrategy");
        }
        provider = selectorProvider;
        final SelectorTuple selectorTuple = openSelector();
        selector = selectorTuple.selector;
        unwrappedSelector = selectorTuple.unwrappedSelector;
        selectStrategy = strategy;
    }

       在NioEventLoop构造函数中调用了super的实现,以及新建了一个selectorTuple变量,这个变量对java.nio.channels.Selector进行了封装,也就是说这里还是一个对Java原生的Selector的实现,再次进入到super()方法,分别依次调用了这个两个super

protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
                                    boolean addTaskWakesUp, int maxPendingTasks,
                                    RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
        tailTasks = newTaskQueue(maxPendingTasks);
    }
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                        boolean addTaskWakesUp, int maxPendingTasks,
                                        RejectedExecutionHandler rejectedHandler) {
        super(parent);
        this.addTaskWakesUp = addTaskWakesUp;
        this.maxPendingTasks = Math.max(16, maxPendingTasks);
        this.executor = ObjectUtil.checkNotNull(executor, "executor");
        taskQueue = newTaskQueue(this.maxPendingTasks);
        rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
    }

       在这2个构造方法中,分别实现了2个queue队列,通过这两个queue的实现,都可以理解为工作线程队列。分析完children数组的创建,我们再次回到MultithreadEventExecutorGroup的构造函数中

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
       ......

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

       接下来我们创建一个了chooser,在这个chooser中,是根据2的n次方进行进行初始化的,看下代码

public EventExecutorChooser newChooser(EventExecutor[] executors) {
        if (isPowerOfTwo(executors.length)) {
            return new PowerOfTwoEventExecutorChooser(executors);
        } else {
            return new GenericEventExecutorChooser(executors);
        }
    }
private static boolean isPowerOfTwo(int val) {
        return (val & -val) == val;
    }

       chooser的选择初始化完成后,在对children中的NioEventLoop对象进行了销毁时的监听,最后创建了一个不可修改的readonlyChildren的set集合,至此就完成了所有的NioEvent对象的创建,在下篇文章中我们分析端口的bind及启动过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值