自顶向下深入分析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 以及如何对它们进行访问. ...
随机推荐
- 编写可维护的JavaScript-随笔(七)
将配置数据从代码中分离出来 代码中有些数据有修改的可能,如果放在函数中的话后期修改的时候会带来一些不必要的风险 需要将配置数据从代码中抽取出来,如果配置数据多的话可以放入一个对象中,然后修改抽取出来的 ...
- ionic创建组件、页面或者过滤器
ionic可以直接 用命令来创建组件.页面或者过滤器. 在ionic项目根目录打开命令窗口.输入下列命令: ionic g page pageName //创建新页面 ionic g componen ...
- JavaScript 之 Date对象
Date对象 Date 是一个构造函数,首先要通过 new Date() 来创建实例对象,提供实例成员. 创建 Date 实例用来处理日期和时间.Date 对象基于 1970年1月1日(世界标准时间 ...
- ArrayList的subList方法带来的坑
最近在项目中遇到了一个问题,由一个对象序列化的结构,在反序列化时一直提示失败,真的百思不得其解啊.在对问题排查了好久之后,才发现是这个序列化的对象中的list调用了ArrayList的sublist方 ...
- Java JDBC结果集的处理
结果集指针的移动 while (resultSet.next()){ //...... } 指针最初指向第一条记录之前,next()是指向下一个位置,返回的是boolean值,true表示有内容(记录 ...
- Java 数组实例——将阿拉伯数字转换为最大写
题目:将阿拉伯数字转换为最大写,比如1234转换为壹仟贰佰叁拾肆. package my_package; public class Transform { private String[] arr1 ...
- DTC测试
DTC配置好后要在2台server之间测试下是否能使用. 1.在A台上建立ODBC的连接B. 控制面板→管理工具→ODBC Datat Source(32bit) 点击添加 选择SQL SERVER ...
- Odoo启动运行参数(script运行参数,不是运行配置文件)
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826315.html 一:启动选项用在哪里 如果你是用Pycharm进行odoo二次开发的话,可以通过 R ...
- Httpd服务入门知识-Httpd服务常见配置案例之修改监听的IP和Port
Httpd服务入门知识-Httpd服务常见配置案例之修改监听的IP和Port 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.查看默认配置 [root@node101.yinzh ...
- 接口测试“八重天”---RestAssured
要记住每一个对你好的人,因为他们本可以不那么做. ---久节奏,慢读书 一.什么是RestAssured 偶然在逛帖子的时候发现一个接口测试框架,觉得不错,学习学习. 官方地址:http://rest ...