Netty4 各种源码文章后的总结

把Netty4关于源码解读的文章看完以后还是很多疑惑,例如:Netty是基于一个怎样的点来设计框架的(解决了一个什么样的痛点),把这些文章归纳总结很有必要。

如果把解析Netty4原理作为一个项目来管理,可以尝试分析这个流程:

  • 范围管理:规划过程(代码模块很多,需要有主次区别对待阅读理解;基础知识储备),监控过程(检查是否过多陷入不必要的细节)
  • 时间管理:对流程有完整的理解,需要对代码进行拆解,进行优先级排序(Netty4主要模块Bootstrap、Channel、Handle、Eventloop、Bytebuf),
  • 通过类图关系找出依赖关系,通过启动入口找出对象的调用关系。

项目落地步骤

  1. Java NIO的网络编程基本原理
  2. 如何拆解串行流程
  3. 整理Netty的数据流程

Java NIO 网络编程基本代码分析

// 创建一个selector
        Selector selector = Selector.open();
        // 创建一个ServerSocketChannel
        ServerSocketChannel servChannel = ServerSocketChannel.open();
        servChannel.configureBlocking(false);
        // 绑定端口号
        servChannel.socket().bind(new InetSocketAddress(8080), 1024);
        // 注册感兴趣事件
        servChannel.register(selector, SelectionKey.OP_ACCEPT);
        
        // select系统调用
        selector.select(1000);
        
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
        Iterator<SelectionKey> it = selectedKeys.iterator();
        SelectionKey key = null;
        while (it.hasNext()) {
            key = it.next();
            it.remove();
            if (key.isValid()) {
                // 处理新接入的请求消息
                if (key.isAcceptable()) {
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    // 接收客户端的连接,并创建一个SocketChannel
                    SocketChannel sc = ssc.accept(); 
                    sc.configureBlocking(false);
                    // 将SocketChannel和感兴趣事件注册到selector
                    sc.register(selector, SelectionKey.OP_READ); 
                }
                if (key.isReadable()) {
                    // 读数据的处理
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buf = (ByteBuffer) key.attachment();
                    long bytesRead = clientChannel.read(buf);
                    if (bytesRead == -1) {
                        clientChannel.close();
                    } else if (bytesRead > 0) {
                        key.interestOps(OP_READ | SelectionKey.OP_WRITE);
                        System.out.println("Get data length: " + bytesRead);
                    }
                }
                
                if (key.isValid() && key.isWritable()) {
                    ByteBuffer buf = (ByteBuffer) key.attachment();
                    buf.flip();
                    SocketChannel clientChannel = (SocketChannel) key.channel();

                    clientChannel.write(buf);

                    if (!buf.hasRemaining()) {
                        key.interestOps(OP_READ);
                    }
                    buf.compact();
                }
            }
        }  

selector、SelectionKey、pollArray等几个数据结构以及相互持有关系

为了使用 Selector,首先需要将Channel注册到Selector中,随后调用Selector的select()方法,这个方法会阻塞,直到注册在Selector中的Channel发送可读写事件。当这个方法返回后,当前的这个线程就可以处理Channel的事件。

从上面的代码可以拆解出不同事件:

  • Channel与Selector 注册(多线程中可以初始化多个selector与channel绑定,boosgroup关键所在)(绑定多端口时有多个selector)
  • Selector Accept事件(处理新接入的请求消息(完成TCP握手,建立物理链接))
  • Selector 读、写事件(此方法非阻塞)

拆解思路

最初很疑惑的地方,俗称伪异步I/O设计,网络接收+队列,服务端收到数据后直接放到队列中,模式可能没问题。但是对网络接收处理有问题,如果要响应上千万的网络请求连接同时需要快速的把数据从网卡(TCP缓冲区)中拿走,如果拿走不及时,TCP window size 会越来越小,客户端最终会停止发送数据,新的请求也无法被响应。

Selector的出现使网络接收处理的流程彻底改变,Selector  Accept 事件与读写事件可以由独立线程处理(TCP),线程处理完Accept事件后,丢给后面的读写线程处理读写事件。而Netty所做的事情就是让这些事件可以在它所设计的多线程体系中不断被编排处理。

Netty 数据流程

客户端代码:

