文档章节

Netty源码分析(一)概览

乒乓狂魔
 乒乓狂魔
发布于 2016/08/17 15:38
字数 1569
阅读 439
收藏 2
点赞 0
评论 0

准备将Netty的源码过一下,一来对自己是个总结消化的过程,二来希望对那些打算看Netty源码的人(已经熟悉Netty的Reactor模型)能有一些帮助。目前所看Netty版本是4.1.3.Final。

1 目录

2 概览

2.1 服务器端demo

看下一个简单的Netty服务器端的例子

public static void main(String[] args){
	EventLoopGroup bossGroup=new NioEventLoopGroup(1);
	EventLoopGroup workerGroup = new NioEventLoopGroup();
	try {
		ServerBootstrap serverBootstrap=new ServerBootstrap();
		serverBootstrap.group(bossGroup,workerGroup)
			.channel(NioServerSocketChannel.class)
			.option(ChannelOption.SO_BACKLOG, 200)
			.childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(80,0,4,0,4));
					ch.pipeline().addLast(new StringDecoder(Charset.forName("UTF-8")));
					ch.pipeline().addLast(new TcpServerHandler());
				}
			});
		ChannelFuture f=serverBootstrap.bind(8080).sync();
		f.channel().closeFuture().sync();
	} catch (InterruptedException e) {
		e.printStackTrace();
	}finally {  
        workerGroup.shutdownGracefully();  
        bossGroup.shutdownGracefully();  
    }  
}

先来简单说说上述遇到的类:

2.2 EventLoopGroup介绍

它主要包含2个方面的功能,注册Channel和执行一些Runnable任务。

EventLoopGroup介绍

功能1:先来看看注册Channel,即将Channel注册到Selector上,由Selector来调度Channel的相关事件,如读、写、Accept等事件。

而EventLoopGroup的设计是,它包含多个EventLoop(每一个EventLoop通常内部包含一个线程),在执行上述注册过程中是需要选择其中的一个EventLoop来执行上述注册行为,这里就出现了一个选择策略的问题,该选择策略接口是EventExecutorChooser,你也可以自定义一个实现。

从上面可以看到,EventLoopGroup做的工作大部分是一些总体性的工作如初始化上述多个EventLoop、EventExecutorChooser等,具体的注册Channel还是交给它内部的EventLoop来实现。

功能2:执行一些Runnable任务

EventLoopGroup继承了EventExecutorGroup,EventExecutorGroup也是EventExecutor的集合,EventExecutorGroup也是掌管着EventExecutor的初始化工作,EventExecutorGroup对于Runnable任务的执行也是选择内部中的一个EventExecutor来做具体的执行工作。

netty中很多任务都是异步执行的,一旦当前线程要对某个EventLoop执行相关操作,如注册Channel到某个EventLoop,如果当前线程和所要操作的EventLoop内部的线程不是同一个,则当前线程就仅仅向EventLoop提交一个注册任务,对外返回一个ChannelFuture。

总结:EventLoopGroup含有上述2种功能,它更多的是一个集合,但是具体的功能实现还是选择内部的一个item元素来执行相关任务。 这里的内部item元素通常即实现了EventLoop,又实现了EventExecutor,如NioEventLoop等

2.3 ChannelPipeline介绍

上述EventLoopGroup可以将一个Channel注册到内部的一个EventLoop的Selector上,然后对于这个Channel的相关读写等事件,Netty专门设计了一个ChannelPipeline来进行处理。每一个Channel都有一个ChannelPipeline来处理该Channel的读写等事件。

ChannelPipeline介绍

2.4 bind过程

上述serverBootstrap的bind过程如下:

  • 创建出你所指定的NioServerSocketChannel,然后初始化一些Socket方面的参数

  • 为上述Channel的ChannelPipeline配置一个ChannelHandler,该ChannelHandler的作用就是在该Channel成功注册到Selector上的时候,初始化一些逻辑,即initChannel方法中执行一些逻辑,该逻辑就是向ChannelPipeline中添加一个新的ChannelHandler即ServerBootstrapAcceptor

  • 然后开始将该Channel注册到上述EventLoopGroup bossGroup中,该EventLoopGroup bossGroup会选择内部的一个EventLoop来执行实际的注册行为(这个时候就是当前线程和操作的EventLoop不是同一个线程,即该过程是异步提交一个Runnable),一旦注册完成,就执行上述ChannelHandler的initChannel方法

至此,就完成了整个bind过程。一旦EventLoop内部的Selector检测到NioServerSocketChannel有新的连接到来的事件,则会交给NioServerSocketChannel的ChannelPipeline来处理,重点就是ChannelPipeline中的上述ServerBootstrapAcceptor,ServerBootstrapAcceptor做如下操作:

  • 1 为新的Channel的ChannelPipeline配置我们上述代码中的childHandler指定的ChannelHandler

  • 2 将新的Channel注册到了上述EventLoopGroup workerGroup中

