博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Netty3之ServerBootstrap分析
阅读量:6989 次
发布时间:2019-06-27

本文共 6795 字,大约阅读时间需要 22 分钟。

  hot3.png

概述

ServerBootstrap是Netty提供的一个服务端工具类,通过设置ChanneFactory,ChannelPipelineFactory,用户可以很方便的启动一个服务端。

ServerBootstrap是做什么的

ServerBootstrap是一个帮助类,用来创建服务端的Channel以监听来自客户端的连接。

面向连接

ServerBootstrap只支持面向连接的传输,比如TCP/IP,如果是没有连接的传输,比如UDP/IP这样,就需要使用ConnectionlessBootstrap。

Parent and Child Channel

ServerBootstrap通过bind()方法使用属性ChannelFactory来创建服务端的Channel,以监听并且accept客户端的连接。服务端的Channel被称为parent Channel,来自客户端的连接被称为child Channel。

配置Channel

使用Options来设置parent and child Channel的可选参数,其中child Channel需要使用child.前缀。

ServerBootstrap b = ...;// Options for a parent channelb.setOption("localAddress", new InetSocketAddress(8080));b.setOption("reuseAddress", true);// Options for its childrenb.setOption("child.tcpNoDelay", true);b.setOption("child.receiveBufferSize", 1048576);

设置Channel Pipeline

Netty使用ChannelPipeline来处理和流转IO事件。parent Channel的Pipeline是由ServerBootstrap内部创建的,并且加入Binder的内部类作为ChannelHandler。我们可以设置ServerBootstrap的属性parentHandler,一旦设置之后parentHandler就会被追加到parent Pipeline中用来我们自定义的ChannelHandler。

child Pipeline有两种方式可以设置,一种方式是通过setPipelineFactory(ChannelPipelineFactory)设置,每个child Channel都会通过ChannelPipelineFactory创建新的ChannelPipeline,是推荐的方式。另一种方式通过setPipeline(ChannelPipeline)设置,这个方法内部会创建ChannelPipelineFactory,复用设置的ChannelPipeline的内部ChannelHandler。

不同配置,不同Channel

ServerBootstrap本身并不是占用资源,而是交给ChannelFactory来处理,官网文档说可以使用同一个ChannelFactory来创建多个ServerBootstrap,每个ServerBootstrap都可以使用不同的配置,从而创建不同的Channel(没见过这种需要。)

ServerBootstrap创建和启动过程

Netty为我们提供ServerBootstrap作为工具来方便的启动服务端,使用起来也很简单,一般分为这几步:

  • 创建ChannelFactory
  • 创建ChannelPipelineFactory
  • 创建ServerBootstrap实例、设置ChannelFactory和ChannelPipelineFactory
  • 调用bind方法

1、创建ChannelFactory

这个ChannelFactory在启动时候会创建parent Channel也就是服务的Channel以监听来自客户端的连接。

ChannelFactory是用来创建parent Channel

2、创建ChannelPipelineFactory

在accept Channel之后会使用这个ChannelPipelineFactory创建ChannelPipeline用来处理IO事件。常见的使用方法是使用Channels.pipeline()创建一个DefaultChannelPipeline,我们往里添加自定义的ChannelHandler即可。

ChannelPipelineFactory是用来服务child Channel

3、设置ChannelFactory和ChannelPipelineFactory

创建ServerBootstrap实例,设置ChannelFactory和ChannelPipelineFactory属性。

4、调用bind()方法启动,绑定在指定端口。

官网示例(也是常用使用方式,Dubbo就是这样的,设计的简约而不简单)

// Configure the server.ServerBootstrap bootstrap = new ServerBootstrap(        new NioServerSocketChannelFactory(                Executors.newCachedThreadPool(),                Executors.newCachedThreadPool()));// Set up the pipeline factory.bootstrap.setPipelineFactory(new ChannelPipelineFactory() {    public ChannelPipeline getPipeline() throws Exception {        return Channels.pipeline(new EchoServerHandler());    }});// Bind and start to accept incoming connections.bootstrap.bind(new InetSocketAddress(port));

bind()绑定过程

ServerBootstrap通过调用bind()方法来启动服务端。绑定方法很简单,首先创建parent的ChannelPipeline,然后使用ChannelFactory创建Channel,结束。