EventLoopGroup group = new NioEventLoopGroup();
try {
    Bootstrap b = new Bootstrap();
    b.group(group)
     .channel(NioSocketChannel.class)
     .option(ChannelOption.TCP_NODELAY, true)
     .handler(new ChannelInitializer<SocketChannel>() {
         @Override
         public void initChannel(SocketChannel ch) throws Exception {
             ChannelPipeline p = ch.pipeline();
             p.addLast(new EchoClientHandler());
         }
     });

    // Start the client.
    ChannelFuture f = b.connect(HOST, PORT).sync();

    // Wait until the connection is closed.
    f.channel().closeFuture().sync();
} finally {
    // Shut down the event loop to terminate all threads.
    group.shutdownGracefully();
}

客户端Channel初始化流程:

Bootstrap.connect -> Bootstrap.doConnect -> AbstractBootstrap.initAndRegister

pipeline初始化流程:

DefaultChannelPipeline 初始化,包含TailContext,HeadContext

EventloopGroup初始化流程:

MultithreadEventExecutorGroup 实现初始化

channel 的注册过程:

AbstractBootstrap.initAndRegister -> MultithreadEventLoopGroup.register -> SingleThreadEventLoop.register -> AbstractChannel.AbstractUnsafe.register

Handler添加过程:

ChannelInitializer 抽象类

客户端连接过程:

Bootstrap.doConnect ->Channel.connect->DefaultChannelPipeline.connect->AbstractChannelHandlerContext.connect->HeadContext.connect -> AbstractNioUnsafe.connect(抽象方法):

服务端代码:

public static void main(String[] args) throws Exception {

		final SslContext sslCtx;
		if (SSL) {
			SelfSignedCertificate ssc = new SelfSignedCertificate();
			sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
		} else {
			sslCtx = null;
		}

		// Configure the server.
		EventLoopGroup bossGroup = new NioEventLoopGroup(1);
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		final EchoServerHandler serverHandler = new EchoServerHandler();
		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();
							if (sslCtx != null) {
								p.addLast(sslCtx.newHandler(ch.alloc()));
							}
							// p.addLast(new LoggingHandler(LogLevel.INFO));
							p.addLast(serverHandler);
						}
					});

			// Start the server.
			ChannelFuture f = b.bind(PORT).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();
		}
	}
  • NioServerSocketChannel 初始化
  • ChannelPipeline 初始化
  • Channel 的注册
  • 绑定:AbstractBootstrap.bind -> AbstractBootstrap.doBind -> AbstractBootstrap.initAndRegister->ServerBootstrap.init (ServerBootstrapAcceptor )
  • handler 的添加过程
  1. 在服务器 NioServerSocketChannel 的 pipeline 中添加的是 handler 与 ServerBootstrapAcceptor.
  2. 当有新的客户端连接请求时, ServerBootstrapAcceptor.channelRead 中负责新建此连接的 NioSocketChannel 并添加 childHandler 到 NioSocketChannel 对应的 pipeline 中, 并将此 channel 绑定到 workerGroup 中的某个 eventLoop 中.
  3. handler 是在 accept 阶段起作用, 它处理客户端的连接请求.
  4. childHandler 是在客户端连接建立以后起作用, 它负责客户端连接的 IO 交互.

在这里插入图片描述

基本流程认识后需要对后续的每个主要类进行详细分析。

	@Override
	void init(Channel channel) {
		setChannelOptions(channel, newOptionsArray(), logger);
		setAttributes(channel, newAttributesArray());

		ChannelPipeline p = channel.pipeline();

		final EventLoopGroup currentChildGroup = childGroup;
		final ChannelHandler currentChildHandler = childHandler;
		final Entry<ChannelOption<?>, Object>[] currentChildOptions = newOptionsArray(childOptions);
		final Entry<AttributeKey<?>, Object>[] currentChildAttrs = newAttributesArray(childAttrs);

		p.addLast(new ChannelInitializer<Channel>() {
			@Override
			public void initChannel(final Channel ch) {
				final ChannelPipeline pipeline = ch.pipeline();

				ChannelHandler handler = config.handler();
				if (handler != null) {
					pipeline.addLast(handler);
				}

				
				//boosgroup  register到channel(注册在AbstractBootstrap中),bossGroup线程执行该代码
                // ServerBootstrapAcceptor 添加到尾端,最后转发,把连接注册到workGroup
				ch.eventLoop().execute(new Runnable() {
					@Override
					public void run() {
						logger.info("===========转发客户端连接");

						pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
					}
				});
			}
		});
	}
 private void doStartThread() {
        //workgroup bossgroup
        executor.execute(new Runnable() {
            @Override
            public void run() {

                try {
                	
                	logger.info("========================doStartThread run()");
                    SingleThreadEventExecutor.this.run();
                    
                    success = true;
                } catch (Throwable t) {
                    logger.warn("Unexpected exception from an event executor: ", t);
                } finally {
                     
                    
                }
            }
        });
    }