2.5 sync介绍

bind方法返回的是一个ChannelFuture,从上面我们也知道该过程是异步的,sync方法则是一直等待到该异步过程结束。

再看下f.channel().closeFuture().sync()这个方法

每一个ChannelFuture都是和一个Channel绑定的,所以可以通过ChannelFuture来获取对应绑定的Channel对象

每一个Channel对象都有一个CloseFuture closeFuture对象,上述closeFuture方法并不是去执行close方法而是获取到这个CloseFuture closeFuture对象,然后调用它的sync方法即等待这个Future的结束。一般正常情况下是不会调用这个Future的结束方法的,只是在上述过程或者其他过程出现问题的时候,如注册到EventLoop失败等才会去调用这个Feture的结束方法,所以正常情况下主线程会一直阻塞在CloseFuture closeFuture的sync方法上。

3 误区

上述的bossGroup的创建问题。

我们都知道bossGroup是用来accept连接,然后将连接绑定到workerGroup中的,一般情况下bossGroup设置线程数为1即可(基本只能为1),我们同时知道Ractor模型中可以使用多个Acceptor线程来执行accept操作,加快accept的速度。

如果你想加快accept的速度,想开启多线程来accept,这时候想设置bossGroup的线程数为多个的话,就大错特错了,是根本没效果的。

结合上面的原理,只有在bind端口的时候才会创建一个ServerSocketChannel,然后注册到bossGroup内部的一个EventLoop中,仍然是单线程负责ServerSocketChannel的accept工作,而bossGroup中的多线程仅仅是为bind多个端口服务的。

我们来看下tomcat是如何允许多个Acceptor线程来执行accept操作的:

  • 1 创建了一个ServerSocketChannel serverSock,并bind到某个端口

  • 2 开启多个Acceptor线程,每个线程逻辑都是执行上述serverSock的accept方法

没有使用Selector来执行accept操作,可以多线程并发执行上述serverSock的accept方法。

一旦使用了Selector,基本上就相当于将ServerSocketChannel serverSock绑定到了Selector所在线程上了(Selector不是线程安全的,只能在一个线程中被调度执行)

4 后续

下一篇就要详细描述下EventLoopGroup了。

欢迎关注微信公众号:乒乓狂魔

微信公众号

© 著作权归作者所有

共有 人打赏支持
乒乓狂魔
粉丝 971
博文 105
码字总数 271356
作品 0
长宁
程序员
源码之下无秘密 ── 做最好的 Netty 源码分析教程

背景 在工作中, 虽然我经常使用到 Netty 库, 但是很多时候对 Netty 的一些概念还是处于知其然, 不知其所以然的状态, 因此就萌生了学习 Netty 源码的想法. 刚开始看源码的时候, 自然是比较痛苦...

永顺 ⋅ 2017/11/29 ⋅ 0

MQTT---HiveMQ源码详解(一)概览

源博客地址:http://blog.csdn.net/pipinet123 MQTT交流群:221405150 面向群体 想自己实现MQTT Broker的朋友 对现有开源的MQTT Broker或多或少有些不满意的朋友 简单介绍 HiveMQ是企业级MQTT ...

pipinet123 ⋅ 2017/03/07 ⋅ 0

Netty 源码分析之 三 我就是大名鼎鼎的 EventLoop(一)

目录 源码之下无秘密 ── 做最好的 Netty 源码分析教程 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 简介 Java NIO 的前生今世 之二 NIO Channel 小结 Java NIO...

永顺 ⋅ 2017/11/29 ⋅ 0

Netty 源码分析之 二 贯穿Netty 的大动脉 ── ChannelPipeline (一)

目录 源码之下无秘密 ── 做最好的 Netty 源码分析教程 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 简介 Java NIO 的前生今世 之二 NIO Channel 小结 Java NIO...

永顺 ⋅ 2017/11/29 ⋅ 0

Netty 源码分析之 二 贯穿Netty 的大动脉 ── ChannelPipeline (二)

目录 源码之下无秘密 ── 做最好的 Netty 源码分析教程 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 简介 Java NIO 的前生今世 之二 NIO Channel 小结 Java NIO...

永顺 ⋅ 2017/11/29 ⋅ 0

Netty 源码分析之 一 揭开 Bootstrap 神秘的红盖头 (客户端)

目录 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 简介 Java NIO 的前生今世 之二 NIO Channel 小结 Java NIO 的前生今世 之三 NIO Buffer 详解 Java NIO 的前生...

永顺 ⋅ 2017/11/29 ⋅ 0

《深入探索Netty原理及源码分析》文集小结

写在2017年末尾,翻看文集的第一篇文章已经是三个月前的事了,也没想过这文集会写那么久,这么慢。。。 Netty文集中的文章主要都是我学习过程的笔记,写博客的主要目的是为了通过输出来倒逼输...

