自顶向下深入分析Netty(三)--Bootstrap
本文开始分析Netty的源码,由于目标是自顶向下分析,在这一节将分析Netty是如何构建起如上图所示的整体框架。首先将使用一个示例展示怎么使用Bootstarp构建服务端应用,然后将深入源码了解底层机制和原理。
1.使用示例
首先使用Netty构造如图所示的框架,源码如下:
// 指定mainReactor
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 指定subReactor
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 用户自定义的ThreadPool
EventExecutorGroup threadPool = new ThreadPool();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100) // 设置TCP参数
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(threadPool,
new DecoderHandler(), // 解码处理器
new ComputeHandler()); // 计算处理器
new EncoderHandler(), // 编码处理器
}
}); // 绑定到本地端口等待客户端连接
ChannelFuture f = b.bind(PORT).sync(); // 等待接受客户端连接的Channel被关闭
f.channel().closeFuture().sync();
} finally {
// 关闭两个线程组
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
threadPool.shutdown();
}
逐行分析代码,EventLoopGroup是Netty实现的线程池接口,两个线程池:bossGroup和workerGroup分别对应mainReactor和subReactor,其中boss专门用于接受客户端连接,worker也就是常说的IO线程专门用于处理IO事件。IO事件包括两类,一类如服务端接收到客户端数据的Read事件,另一类如用户线程主动向客户端发送数据的Write事件。在4.0版本中,用户自定义的业务线程池须实现EventExecutorGroup接口,4.1版本则可以直接使用JAVA自带的线程池。
为了帮助用户快速构建基于Netty的服务,Netty提供了两个启动器ServerBootstrap和Bootstrap,分别用于启动服务器端和客户端程序。group(EventLoopGroup...)方法用于指定一个或两个Reactor,本例中指定为两个。channel(Channel)方法本质用来指定一个Channel工厂,本例中该工厂生产服务端用于accept客户端连接的Channel,将默认使用Channel的无参构造方法。如果用户需要自定义有参数的Channel,可自定义所需的工厂实现。option(Key, Value)用于指定TCP相关的参数以及一些Netty自定义的参数。childHandler()用于指定subReactor中的处理器,类似的,handler()用于指定mainReactor的处理器,只是默认情况下mainReactor中已经添加了acceptor处理器,所以无需再指定。需要注意的是:这两个方法并不能累积调用而达到增加多个处理器的目的,所以引入了 ChannelInitializer,它是一个特殊的Handler,功能是初始化多个Handler,如本例中的DecoderHandler,ComputeHandler,EncoderHandler。完成初始化工作后,ChannelInitializer会从Handler链中删除。至此,如图所示的框架已经构建完毕。
最后临门一脚,bind(int)方法将服务端Channel绑定到本地端口,成功后将accept客户端的连接,从而是整个框架运行起来。使用sync()方法是由于Netty中的事件都是异步的,所以需要同步等待结果。准确的说,这个方法在这里使用是有问题的,sync()完成后只能表明绑定事件运行完毕,但并不能说明绑定成功,虽然失败的可能性微乎其微。
f.channel().closeFuture().sync()方法仅仅是为了使当前main线程阻塞而不立即执行之后的各种shutdown()方法,其语义是等到服务端接受客户端连接的Channel被关闭时,才执行后面代码的操作。在实际应用中,这样的代码并不实用,我们可能需要接受诸如kill命令后,优雅关闭线程组。
一些情况下,我们并不使用如图所示的结构,比如当业务逻辑都很简单,也就是如图所示的decode,compute,encode能在短时间完成(数十毫秒或更少),那么可以不使用业务线程池。代码也很简单,只需要改动ChannelInitializer即可:
b.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new DecoderHandler()); // 解码处理器
p.addLast(new ComputeHandler()); // 计算处理器
p.addLast(new EncoderHandler()); // 编码处理器
}
});
事实上这是Netty的默认方法,也就是说不在addLast(Handler)方法中指定线程池,那么将使用默认的subReacor即woker线程池也即IO线程池执行处理器中的业务逻辑代码。
又比如,如开始的例子只让IO线程池处理read,write等IO事件会觉得有点大材小用,于是将decode和encode交给IO线程处理,如果此时的compute查询需要数据库中的数据,那么代码可改动为如下:
b.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new DecoderHandler()); // 解码处理器
p.addLast(new EncoderHandler()); // 编码处理器
p.addLast(threadPool, new ComputeWithSqlHandler()); // 附带SQL查询的计算
}
});
最佳实践
简单快速的业务逻辑可由IO线程池执行,复杂耗时的业务(如查询数据库,取得网络连接等)使用新的业务逻辑线程池执行。
本文介绍了Bootstrap的使用,如果还想知道背后的原理,可移步后续文章:自顶向下深入分析Netty(三)--Bootstrap源码分析。
作者:Hypercube
链接:https://www.jianshu.com/p/e896c7f461b1
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
自顶向下深入分析Netty(三)--Bootstrap的更多相关文章
- 自顶向下深入分析Netty(六)--Channel总述
自顶向下深入分析Netty(六)--Channel总述 自顶向下深入分析Netty(六)--Channel源码实现 6.1 总述 6.1.1 Channel JDK中的Channel是通讯的载体,而N ...
- 自顶向下深入分析Netty(七)--ChannelPipeline和ChannelHandler总述
自顶向下深入分析Netty(七)--ChannelPipeline和ChannelHandler总述 自顶向下深入分析Netty(七)--ChannelPipeline源码实现 自顶向下深入分析Net ...
- 自顶向下深入分析Netty(五)--Future
再次回顾这幅图,在上一章中,我们分析了Reactor的完整实现.由于Java NIO事件驱动的模型,要求Netty的事件处理采用异步的方式,异步处理则需要表示异步操作的结果.Future正是用来表示异 ...
- netty之bootstrap
转载自https://blog.csdn.net/zxhoo/article/details/17419229 Netty4学习笔记(2)-- Bootstrap Netty4的代码比我想象的要复杂的 ...
- (三)Bootstrap.jar
catalina.bat 在最后启动了bootstrap.jar, 传递了start作为参数(如果多个参数的话,start在尾部). 然后org.apache.catalina.startup.Boo ...
- Netty(三) 什么是 TCP 拆、粘包?如何解决?
前言 记得前段时间我们生产上的一个网关出现了故障. 这个网关逻辑非常简单,就是接收客户端的请求然后解析报文最后发送短信. 但这个请求并不是常见的 HTTP ,而是利用 Netty 自定义的协议. 有个 ...
- 响应式开发(三)-----Bootstrap框架的安装使用
下载 Bootstrap 可以从http://getbootstrap.com/上下载 Bootstrap 的最新版本. Download Bootstrap:下载 Bootstrap.点击该按钮,您 ...
- netty(三)---NioEventLoop分析
问题 : NioEventLoop 作用到底是什么?是在哪里用到的? NioEventLoop 和我们开头创建的 ServerBootstrap 和 EventLoopGroup 是什么关系 ? Ni ...
- 【WebService】WebService之WSDL文档深入分析(三)
WSDL概念 WSDL(网络服务描述语言,Web Services Description Language)是一门基于 XML 的语言,用于描述 Web Services 以及如何对它们进行访问. ...
随机推荐
- adb shell get/setprop, setenforce...
adb shell getprop <key> 获取设备参数信息adb shell setprop <key> <value> 设置设备参数信息 例子1:>C ...
- java集合学习(2):Map和HashMap
Map接口 java.util 中的集合类包含 Java 中某些最常用的类.最常用的集合类是 List 和 Map. Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含 ...
- 排序算法的c++实现——堆排序
我们利用最大堆可以实现数组从小到大的原址排序,利用最小堆的可以实现对数组从大到小的原址排序. 1 二叉堆的简单介绍: 最大堆与最小堆可以当作通过数组来实现的一个完全二叉树,除了最底层之外其它层都是满 ...
- windows jupyter notebook 切换默认环境
windows jupyter notebook 切换默认环境 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog ...
- springboot+内置改为外置tomcat
1.pom.xml springboot项目利用的是自己内置的tomcat,这边就是不依赖内置的tomcat,将其编译的作用域设置为provided <dependency> <gr ...
- springboot读取静态资源文件的方式
springboot的请求路径一般会经过Controller处理,但是静态资源文件在请求之后是直接返回的.这涉及到俩个配置项. spring.mvc.static-path-pattern=/** s ...
- Laravel实现用户的注册、登录
一.安装 Laravel(使用 Laravel5.5) 通过 Composer 创建项目 composer create-project --prefer-dist laravel/laravel s ...
- python中pymsql常用方法(1)
python中pymysql模块常用方法以及其使用 首先我们知道pymysql 是python中操作数据库的模块 使用步骤分为如下几步: 1.与数据库服务器建立链接 conn=pymysql.Co ...
- Mac原型动画设计软件Drama创建3D图层的注意事项,你知道吗?
Drama创建3D图层的注意事项:要跨层保留3D空间,可以使用组.它们具有保留子图层和嵌套组的3D空间的特殊功能. Drama支持通过在三维空间中定位和旋转2D图层来创建3D内容.要在3D空间中定位图 ...
- 离线环境下自动化部署python环境(含openssl)
遇到有项目要在内网环境下安装python项目,所以空余时写了自动化部署python环境和python项目的脚本,由于项目涉密,这里仅提供自动化部署python环境的shell脚本,包括openssl的 ...