#SingleThreadEventExecutor    
private void execute(Runnable task, boolean immediate) {
        boolean inEventLoop = inEventLoop();
        addTask(task);
        if (!inEventLoop) {
            startThread();
            if (isShutdown()) {
                boolean reject = false;
                try {
                    if (removeTask(task)) {
                        reject = true;
                    }
                } catch (UnsupportedOperationException e) {
                   
                }
                if (reject) {
                    reject();
                }
            }
        }

    }

读代码注意:

  1. 注册监听处理器,是由bossGroup线程执行(bossGroup与channel绑定)
  2. SingleThreadEventExecutor  execute方法执行线程, executor属性可以是bossGroup或workGroup,读代码过程要注意EventLoop是哪个
  3. inEventLoop方法也需要注意,可能会执行startThread()方法(startThread()会获取IO事件)
  4. NioEventLoop中run方法循环获取事件
  5. handle链如何执行,pipeline绑定在channel上,NioEventLoop.run()执行绑定handle
  6. handler与childHandler把这个handler理解成一个,其实是独立的,分别对应boss和work线程中的channel

日志

13:37:46.293 [main] INFO  i.n.c.MultithreadEventLoopGroup - ============multithread register
main============single thread
13:37:46.324 [main] INFO  io.netty.channel.AbstractChannel - ==============register
13:37:46.339 [main] INFO  i.n.u.c.SingleThreadEventExecutor - false=======================io.netty.util.internal.ThreadExecutorMap$1@2344fc66
13:37:46.339 [main] INFO  i.n.u.c.SingleThreadEventExecutor - ========================doStartThread
#boss线程启动
13:37:46.339 [nioEventLoopGroup-2-1] INFO  i.n.u.c.SingleThreadEventExecutor - ========================doStartThread run()
13:37:46.339 [nioEventLoopGroup-2-1] INFO  io.netty.channel.nio.NioEventLoop - =================NioEvent start
13:37:46.339 [nioEventLoopGroup-2-1] INFO  i.n.channel.nio.AbstractNioChannel - =========selectionKey:::sun.nio.ch.SelectionKeyImpl@5b9db458
13:37:46.354 [nioEventLoopGroup-2-1] INFO  i.n.channel.DefaultChannelPipeline - ========================invokeHandlerAddedIfNeeded[id: 0x5965918e]
13:37:46.354 [nioEventLoopGroup-2-1] INFO  i.n.u.c.SingleThreadEventExecutor - true=======================io.netty.util.internal.ThreadExecutorMap$1@2344fc66
13:37:46.354 [nioEventLoopGroup-2-1] INFO  io.netty.channel.AbstractChannel - ===================register0##########DefaultChannelPipeline{(LoggingHandler#0 = io.netty.handler.logging.LoggingHandler)}
13:37:46.354 [nioEventLoopGroup-2-1] INFO  i.n.u.c.SingleThreadEventExecutor - true=======================io.netty.util.internal.ThreadExecutorMap$1@2344fc66
13:37:46.354 [nioEventLoopGroup-2-1] INFO  i.n.channel.DefaultChannelPipeline - ========================invokeHandlerAddedIfNeeded[id: 0x5965918e]
13:37:46.354 [nioEventLoopGroup-2-1] INFO  i.n.c.AbstractChannelHandlerContext - =======================fireChannelRegistered
13:37:46.354 [nioEventLoopGroup-2-1] INFO  i.n.handler.logging.LoggingHandler - [id: 0x5965918e] REGISTERED
13:37:46.354 [nioEventLoopGroup-2-1] INFO  i.n.c.AbstractChannelHandlerContext - =======================fireChannelRegistered
13:37:46.354 [nioEventLoopGroup-2-1] INFO  io.netty.bootstrap.ServerBootstrap - ===========转发客户端连接
13:37:46.370 [nioEventLoopGroup-2-1] INFO  i.n.handler.logging.LoggingHandler - [id: 0x5965918e] BIND: 0.0.0.0/0.0.0.0:8007
13:37:46.370 [nioEventLoopGroup-2-1] INFO  i.n.u.c.SingleThreadEventExecutor - true=======================io.netty.util.internal.ThreadExecutorMap$1@2344fc66
13:37:46.386 [nioEventLoopGroup-2-1] INFO  i.n.handler.logging.LoggingHandler - [id: 0x5965918e, L:/0:0:0:0:0:0:0:0:8007] ACTIVE
###########boss启动完成
###########boss线程监听到read事件
nioEventLoopGroup-2-1 AbstractNioMessageChannel.read()=============DefaultChannelPipeline{(LoggingHandler#0 = io.netty.handler.logging.LoggingHandler), (ServerBootstrap$ServerBootstrapAcceptor#0 = io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor)}
13:38:29.549 [nioEventLoopGroup-2-1] INFO  i.n.handler.logging.LoggingHandler - [id: 0x5965918e, L:/0:0:0:0:0:0:0:0:8007] READ: [id: 0xd43b4a62, L:/127.0.0.1:8007 - R:/127.0.0.1:64813]
=================log read
13:38:29.549 [nioEventLoopGroup-2-1] INFO  io.netty.bootstrap.ServerBootstrap - =======================ServerBootstrapAcceptor
13:38:29.549 [nioEventLoopGroup-2-1] INFO  io.netty.bootstrap.ServerBootstrap - =======================ServerBootstrapAcceptor###########   DefaultChannelPipeline{(EchoServer$1#0 = io.netty.example.echo.EchoServer$1)}
13:38:29.549 [nioEventLoopGroup-2-1] INFO  io.netty.bootstrap.ServerBootstrap - =======================ServerBootstrapAcceptor register
13:38:29.549 [nioEventLoopGroup-2-1] INFO  i.n.c.MultithreadEventLoopGroup - ============multithread register
nioEventLoopGroup-2-1============single thread
13:38:29.549 [nioEventLoopGroup-2-1] INFO  io.netty.channel.AbstractChannel - ==============register
13:38:29.549 [nioEventLoopGroup-2-1] INFO  i.n.u.c.SingleThreadEventExecutor - false=======================io.netty.util.internal.ThreadExecutorMap$1@f8071a0
13:38:29.549 [nioEventLoopGroup-2-1] INFO  i.n.u.c.SingleThreadEventExecutor - ========================doStartThread
################work线程启动
13:38:29.549 [nioEventLoopGroup-3-1] INFO  i.n.u.c.SingleThreadEventExecutor - ========================doStartThread run()
13:38:29.549 [nioEventLoopGroup-3-1] INFO  io.netty.channel.nio.NioEventLoop - =================NioEvent start
13:38:29.549 [nioEventLoopGroup-3-1] INFO  i.n.channel.nio.AbstractNioChannel - =========selectionKey:::sun.nio.ch.SelectionKeyImpl@3be818a7
13:38:29.549 [nioEventLoopGroup-3-1] INFO  i.n.channel.DefaultChannelPipeline - ========================invokeHandlerAddedIfNeeded[id: 0xd43b4a62, L:/127.0.0.1:8007 - R:/127.0.0.1:64813]
13:38:29.549 [nioEventLoopGroup-2-1] INFO  i.n.handler.logging.LoggingHandler - [id: 0x5965918e, L:/0:0:0:0:0:0:0:0:8007] READ COMPLETE
13:38:29.549 [nioEventLoopGroup-3-1] INFO  io.netty.channel.AbstractChannel - ===================register0##########DefaultChannelPipeline{(EchoServerHandler#0 = io.netty.example.echo.EchoServerHandler)}
13:38:29.549 [nioEventLoopGroup-3-1] INFO  i.n.channel.DefaultChannelPipeline - ========================invokeHandlerAddedIfNeeded[id: 0xd43b4a62, L:/127.0.0.1:8007 - R:/127.0.0.1:64813]
13:38:29.549 [nioEventLoopGroup-3-1] INFO  i.n.c.AbstractChannelHandlerContext - =======================fireChannelRegistered

buddy算法.png

 

 

 

 

 

参考:

Java NIO之网络编程

Java NIO 的前生今世 之四 NIO Selector 详解

《TCP-IP详解:卷2》

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值