tomas家的小拨浪鼓 ⋅ 2017/12/30 ⋅ 0

dubbo中的那些“坑"(3)-netty4-rpc网络接口中的高并发的bug

在几个月前改造dubbo时,netty4已经稳定很久了,一时手痒,按照netty3-rpc的源码克隆了一套netty4,在修正了大量的包、类型不同之后,基本保持了netty3的风格,并发量小或者数据包很小时,一...

阿阮 ⋅ 2014/12/02 ⋅ 10

Netty 源码分析之 三 我就是大名鼎鼎的 EventLoop(二)

接上篇Netty 源码分析之 三 我就是大名鼎鼎的 EventLoop(一) Netty 的 IO 处理循环 接下来我们先从 IO 操纵方面入手, 看一下 TCP 数据是如何从 Java NIO Socket 传递到我们的 handler 中的....

永顺 ⋅ 2017/11/29 ⋅ 0

Netty 源码分析之 一 揭开 Bootstrap 神秘的红盖头 (服务器端)

目录 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 简介 Java NIO 的前生今世 之二 NIO Channel 小结 Java NIO 的前生今世 之三 NIO Buffer 详解 Java NIO 的前生...

永顺 ⋅ 2017/11/29 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

对于程序员的招聘问题,作为软件人的一些吐槽和建议

作为软件人,找工作有时候似乎挺苦逼的。 说真的,让我去掉前面这句中“似乎”二字吧。就是苦逼!很多人都曾抱怨处在招聘的一方很糟糕——我们没有任何可靠的方式来甄别会写代码并且写得好的...

老道士 ⋅ 35分钟前 ⋅ 0

HDFS原理学习

一、概述 1、 Hadoop整合了众多的文件系统,首先提供了一个高层的文件系统抽象org.apache.hadoop.fs.FileSystem。然后有各个文件系统的实现类。 2、Hadoop是JAVA编写的,不同文件系统之间的交...

cjxcloud ⋅ 39分钟前 ⋅ 0

Linux下MySQL表名不区分大小写的设置方法(抄袭别人的)

Linux下MySQL表名不区分大小写的设置方法 MySQL表名不区分大小写的设置方法 在用centox安装mysql后,把项目的数据库移植了过去,发现一些表的数据查不到,排查了一下问题,最后发现是表名的大...

随风而浮沉 ⋅ 44分钟前 ⋅ 0

ubuntu下安装宋体simsun

sudo cp simsun.ttc /usr/share/fonts cd /usr/share/fonts sudo chmod 644 simsun.ttc 更新字体缓存: 代码: sudo mkfontscale 代码: sudo mkfontdir 代码: sudo fc-cache -fsv 安装chrome扩......

wangxuwei ⋅ 45分钟前 ⋅ 0

利用 ssh 传输文件

Linux 下一般可以用 scp 命令通过 ssh 传送文件: #把服务器上的 /home/user/a.txt 发送到本机的 /var/www/local_dir 目录下scp username@servername:/home/user/a.txt /var/www/local_dir...

大灰狼时间 ⋅ 55分钟前 ⋅ 0

web3j教程:android和java程序员如何使用web3j开发区块链以太坊

如何使用web3j为Java应用或Android App增加以太坊区块链支持,本教程内容即涉及以太坊中的核心概念,例如账户管理包括账户的创建、钱包创建、交易转账,交易与状态、智能合约开发与交互、过滤...

智能合约 ⋅ 今天 ⋅ 0

web3j开发java或android以太坊智能合约快速入门

web3j简介 web3j是一个轻量级、高度模块化、响应式、类型安全的Java和Android类库提供丰富API,用于处理以太坊智能合约及与以太坊网络上的客户端(节点)进行集成。 可以通过它进行以太坊区块链...

笔阁 ⋅ 今天 ⋅ 0

一起读书《深入浅出nodejs》-异步I/O

异步I/O “异步”这个名词其实很早就诞生了,但它大规模流行却是在Web 2.0浪潮中,它伴随着AJAX的第一个A(Asynchronous)席卷了Web。 为什么要异步I/O 关于异步I/O为何在Node里如此重要,这与...

小草先森 ⋅ 今天 ⋅ 0

JVM各种问题

1、如果启动什么都不设,会怎样? 先来看一个命令 [root@localhost bin]# java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=29899008 -XX:MaxHeapSize=478384128 -XX:+PrintCo......

算法之名 ⋅ 今天 ⋅ 0

SAS笔记-宏2

宏是一种文本,一般来说其编译是在程序执行之前。 宏变量的创建 %let语句 %let macro_variables = text; %let是常见的宏变量建立方式,其编译就在执行前。如下例中,想要宏变量test等于数据集...

tonorth123 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部