public Channel bind(final SocketAddress localAddress) {    ChannelFuture future = bindAsync(localAddress);    // Wait for the future.    future.awaitUninterruptibly();    if (!future.isSuccess()) {        future.getChannel().close().awaitUninterruptibly();        throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());    }    return future.getChannel();}public ChannelFuture bindAsync(final SocketAddress localAddress) {    if (localAddress == null) {        throw new NullPointerException("localAddress");    }    // 设置bossPipeline    Binder binder = new Binder(localAddress);    ChannelHandler parentHandler = getParentHandler();    ChannelPipeline bossPipeline = pipeline();    bossPipeline.addLast("binder", binder);    if (parentHandler != null) {        bossPipeline.addLast("userHandler", parentHandler);    }    // 创建parent Channel    Channel channel = getFactory().newChannel(bossPipeline);    final ChannelFuture bfuture = new DefaultChannelFuture(channel, false);    binder.bindFuture.addListener(new ChannelFutureListener() {        public void operationComplete(ChannelFuture future) throws Exception {            if (future.isSuccess()) {                bfuture.setSuccess();            } else {                // Call close on bind failure                bfuture.getChannel().close();                bfuture.setFailure(future.getCause());            }        }    });    return bfuture;}

分析方法过程:

  1. 创建parent ChannelPipeline,添加Binder、外部设置的parentChannelHandler,这个Pipeline是给parent Channel服务的
  2. 使用ChannelFactory创建Channel,是parent Channel

Binder类是ServerBootstrap内部类,是一个UpstreamHandler,处理三个事件:channelOpen、childChannelOpen、exceptionCaught。

NioServerSocketChannelFactory创建NioServerSocketChannel,在NioServerSocketChannel的构造函数中会触发一个ChannelOpen的事件传入Pipeline中,这个Pipeline就是在ServerBootstrap的bind()方法里创建的parent Pipeline。而ChannelOpen是一个UpstreamEvent,因此Binder类就会执行相应的逻辑。

关键代码

// NioServerSocketChannel的构造函数中执行socket = ServerSocketChannel.open();socket.configureBlocking(false);fireChannelOpen(this);		// Binder类的channelOpen方法,最后执行channel的bind方法evt.getChannel().bind(localAddress).addListener(new ChannelFutureListener() {    public void operationComplete(ChannelFuture future) throws Exception {        if (future.isSuccess()) {            bindFuture.setSuccess();        } else {            bindFuture.setFailure(future.getCause());        }    }});// NioServerSocketChannel的bind方法channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(channel, future, ChannelState.BOUND, localAddress));// NioServerSocketPipelineSink,调用NioServerBoss的bind方法case BOUND:    if (value != null) {        ((NioServerBoss) channel.boss).bind(channel, future, (SocketAddress) value);    } else {        ((NioServerBoss) channel.boss).close(channel, future);    }    break;// NioServerBoss中创建RegisterTaskvoid bind(final NioServerSocketChannel channel, final ChannelFuture future,          final SocketAddress localAddress) {    registerTask(new RegisterTask(channel, future, localAddress));}// RegisterTask中执行nio的bind和注册到Selector,属性的nio知识点channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog());bound = true;future.setSuccess();fireChannelBound(channel, channel.getLocalAddress());channel.socket.register(selector, SelectionKey.OP_ACCEPT, channel);

流程分析:

  1. NioServerSocketChannel构造方法创建Java nio的ServerSocketChannel;
  2. NioServerSocketChannel构造方法中触发OPEN的UpstreamEvent;
  3. Binder类中channelOpen()方法中调用Channel的bind()方法;
  4. NioServerSocketChannel的bind()方法触发一个BOUND的DownstreamEvent;
  5. NioServerSocketPipelineSink,调用NioServerBoss的bind()方法;
  6. NioServerBoss中创建RegisterTask,RegisterTask是一个Runnable对象,run()方法中执行NIO的相关操作,真正处理IO的地方
  7. ServerSocketChannel的bind方法,ServerSocketChannel注册到Selector,事件是OP_ACCEPT

总体的看来,ServerBootstrap内部将任务交给了Binder和ChannelFactory,进一步说其实是Channel来处理。通过一系列的事件触发,最终调用JDK NIO的ServerSocketChannel完成启动并注册监听。

ServerBootstrap没做什么,都是ServerChannelFactory在处理,想要理清楚Netty的服务端,就得剖析NioServerSocketChannelFactory。

思考一下

为什么不直接在ServerBootstrap的bind方法里直接执行Channel的bind方法呢?我觉得是想把复杂的逻辑剥离出来到Binder这个内部类里。may be

相关阅读

转载于:https://my.oschina.net/cregu/blog/2874332

你可能感兴趣的文章
收集网址
查看>>
python文本操作
查看>>
本周学习小结(01/04 - 07/04)
查看>>
精选CSDN的ACM-ICPC博文
查看>>
C语言的面向对象技术(转)
查看>>
C/C++读写二进制文件
查看>>
经典SQL语句(转载)
查看>>
#从零开始学SWIFT2.0# NO.5 可选变量和集合
查看>>
处理矩阵的行列转换
查看>>
java返回一个简单的日历
查看>>
java中包的命令行(cmd)操作详解
查看>>
git中fatal: Authentication failed的问题
查看>>
数学建模排版中加页码与首页不加页码问题
查看>>
matlab-1
查看>>
C# 用POST提交json数据
查看>>
cpu亲和性绑定
查看>>
变量 、缓冲值 、编码
查看>>
20160408javaweb之JDBC ---PreparedStatement
查看>>
2018.11.03-dtoj-2092-交通 (traffic)
查看>>
内置模块(二)
查看>>