Netty 源码共读(一) 服务端启动流程
前言
作为一个基本没有怎么系统性探索过源码的人来说,借此活动机会,也来简单学一下源码的阅读。
文明和谐,不喜勿喷
文章导读
📖 阅读本文,你将跟随我一起了解到
- 克隆并运行测试张师傅给出的demo
- 通过示例代码探索 Netty 服务端的启动流程
运行示例demo
学习源码,必不可少的我们要从一个简单的demo来入手。这里我们直接使用领读员张师傅在活动的给出的代码
克隆项目并运行
克隆项目
git clone https://github.com/arthur-zhang/netty-study.git
或者直接在IDEA
中新建
运行项目
找到Myserver
,并运行该项目。
这个服务只是个服务端。我们使用张师傅推荐的工具来充当客户端进行测试,这里选择了nc
工具
Linux 下可以直接使用netcat命令。Windows下没有这个命令,需要自己安装。nc下载地址
解压文件夹,并将解压后的文件夹地址,加入到系统的环境变量即可。
至此,我们已经成功的运行该示例项目,并简单测试了代码的作用
一个简单的Echo 服务。返回向服务端发送的消息。
下面,就让我们通过该服务端示例代码,来探索下Netty服务端的启动流程
服务端启动流程探索
代码
public class MyServer {
public static void main(String[] args) throws InterruptedException {
//引导类 ①
ServerBootstrap serverBootstrap = new ServerBootstrap();
//IO模型 ②
serverBootstrap.channel(NioServerSocketChannel.class);
//临时存在已完成三次握手请求队列的的最大长度 ③
serverBootstrap.option(NioChannelOption.SO_BACKLOG, 511);
//开启 Nagle 算法
serverBootstrap.childOption(NioChannelOption.TCP_NODELAY, true);
//设置Log级别
serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
//监听端口,处理接收新的连接的 线程组 ④
NioEventLoopGroup bossGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("boss"));
//处理每一条连接的相关IO的线程组
NioEventLoopGroup workGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("worker"));
try {
// 配置两个主要的线程组 ⑤
serverBootstrap.group(bossGroup, workGroup);
//Netty对 NIO 类型连接的的抽象和处理。
final MyEchoServerHandler serverHandler = new MyEchoServerHandler();
serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ServerIdleCheckHandler()); //⑥
pipeline.addLast(new LoggingHandler(LogLevel.INFO));
pipeline.addLast(serverHandler);
}
});
//绑定启动端口 ⑦
ChannelFuture f = serverBootstrap.bind(8888).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
下面我们就通过这个代码,一步步的分析和了解他的过程。
ServerBootstrap
在代码的一开始,我们先定义了一个ServerBootstrap
类。这个类的主要作用是来 完成服务器端的 Netty 初始化。
.channel 设置netty的ServerSocketChannel模型
在②中,我们设置了
serverBootstrap.channel(NioServerSocketChannel.class);
从源码io.netty.bootstrap.AbstractBootstrap#channel
看该方法的调用
该
channel
方法,创建了并返回了一个channelFactory
的工厂实例ReflectiveChannelFactory
。咋启动的时候通过这个去创建相应的channel
实例。
常用的类型
- NioSocketChannel/OioSocketChannel : 用于客户端的异步非阻塞/同步阻塞类型
- NioServerSocketChannel/OioServerSocketChannel : 用于服务端的异步非阻塞/同步阻塞模型
.option 设置服务端接受TCP连接的相关设置
在③的示例代码中NioChannelOption.SO_BACKLOG
,这个参数设置了服务端接受连接的队列的最大值,
它其实对应的就是是tcp/ip
协议listen
函数中的backlog
参数,函数listen(int socketfd,int backlog)用来初始化服务端可连接队列。
因为服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小。当超过这个限制的时候,它就是拒绝客户端的连接。
当然,他不止这一个参数,还有SO_REUSEADDR
,SO_KEEPALIVE
等等。
.childOption 设置处理连接的线程的相关设置
相比较简单来说,.option
是给bossGroup
进行设置,那.childOption
就是给workGroup
就如例子中设置的TCP_NODELAY
,表示是否立即是否立即返回数据。它同样也拥有多种的设置,如SO_RCVBUF
设置缓冲区大小之类的。
两个NioEventLoopGroup线程组
- bossGroup :处理客户端连接的线程组
- workGroup :处理每一条连接相关IO事件的线程组
既然是线程组,那么它们默认包含了多少个线程呢。
示例代码④中,我们设置了0,从源码看
然后看它的this调用
继续看它的super
可以看到 当指定的nThreads
设置为 0 时,它会有一个默认值 DEFAULT_EVENT_LOOP_THREADS
,那么这个值是多少呢。
通过源码可以看到,默认的线程数是cpu核数的两倍。这个我们也可以通过查看我们的系统内核和代码来验证下
我的电脑cpu是12
核的
在代码中打个断点,我们可以看到 每个线程组里面默认初始化了24个线程。
.group
从源码看,启动类通过.group
,将bossGroup
和workGroup
设置给了ServerBootstrap
的parentGroup
和childGroup
。
我们上面的.option
和.childoption
就是分别对两个线程组对应进行设置的。
设置流水线处理器
在客户端连接之后,我们那要对其连接进行处理。那要怎么处理,由谁来处理呢。
从源码来看,传入的
childHandler
赋值给ServerBootstrap的childHandler
属性。我们通过设置channelHandler
来处理客户端的请求的channel
的IO
处理。
在⑥中,示例代码通过匿名内部类的方式实例化了一个ChannelInitializer
,并重写了其initChannel
f方法,在其中给流水线设置了我们自定义实现的处理器ServerIdleCheckHandler
和MyEchoServerHandler
。来实现我们消息返回处理。
.bind(8888).sync() 绑定端口,启动服务
这里是真正的服务启动的入口,提供用于服务端或者客户端绑定服务器地址和端口号,默认是异步启动。如果加上sync()
方法则是同步。
我们从源码一步步看他是如何启动的。
首先是调用bind()
。首先validate()
对相关参数进行了校验。
然后调用到dobind()
首先是在initAndRegister()
中,实例化了一个channel
,是NioServerSocketChannel.class
的一个实例,然后对其进行init(),设置我们在之前代码设置是属性。之后并将其进行注册register()
最后是调用
doBind0()
然后一步步追踪下去。最后在ServerSocketChannelImpl
中完成了地址和端口的绑定。
总结
以上就是通过活动的示例代码。来简单探索了一下服务端启动流程。并通过示例代码中的代码,简单了解了下所涉及的对象和一些常用的方法和熟悉。当然,这只是一个简单了解。只是自我探索的一个记录。非喜勿喷~~
最后
我是Ylimhs
,一个长期活跃于沸点的最佳摸鱼手,热爱Coding,喜欢分享,五湖四海皆兄弟,欢迎大家一起在沸点摸鱼ヾ(≧▽≦*)o
转载自:https://juejin.cn/post/7181017278039719991