netty

Netty源码全解

源码下载

百度一下 Netty 4.1.56.Final 源码下载,按照提示下载即可。这里由于节约时间,给您带来不便,敬请原谅。

阿里云镜像设置

将以下内容复制到settings.xml文件的<mirrors>标签下,然后进入IDEA,修改Maven的settings文件目录。

<mirror>
    <id>alimaven</id>
    <mirrorOf>central</mirrorOf>
    <name>aliyun maven</name>
    <url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</mirror>

导入源码

打开IDEA,左上角点击Open选择项目根目录。

编译源码

在idea的右上角点击运行配置,左上角一个+号,点开打开Maven,复制:clean compile -Dcheckstyle.skip=true 运行编译,出现错误没关系,能跑就行。

Netty初见

总体描述

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

Netty是一个异步事件驱动的网络应用 框架 ,快速开发可维护的高性能协议服务器和客户端。

详细描述

Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.

'Quick and easy' doesn't mean that a resulting application will suffer from a maintainability or a performance issue. Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols. As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise.

Netty是一个NIO客户端服务器框架,它支持快速、轻松地开发网络应用程序,如 协议 服务器和客户端。 它极大地简化了网络编程,如使用TCP和UDP套接字来构建服务器。

“快速和简单”并不意味着最终的应用程序将面临可维护性或性能问题。 Netty是根据从实现许多协议(如FTP、SMTP、HTTP和各种二进制和基于文本的遗留协议)中获得的经验精心设计出来的, 因此,Netty成功地找到了一种方法,在不妥协的情况下实现了简单的开发、性能、稳定性和灵活性。

特性

Ease of use(简单易用)、Performance(高性能)、Security(安全性T LS、SSL)、Community(社区服务)

设计

用于各种传输类型的统一API - 阻塞(BIO)和非阻塞(NIO)套接字

基于灵活和可扩展的事件驱动模型,该模型极大的解耦合,使开发者关注于业务而不是网络通讯模型与设计实现

高度可定制的线程模型——支持单个线程、一个或多个线程池模型

真正的无连接数据报套接字支持(从3.1开始)

简单例子

DiscardServer

Discards any incoming data。丢弃所有收到的数据。只关注主流程。流程如下:

  1. 通过系统变量检测是否开启了SSL,通常我们这里都没有开启,所以为false

  2. 通过系统变量获取端口号,默认值为8009

  3. 如果设置了,那就配置SSL。该上下文默认为空

  4. 创建主(boss)事件循环组(为什么叫事件循环组?因为这里翻译:Event Loop Group)

  5. 创建从(worker)事件循环组

  6. 创建了一个Server启动器

  7. 将上面的两个事件循环组对象放入server启动器中

  8. 设置服务端的ServerSocket通道实现的类为NioServerSocketChannel

  9. 设置处理ServerSocket通道接收到的客户端事件处理器,也即打日志处理器

  10. 设置接收到socket客户端请求的处理器

  11. 如果设置了SSL那么设置这个处理器,当然这里默认为空,所以等于不实现向流水线的末尾添加一个自定义的数据处理器,也即Discard丢弃服务处理器

  12. 绑定端口并开始接受传入的连接

  13. 等待直到服务器套接字关闭。 在本例中,不会发生这种情况,但您可以这样做以优雅地关闭服务器。

  14. 优雅的关闭主、从事件循环组

public final class DiscardServer {
    // 通过系统变量检测是否开启了SSL,通常我们这里都没有开启,所以为false
    static final boolean SSL = System.getProperty("ssl") != null;
    // 通过系统变量获取端口号,默认值为8009
    static final int PORT = Integer.parseInt(System.getProperty("port", "8009"));

    public static void main(String[] args) throws Exception {
        // 如果设置了,那就配置SSL。该上下文默认为空
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }
        // 创建主(boss)事件循环组(为什么叫事件循环组?因为这里翻译:Event    Loop    Group)
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // 创建从(worker)事件循环组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // 创建了一个Server启动器
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup) // 将上面的两个事件循环组对象放入server启动器中
                .channel(NioServerSocketChannel.class) // 设置服务端的ServerSocket通道实现的类为NioServerSocketChannel
                .handler(new LoggingHandler(LogLevel.INFO)) // 设置处理ServerSocket通道接收到的客户端事件处理器,也即打日志处理器
                .childHandler(new ChannelInitializer<SocketChannel>() { // 设置接收到socket客户端请求的处理器
                    @Override
                    public void initChannel(SocketChannel ch) { // 将处理器放入Socket通道对象的通道流水线对象中处理
                        ChannelPipeline p = ch.pipeline();
                        if (sslCtx != null) {
                            // 如果设置了SSL那么设置这个处理器,当然这里默认为空,所以等于不实现
                            p.addLast(sslCtx.newHandler(ch.alloc()));
                        }
                        // 向流水线的末尾添加一个自定义的数据处理器,也即Discard丢弃服务处理器
                        p.addLast(new DiscardServerHandler());
                    }
                });

            // 绑定端口并开始接受传入的连接
            ChannelFuture f = b.bind(PORT).sync();

            // 等待直到服务器套接字关闭。 在本例中,不会发生这种情况,但您可以这样做以优雅地关闭服务器。 
            f.channel().closeFuture().sync();
        } finally {
            // 优雅的关闭主、从事件循环组
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

DiscardServerHandler

DiscardServer的处理器。流程如下:

  1. channelRead0为通道读取到数据的时候回调的方法,这里什么也不做,只是把收到的数据discard,也即丢弃掉

  2. exceptionCaught为发生异常时回调方法

public class DiscardServerHandler extends SimpleChannelInboundHandler<Object> {

    // 通道读取到数据的时候回调的方法
    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        // discard 丢弃数据
    }
	
    // 发生异常时,将会回调这个方法
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // Close the connection when an exception is raised. 当引发异常时关闭连接。 
        cause.printStackTrace(); // 打印错误日志到控制台
        ctx.close(); // 关闭连接
    }
}

DiscardClient

Keeps sending random data to the specified address。持续发送随机数据到指定的地址。流程如下:

  1. 通过系统变量检测是否开启了SSL,通常我们这里都没有开启,所以为false

  2. 通过系统变量获取主机字符串,通常我们这里都没有指定,所以为默认值"127.0.0.1"字符串

  3. 通过系统变量获取端口号,通常我们这里都没有指定,所以为默认值8009端口号

  4. 通过系统变量获取发送数据大小,通常我们这里都没有指定,所以为默认值256

  5. 配置安全上下文,这里忽略即可

  6. 创建一个事件循环组

  7. 创建一个客户端启动器

  8. 将事件循环组放入客户端启动器中

  9. 设置客户端NIO Socket处理器类名为NioSocketChannel

  10. 初始化通道处理器

  11. 获取到通道的流水线对象

  12. 如果配置SSL,那么设置SSL处理器

  13. 将自定义通道处理器放入流水线的末尾

  14. 尝试连接服务端

  15. 等待连接关闭

  16. 优雅的关闭事件循环组

public final class DiscardClient {
    // 通过系统变量检测是否开启了SSL,通常我们这里都没有开启,所以为false 
    static final boolean SSL = System.getProperty("ssl") != null;
    // 通过系统变量获取主机字符串,通常我们这里都没有指定,所以为默认值"127.0.0.1"字符串
    static final String HOST = System.getProperty("host", "127.0.0.1");
    // 通过系统变量获取端口号,通常我们这里都没有指定,所以为默认值8009端口号
    static final int PORT = Integer.parseInt(System.getProperty("port", "8009"));
    // 通过系统变量获取发送数据大小,通常我们这里都没有指定,所以为默认值256
    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));

    public static void main(String[] args) throws Exception {
        // 配置安全上下文,这里忽略即可
        final SslContext sslCtx;
        if (SSL) {
            sslCtx = SslContextBuilder.forClient()
                .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
        } else {
            sslCtx = null;
        }
        // 创建一个事件循环组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            // 创建一个客户端启动器
            Bootstrap b = new Bootstrap();
            b.group(group) // 将事件循环组放入客户端启动器中
                .channel(NioSocketChannel.class) // 设置客户端NIO Socket处理器类名为NioSocketChannel
                .handler(new ChannelInitializer<SocketChannel>() { // 初始化通道处理器
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline p = ch.pipeline(); // 获取到通道的流水线对象
                        if (sslCtx != null) {// 如果配置SSL,那么设置SSL处理器
                            p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
                        }
                        // 将自定义通道处理器放入流水线的末尾
                        p.addLast(new DiscardClientHandler());
                    }
                });
            // 尝试连接服务端
            ChannelFuture f = b.connect(HOST, PORT).sync();
            // 等待连接关闭
            f.channel().closeFuture().sync();
        } finally {
            // 优雅的关闭事件循环组
            group.shutdownGracefully();
        }
    }
}

 

DiscardClientHandler

废弃客户端处理器。流程如下:

  1. channelActive为通道激活的时候,回调该方法。在该方法中:

    1. 分配缓冲区

    2. 设置直接内存缓冲区大小为DiscardClient.SIZE

    3. 向缓冲区中写入0byte,大小为DiscardClient.SIZE

    4. 发送初始化后的数据

  2. channelInactive为通道变为不活跃时,也即关闭时回调方法,在该方法中,Server服务端应该什么也不发送,但如果它发送了什么,丢弃它。 直到连接关闭。

  3. exceptionCaught为通道发生了异常时调用,当发生任何异常时,打印异常信息并关闭连接

  4. generateTraffic将数据发送到服务端

  5. trafficGenerator为监听器对象,用于监听ChannelHandlerContext上下文操作

public class DiscardClientHandler extends SimpleChannelInboundHandler<Object> {
    // 内容字节缓冲区
    private ByteBuf content;
    private ChannelHandlerContext ctx;

    // 当通道激活的时候,回调该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        this.ctx = ctx;
        // 初始化消息
        content = ctx.alloc(). // 分配缓冲区
            directBuffer(DiscardClient.SIZE). // 设置直接内存缓冲区大小为DiscardClient.SIZE
            writeZero(DiscardClient.SIZE);  // 向缓冲区中写入0byte,大小为DiscardClient.SIZE
        // 发送初始化后的数据
        generateTraffic();
    }

    // 当通道变为不活跃时,也即关闭时回调方法
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        content.release(); // 释放分配的缓冲区对象
    }

    // 当读取到服务端发送的数据时调用
    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        // Server服务端应该什么也不发送,但如果它发送了什么,丢弃它。 直到连接关闭。
    }

    // 当发生了异常时调用
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 当发生任何异常时,关闭连接
        cause.printStackTrace(); // 打印异常信息
        ctx.close(); // 关闭连接
    }

    long counter; // 计时器

    private void generateTraffic() { // 生成传输数据
        // 将输出缓冲区刷新到套接字。
        // 一旦刷新,将再次产生相同数量的流量。
        ctx.writeAndFlush(content.retainedDuplicate()). // 将数据写入socket中并且刷出到服务端
            addListener(trafficGenerator); // 添加监听器对象
    }

    // 监听器对象,用于监听ChannelHandlerContext上下文操作
    private final ChannelFutureListener trafficGenerator = new ChannelFutureListener() {
        // 当操作完成时,将会调用该方法
        @Override
        public void operationComplete(ChannelFuture future) { 
            if (future.isSuccess()) {
                // 如果future已经完成,那么生成新的数据
                generateTraffic();
            } else {
                // 异常完成,那么打印异常信息,并且关闭通道
                future.cause().printStackTrace();
                future.channel().close();
            }
        }
    };
}

 

Netty Http用例

HttpHelloWorldServer

  1. 通过系统变量检测是否开启了SSL,通常我们这里都没有开启,所以为false

  2. 通过系统变量获取端口号,默认值为8080,如果开启了SSL,那么端口号默认为8443

  3. 如果设置了,那就配置SSL。该上下文默认为空

  4. 创建主(boss)事件循环组(为什么叫事件循环组?因为这里翻译:Event Loop Group)

  5. 创建从(worker)事件循环组

  6. 创建了一个Server启动器

  7. 将上面的两个事件循环组对象放入server启动器中

  8. 设置Server通道的BACKLOG 1024(backlog为:请求的传入连接队列的最大长度,详细描述见:ServerSocket类的第164行)

  9. 设置服务端的ServerSocket通道实现的类为NioServerSocketChannel

  10. 设置处理ServerSocket通道接收到的客户端事件处理器,也即打日志处理器

  11. 设置接收到socket客户端请求的处理器,这里为自定义的HttpHelloWorldServerInitializer类

  12. 绑定端口并开始接受传入的连接

  13. 等待直到服务器套接字关闭。 在本例中,不会发生这种情况,但您可以这样做以优雅地关闭服务器。

  14. 优雅的关闭主、从事件循环组

public final class HttpHelloWorldServer {
    // 通过系统变量检测是否开启了SSL,通常我们这里都没有开启,所以为false 
    static final boolean SSL = System.getProperty("ssl") != null;
    // 通过系统变量获取端口号,默认值为8080,如果开启了SSL,那么端口号默认为8443
    static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080"));

    public static void main(String[] args) throws Exception {
        // 配置SSL上下文,这里忽略即可
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }
        // 配置服务器
        // 创建主(boss)事件循环组(为什么叫事件循环组?因为这里翻译:Event    Loop    Group)
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // 创建从(worker)事件循环组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // 创建了一个Server启动器
            ServerBootstrap b = new ServerBootstrap();
            b.option(ChannelOption.SO_BACKLOG, 1024); // 设置Server通道的BACKLOG 1024(backlog为:请求的传入连接队列的最大长度,详细描述见:ServerSocket类的第164行)
            b.group(bossGroup, workerGroup) // 将上面的两个事件循环组对象放入server启动器中
                .channel(NioServerSocketChannel.class) // 设置服务端的ServerSocket通道实现的类为NioServerSocketChannel
                .handler(new LoggingHandler(LogLevel.INFO)) // 设置处理ServerSocket通道接收到的客户端事件处理器,也即打日志处理器
                .childHandler(new HttpHelloWorldServerInitializer(sslCtx)); // 设置接收到socket客户端请求的处理器,这里为自定义的HttpHelloWorldServerInitializer类
            // 绑定端口并开始接受传入的连接
            Channel ch = b.bind(PORT).sync().channel();
            // 输出红色的信息,因为这里为err,err在控制台上会输出红色的字符
            System.err.println("Open your web browser and navigate to " +
                               (SSL? "https" : "http") + "://127.0.0.1:" + PORT + '/');
            // 等待直到服务器套接字关闭。 在本例中,不会发生这种情况,但您可以这样做以优雅地关闭服务器。
            ch.closeFuture().sync();
        } finally {
            // 优雅的关闭主、从事件循环组
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

HttpHelloWorldServerInitializer

用于初始化SocketChannel通道对象,设置它的流水线。流程如下:

  1. 获取到与Channel绑定的流水线对象

  2. 设置SSL上下文(由于我们不讨论SSL相关的,所以我们这里忽略掉即可)

  3. 将Http Server 解码器对象放入流水线中

  4. 将Http Server 异常处理对象放入流水线中

  5. 将自定义的Http HelloWorld 处理器对象放入流水线中

 

public class HttpHelloWorldServerInitializer extends ChannelInitializer<SocketChannel> {

    private final SslContext sslCtx;

    public HttpHelloWorldServerInitializer(SslContext sslCtx) {
        this.sslCtx = sslCtx;
    }

    @Override
    public void initChannel(SocketChannel ch) {
        // 获取到与Channel绑定的流水线对象
        ChannelPipeline p = ch.pipeline();
        if (sslCtx != null) {
            // 设置SSL上下文(由于我们不讨论SSL相关的,所以我们这里忽略掉即可)
            p.addLast(sslCtx.newHandler(ch.alloc()));
        }
        // 将Http Server 解码器对象放入流水线中
        p.addLast(new HttpServerCodec());
        // 将Http Server 异常处理对象放入流水线中
        p.addLast(new HttpServerExpectContinueHandler());
        // 将自定义的Http HelloWorld 处理器对象放入流水线中
        p.addLast(new HttpHelloWorldServerHandler());
    }
}

HttpHelloWorldServerHandler

从上面可以看到,该处理器放入在ChannelPipeline的后面。流程如下:

  1. 定义一个常量字节数组,里面包括了写入到客户端的内容体,为字符类型

  2. channelReadComplete为在通道读取完成时调用

  3. channelRead0为该方法在通道内容可读时调用。处理流程如下:

    1. 判断消息体是否为HttpRequest的实例

    2. 强转该实例

    3. 判断请求是否为keep-alive的头部

    4. 创建http响应对象FullHttpResponse

    5. 设置HTTP头部信息

    6. 如果使用了keepalive保活机制,那么设置Connection头部,否则告诉客户端我们要关闭连接,也即设置Connection头部为Close

    7. 向CTX中写入响应信息

    8. 如果没有使用KeepAlive操作,那么添加一个监听器,当操作完成时关闭客户端连接

  4. exceptionCaught为发生异常时调用该方法

public class HttpHelloWorldServerHandler extends SimpleChannelInboundHandler<HttpObject> {
    // 定义一个常量字节数组,里面包括了写入到客户端的内容体,为字符类型
    private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };

    // 该方法在通道读取完成时调用
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush(); // 将CTX中的内容刷出
    }

    // 该方法在通道内容可读时调用
    @Override
    public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
        // 判断消息体是否为HttpRequest的实例
        if (msg instanceof HttpRequest) {
            // 强转该实例
            HttpRequest req = (HttpRequest) msg;
            // 判断请求是否为keep-alive的头部(在早期的HTTP/1.0中,每次http请求都要创建一个连接,而创建连接的过程需要消耗资源和时间,为了减少资源消耗,缩短响应时间,就需要重用连接。在后来的HTTP/1.0中以及HTTP/1.1中,引入了重用连接的机制,就是在http请求头中加入 Connection: keep-alive 来告诉对方这个请求响应完成后不要关闭,下一次咱们还用这个请求继续交流。协议规定HTTP/1.0如果想要保持长连接,需要在请求头中加上Connection: keep-alive,而HTTP/1.1默认是支持长连接的,有没有这个请求头都行。当然了,协议是这样规定的,至于支不支持还得看服务器(比如tomcat)和客户端(比如浏览器)的具体实现。在实践过程中发现谷歌浏览器使用HTTP/1.1协议时请求头中总会带上Connection: keep-alive,另外通过httpclient使用HTTP/1.0协议去请求tomcat时,即使带上Connection: keep-alive请求头也保持不了长连接。如果HTTP/1.1版本的http请求报文不希望使用长连接,则要在请求头中加上Connection: close,接收到这个请求头的对端服务就会主动关闭连接。但是http长连接会一直保持吗?肯定是不会的。一般服务端都会设置keep-alive超时时间。超过指定的时间间隔,服务端就会主动关闭连接。同时服务端还会设置一个参数叫最大请求数,比如当最大请求数是300时,只要请求次数超过300次,即使还没到超时时间,服务端也会主动关闭连接。)
            boolean keepAlive = HttpUtil.isKeepAlive(req);
            // 创建http响应对象
            FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK,
                                                                    Unpooled.wrappedBuffer(CONTENT));
            // 设置HTTP头部信息
            response.headers()
                .set(CONTENT_TYPE, TEXT_PLAIN) // 设置MIME类型(MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。它是一个互联网标准,扩展了电子邮件标准,使其能够支持:非ASCII字符文本;非文本格式附件(二进制、声音、图像等);由多部分(multiple parts)组成的消息体;包含非ASCII字符的头信息(Header information))
                .setInt(CONTENT_LENGTH, response.content().readableBytes()); // 设置Content-Length头部(Content-Length用于描述HTTP消息实体的传输长度the transfer-length of the message-body。在HTTP协议中,消息实体长度和消息实体的传输长度是有区别,比如说gzip压缩下,消息实体长度是压缩前的长度,消息实体的传输长度是gzip压缩后的长度。在具体的HTTP交互中,客户端是如何获取消息长度的呢,主要基于以下几个规则:响应为1xx,204,304相应或者head请求,则直接忽视掉消息实体内容。如果有Transfer-Encoding,则优先采用Transfer-Encoding里面的方法来找到对应的长度。比如说Chunked模式。“如果head中有Content-Length,那么这个Content-Length既表示实体长度,又表示传输长度。如果实体长度和传输长度不相等(比如说设置了Transfer-Encoding),那么则不能设置Content-Length。如果设置了Transfer-Encoding,那么Content-Length将被忽视”。这句话翻译的优点饶,其实关键就一点:有了Transfer-Encoding,则不能有Content-Length。Range传输。不关注,没详细看了:)通过服务器关闭连接能确定消息的传输长度。(请求端不能通过关闭连接来指明请求消息体的结束,因为这样可以让服务器没有机会继续给予响应)。这种情况主要对应为短连接,即非keep-alive模式。)
            // 如果使用了keepalive保活机制,那么设置Connection头部
            if (keepAlive) {
                if (!req.protocolVersion().isKeepAliveDefault()) {
                    response.headers().set(CONNECTION, KEEP_ALIVE);
                }
            } else {
                // 否则告诉客户端我们要关闭连接,也即设置Connection头部为Close
                response.headers().set(CONNECTION, CLOSE);
            }
            // 向CTX中写入响应信息
            ChannelFuture f = ctx.write(response);
            if (!keepAlive) {
                // 如果没有使用KeepAlive操作,那么添加一个监听器,当操作完成时关闭客户端连接
                f.addListener(ChannelFutureListener.CLOSE);
            }
        }
    }

    // 发生异常时调用该方法
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

 

NioEventLoopGroup类

流程如下:

  1. 使用默认的线程数创建一个新实例,默认的{@link ThreadFactory}和 由{@link SelectorProvider#provider()}返回的{@link SelectorProvider}指定

  2. 创建一个新的实例,使用指定的线程数,其中{@link ThreadFactory}和 由{@link SelectorProvider#provider()}创建返回

  3. 创建Selector的Provider提供器

  4. 创建一个默认的选择策略工厂创建的实例

  5. 创建一个拒绝执行处理器

  6. 根据环境变量io.netty.eventLoopThreads来获取到线程数,如果没有指定,那么返回CPU的 (核心数 乘以 2) 和 1 的最大值,作为默认的线程数

  7. 如果nThreads为0,那么取默认的事件循环组的线程数,否则使用传递的线程数

  8. 创建了一个默认事件执行选择器工厂的实例

  9. 校验线程数不能小于等于0

  10. 如果执行器为空,那么创建ThreadPerTaskExecutor对象

  11. 创建事件执行器数组,数组大小为nThreads

  12. 遍历事件执行器数组

  13. 创建EventExecutor对象

  14. 设置保存成功

  15. 创建一个选择对象

  16. 创建一个终结监听器对象

  17. 遍历所有的EventExecutor对象,向他们添加终结监听器

  18. 将children数组中的EventExecutor对象放入LinkedHashSet集合

  19. 将该集合变为一个只读的Children集合

public class NioEventLoopGroup extends MultithreadEventLoopGroup {
    // 使用默认的线程数创建一个新实例,默认的{@link ThreadFactory}和 由{@link SelectorProvider#provider()}返回的{@link SelectorProvider}指定 
    public NioEventLoopGroup() {
        this(0); // 直接调用有参构造,默认为0
    }

    // 创建一个新的实例,使用指定的线程数,其中{@link ThreadFactory}和 由{@link SelectorProvider#provider()}创建返回
    public NioEventLoopGroup(int nThreads) {
        this(nThreads, (Executor) null);
    }

    // 这里提供了一个Selector的Provider提供器
    public NioEventLoopGroup(int nThreads, Executor executor) {
        this(nThreads, executor, SelectorProvider.provider()); 
    }

    // 这里提供了一个默认的选择策略工厂创建的实例
    public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) {
        this(nThreads, executor, selectorProvider,DefaultSelectStrategyFactory.INSTANCE);
    }

    // 这里提供了一个拒绝执行处理器
    public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) {
        super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
    }
}

// 这个类为NioEventLoopGroup的父类
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
    private static final int DEFAULT_EVENT_LOOP_THREADS; // 默认的线程数
    static {
        // 根据环境变量io.netty.eventLoopThreads来获取到线程数,如果没有指定,那么返回CPU的  (核心数 乘以 2) 和 1  的最大值,作为默认的线程数
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
            "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
    }
    // 如果nThreads为0,那么取默认的事件循环组的线程数,否则使用传递的线程数
    protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }
}

public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {
    // 事件执行器组
    private final EventExecutor[] children;
    // 选择对象
    private final EventExecutorChooserFactory.EventExecutorChooser chooser;
    // 这里呢传递了一个默认事件执行选择器工厂的实例
    protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
        this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
    }
 
    protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        if (nThreads <= 0) { // 校验线程数不能小于等于0
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }
        // 如果执行器为空,那么创建ThreadPerTaskExecutor对象
        if (executor == null) {
            executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
        }
        // 创建事件执行器数组,数组大小为nThreads
        children = new EventExecutor[nThreads];
        // 遍历事件执行器数组
        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                // 创建EventExecutor对象
                children[i] = newChild(executor, args);
                success = true; // 设置保存成功
            } catch (Exception e) {
                // 发生异常,那么抛出异常信息
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                // 如果没有创建成功
                if (!success) {
                    // 遍历children数组的所有的EventExecutor对象,调用他们的shutdownGracefully方法关闭
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdownGracefully();
                    }
                    // 遍历等待所有的EventExecutor关闭
                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
                            // Let the caller handle the interruption.
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }
        // 创建一个选择对象
        chooser = chooserFactory.newChooser(children);
        // 创建一个终结监听器对象
        final FutureListener<Object> terminationListener = new FutureListener<Object>() {
            @Override
            public void operationComplete(Future<Object> future) throws Exception {
                if (terminatedChildren.incrementAndGet() == children.length) {
                    terminationFuture.setSuccess(null);
                }
            }
        };

        // 遍历所有的EventExecutor对象,向他们添加终结监听器
        for (EventExecutor e: children) {
            e.terminationFuture().addListener(terminationListener);
        }
        // 将children数组中的EventExecutor对象放入LinkedHashSet集合
        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        // 将该集合变为一个只读的Children集合
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }
}

 问题:老师,eventexcutor与excutor什么关系?根据以下代码可知:EventExecutor继承自EventExecutorGroup接口,EventExecutorGroup接口继承自ScheduledExecutorService接口,ScheduledExecutorService接口继承自ExecutorService接口,ExecutorService接口继承自Executor,Executor为顶层接口。

public interface EventExecutor extends EventExecutorGroup {}
public interface EventExecutorGroup extends ScheduledExecutorService, Iterable<EventExecutor> {}
public interface ScheduledExecutorService extends ExecutorService {}
public interface ExecutorService extends Executor {}
public interface Executor {}

问题:老师,问下,你以前咋学?就这么学呀

问题:看源码吗?是的

ServerBootstrap类

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
    public ServerBootstrap() { }
}

option方法

流程如下:

  1. 判断传入的option不能为空

  2. 对options集合上锁

  3. 如果设置的值为空,那么表示移除该option

  4. 否则将该option和值放到options map中

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
    
    public <T> B option(ChannelOption<T> option, T value) {
        ObjectUtil.checkNotNull(option, "option"); // 判空
        synchronized (options) { // 上锁
            if (value == null) { // 如果设置的值为空,那么表示移除该option
                options.remove(option);
            } else {
                // 否则将该option和值放到options map中
                options.put(option, value);
            }
        }
        return self();
    }
}

group方法

流程如下:

  1. 初始化父类

  2. childGroup变量如果已经有值,那么抛出异常

  3. 判断传入的childGroup对象不能为空

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup); // 初始化父类
        if (this.childGroup != null) { // childGroup变量如果已经有值,那么抛出异常
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup"); // 判空
        return this;
    }
}

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    public B group(EventLoopGroup group) {
        ObjectUtil.checkNotNull(group, "group"); // 判空
        if (this.group != null) { // group变量如果已经有值,那么抛出异常
            throw new IllegalStateException("group set already");
        }
        this.group = group; // 初始化group
        return self();
    }
}

channel方法

流程如下:

  1. 直接调用channelFactory方法,创建了一个ReflectiveChannelFactory工厂对象,调用父类AbstractBootstrap的channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory)方法

  2. 对成员变量this.channelFactory进行判空操作

  3. 保存Channel工厂对象到this.channelFactory成员变量中

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> { 
    public B channel(Class<? extends C> channelClass) {
        // 直接调用channelFactory方法,创建了一个ReflectiveChannelFactory工厂对象
        return channelFactory(new ReflectiveChannelFactory<C>(
            ObjectUtil.checkNotNull(channelClass, "channelClass")
        ));
    }
}
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
        return channelFactory((ChannelFactory<C>) channelFactory);
    }
    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        ObjectUtil.checkNotNull(channelFactory, "channelFactory");
        if (this.channelFactory != null) { // 判空
            throw new IllegalStateException("channelFactory set already");
        }
        // 保存Channel工厂对象
        this.channelFactory = channelFactory;
        return self();
    }
}

问题:this去掉,可不可以?答:不可以的,因为this代表了当前对象的引用,而参数名与对象引用的实例变量发生了重名现象,需要使用this操作符来指明对象的成员变量。

handler方法

流程如下:

对传入的变量进行判空并且赋值给当前对象的handler成员变量

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    public B handler(ChannelHandler handler) {
        // 对传入的变量进行判空并且赋值给当前对象的handler成员变量
        this.handler = ObjectUtil.checkNotNull(handler, "handler");
        return self();
    }
}

childHandler方法

流程如下:

对传入的变量进行判空并且赋值给当前对象的childHandler成员变量

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> { 
    public ServerBootstrap childHandler(ChannelHandler childHandler) {
        // 对传入的变量进行判空并且赋值给当前对象的childHandler成员变量
        this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
        return this;
    }
}

 

问题:This后面的是当前类属性?答:不是,因为this是当前对象的引用,而后面紧跟的变量通常为实例变量,而不是类变量。

bind方法

流程如下:

  1. 直接调用bind方法,创建了一个InetSocketAddress实例

  2. 验证变量信息, 对localAddress进行判空

  3. 进行初始化并注册

  4. 获取到注册的通道

  5. 如果发生异常,那么返回

  6. 初始化并注册完成

  7. 此时,我们知道注册已经完成并且成功了

  8. 做进一步绑定操作

  9. 注册将在未来某个时间点完成,但只是以防万一它没有完成,进行异常检测

  10. 如果异常对象不为空,那么标识在EventLoop上注册失败,所以ChannelPromise在尝试访问该通道的EventLoop时直接失败,从而不会导致IllegalStateException,这时设置promise异常对象

  11. 注册成功,所以要设置正确的执行器

  12. 回调Promise的注册完成方法

  13. 做进一步的绑定操作

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    
    public ChannelFuture bind(int inetPort) {
        // 直接调用bind方法,创建了一个InetSocketAddress实例
        return bind(new InetSocketAddress(inetPort));
    }
    
    public ChannelFuture bind(SocketAddress localAddress) {
        validate(); // 验证变量信息
        return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));  // 对localAddress进行判空
    }
    
    // 目前在讲绑定操作
    private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister(); // 进行初始化并注册
        final Channel channel = regFuture.channel(); // 获取到注册的通道
        if (regFuture.cause() != null) { // 如果发生异常,那么返回
            return regFuture;
        }
        // 初始化并注册完成
        if (regFuture.isDone()) {
            // 此时,我们知道注册已经完成并且成功了
            ChannelPromise promise = channel.newPromise();
            // 做进一步绑定操作
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // 注册未来几乎总是已经完成,但只是以防万一它没有完成
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            // 添加一个通道Future监听器,也即Promise模式
            regFuture.addListener(new ChannelFutureListener() {
                // 当操作完成时回调该方法
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    // 获取到异常对象
                    Throwable cause = future.cause();
                    if (cause != null) { // 异常对象不为空
                        // 在EventLoop上注册失败,所以ChannelPromise在尝试访问该通道的EventLoop时直接失败,从而不会导致IllegalStateException。
                        promise.setFailure(cause); // 设置promise异常对象
                    } else {
                        // 注册成功,所以要设置正确的执行器  
                        // 看这个地址 https://github.com/netty/netty/issues/2586描述的bug问题
                        promise.registered(); // 回调Promise的注册完成方法
                        // 做进一步的绑定操作
                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }
}

问题:可以讲一下这些类组合起来要达成什么目标?以及这么写好在哪里?总体的设计思路?答:达成初始化并且绑定ServerSocket服务。这么写有助于解耦合提供给开发者便捷的配置方式。总体设计思路为构建者模式 + Promise + Future异步执行模式。

问题:promise干什么的?你也得说下具体的思想,要不然我们很懵逼?答,通过源码得知,Promise继承自Future接口,提供了监听器的支持,当操作完成时将会回调监听器。

// 特殊可写的{@link ChannelFuture}
public interface ChannelPromise extends ChannelFuture, Promise<Void> {
}
// 代表了一个异步{@link Channel} I/O操作的结果。Netty中的所有I/O操作都是异步的。 这意味着任何I/O调用将立即返回,而不能保证请求的I/O操作在调用结束时已经完成。 相反,您将获得一个返回的{@link ChannelFuture}实例,该实例向您提供关于I/O操作的结果或状态的信息。 {@link ChannelFuture}要么未完成(uncompleted)、要么已完成(completed)。  当I/O操作开始时,将创建一个新的future对象,该future对象最初状态为未完成——代表了它既没有成功,也没有失败,也没有被取消,因为I/O操作还没有完成。 如果I/O操作成功完成,那么这时可能失败,可能被取消,Future对象将被标记为完成,并带有更具体的信息,例如失败的原因。 请注意,即使失败和取消也属于完成状态。 
public interface ChannelFuture extends Future<Void> {}

// 继承自Future接口
public interface Promise<V> extends Future<V> {
    // 将此Future标记为成功,并通知所有监听器,如果已经成功或失败,它将抛出一个{@link IllegalStateException}。  
    Promise<V> setSuccess(V result);
    // 将此Future标记为成功,并通知所有监听器,返回是否成功
    boolean trySuccess(V result);
    // 将此Future标记为失败,并通知所有监听器
    Promise<V> setFailure(Throwable cause);

    // 将此Future标记为失败,并通知所有监听器,返回是否成功
    boolean tryFailure(Throwable cause);
    // 标记Future有可能被取消
    boolean setUncancellable();
    // 添加一个监听器
    Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
    // 添加一组监听器
    @Override
    Promise<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
    // 移除一个监听器
    @Override
    Promise<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);
    // 移除一组监听器
    @Override
    Promise<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
    // 等待完成
    @Override
    Promise<V> await() throws InterruptedException;
    // 不可中断等待完成
    @Override
    Promise<V> awaitUninterruptibly();
    // 异步转同步等待
    @Override
    Promise<V> sync() throws InterruptedException;
    // 异步转同步等待,不可中断返回
    @Override
    Promise<V> syncUninterruptibly();
}

问题:为什么bind操作,里面有chanel,又有promise,regfuture?答:因为在Netty中的所有I/O操作都是异步的。

建议:IDEA中按住鼠标中键选中选好选点,这时可以在翻译的时候不用删*号~

问题:ServerbootStrap是什么 类?答:ServerBootstrap为{@link Bootstrap}的子类,它允许Netty能够被简单地对{@link ServerChannel} 进行设置和使用。

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {}

 问题:和启动没 关系?答:根据使用方式来看,我们通过ServerBootstrap类进行了配置,然后调用了bind操作,答案是肯定有关系的,因为我们使用它启动了服务器。

// 该类的使用方式
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)
    .handler(new LoggingHandler(LogLevel.INFO))
    .childHandler(new HttpHelloWorldServerInitializer(sslCtx));
Channel ch = b.bind(PORT).sync().channel();
ch.closeFuture().sync(); // 此时服务器已经启动,所以等待关闭

 问题:channel是什么呢?答:channel为连接到网络套接字或组件的一种连接,它具有读、写、连接和绑定等操作。

public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparable<Channel> {}

 问题:就是socket吗?答:通过源码得知,该Channel不是Socket,为JDK的SelectableChannel对象。

protected ServerSocketChannel javaChannel() {
    return (ServerSocketChannel) super.javaChannel();
}

protected SelectableChannel javaChannel() {
    return ch;
}

private final SelectableChannel ch;

initAndRegister方法

流程如下:

  1. 通过channelFactory创建一个新的通道对象

  2. 初始化通道

  3. 注册通道

  4. 返回ChannelFuture regFuture

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        channel = channelFactory.newChannel(); // 创建一个新的通道对象
        init(channel); // 初始化通道
    } catch (Throwable t) {
        ...
    }
    // 注册通道
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) { // 发生异常
        if (channel.isRegistered()) {
            channel.close(); // 关闭通道
        } else {
            // 强制关闭
            channel.unsafe().closeForcibly();
        }
    }
    // 如果我们在这里,承诺没有失败,这是下列情况之一:  
    // 1)如果我们试图从事件循环中注册,那么注册已经在此时完成。  
    //例如,现在尝试bind()或connect()是安全的,因为通道已经注册了。  
    // 2)如果我们试图从其他线程注册,注册请求已经成功  
    //添加到事件循环的任务队列中,以便稍后执行。  
    //现在尝试bind()或connect()是安全的:  
    //因为bind()或connect()将在*计划注册任务执行后执行  
    //因为register(), bind()和connect()都绑定到同一个线程。  
    return regFuture;
}

ChannelFactory类

可以看到直接使用反射通过构造器来创建Channel对象。

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
    public T newChannel() {
        try {
            return constructor.newInstance(); // 直接使用构造器创建Channel实例
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);
        }
    }
}

 

NioServerSocketChannel类

由于我们传进的Channel实现类为io.netty.channel.socket.nio.NioServerSocketChannel类,所以我们这里关注该类即可。流程如下:

  1. 使用newSocket创建实例,使用DEFAULT_SELECTOR_PROVIDER默认选择器提供器对象

  2. 通过选择器提供器对象打开一个服务端套接字通道对象。ServerSocketChannel

  3. 创建NioServerSocketChannelConfig配置对象,并初始化父类

  4. 设置注册时的感兴趣事件集为OP_ACCEPT

  5. 保存通道对象

  6. 创建id

  7. 创建Unsafe对象

  8. 创建Channel Pipeline对象

  9. 保存感兴趣事件集

  10. 配置通道对象为非阻塞IO

  11. 如果发生配置异常,那么关闭打开的通道对象

public class NioServerSocketChannel extends AbstractNioMessageChannel implements io.netty.channel.socket.ServerSocketChannel {
    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
    public NioServerSocketChannel() {
        // 使用newSocket创建实例,使用DEFAULT_SELECTOR_PROVIDER默认选择器提供器对象
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }
    private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            // 通过选择器提供器对象打开一个服务端套接字通道对象。ServerSocketChannel
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException(
                "Failed to open a server socket.", e);
        }
    }

    // 创建NioServerSocketChannelConfig配置对象,并初始化父类
    public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT); // 设置注册时的感兴趣事件集为OP_ACCEPT
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }
}

public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
    protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        // 直接初始化父类
        super(parent, ch, readInterestOp);
    }
}

public abstract class AbstractNioChannel extends AbstractChannel {
    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent); // 初始化父类
        this.ch = ch; // 保存通道对象
        this.readInterestOp = readInterestOp; // 保存感兴趣事件集
        try {
            ch.configureBlocking(false); // 配置通道对象为非阻塞IO
        } catch (IOException e) {
            try {
                ch.close(); // 如果发生配置异常,那么关闭打开的通道对象
            } catch (IOException e2) {
                ... 
            }
            // 抛出配置NIO失败的异常信息
            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
}

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
    protected AbstractChannel(Channel parent) {
        this.parent = parent; // 保存通道对象
        id = newId(); // 创建id
        unsafe = newUnsafe(); // 创建Unsafe类
        pipeline = newChannelPipeline(); // 创建Channel Pipeline对象
    }
}

 

init方法

流程如下:

  1. 设置通道选项与属性

  2. 获取到通道流水线对象

  3. 获取到子事件循环组对象

  4. 获取到子通道处理器对象

  5. 获取子通道选项锁,获取到当前子通道的选项对象

  6. 将该选项对象转变为数组

  7. 向通道流水线对象中添加一个 ChannelInitializer对象,也即通道初始化对象

  8. 初始化通道

  9. 在初始化通道对象中:

    1. 获取到通道流水线对象

    2. 获取到通道处理器

    3. 处理器不为空,那么将其添加到流水线中

    4. 获取到通道的事件循环组,让其执行Runnable线程执行体,在该Runnable方法的run方法中,向流水线中添加了添加了ServerBootstrapAcceptor对象

 

abstract void init(Channel channel) throws Exception;

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
    void init(Channel channel) {
        // 设置通道选项与属性
        setChannelOptions(channel, newOptionsArray(), logger);
        setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
        // 获取到通道流水线对象
        ChannelPipeline p = channel.pipeline();
        // 获取到子事件循环组对象
        final EventLoopGroup currentChildGroup = childGroup;
        // 获取到子通道处理器对象
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        synchronized (childOptions) { // 获取子通道选项锁,获取到当前子通道的选项对象
            currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
        }
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY); // 将该选项对象转变为数组
        p.addLast(new ChannelInitializer<Channel>() { // 向通道流水线对象中添加一个 ChannelInitializer对象,也即通道初始化对象
            @Override
            public void initChannel(final Channel ch) { // 初始化通道
                final ChannelPipeline pipeline = ch.pipeline(); // 获取到通道流水线对象
                ChannelHandler handler = config.handler(); // 获取到通道处理器
                if (handler != null) { // 处理器不为空,那么将其添加到流水线中
                    pipeline.addLast(handler);
                }
                // 获取到通道的事件循环组,让其执行Runnable线程执行体
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() { // 在该方法中添加了ServerBootstrapAcceptor对象
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
包含最新版文档以及全部jar包: jar包如下 netty-buffer-4.1.32.Final-sources.jar netty-buffer-4.1.32.Final.jar netty-build-22-sources.jar netty-build-22.jar netty-codec-4.1.32.Final-sources.jar netty-codec-4.1.32.Final.jar netty-codec-http-4.1.32.Final-sources.jar netty-codec-http-4.1.32.Final.jar netty-codec-http2-4.1.32.Final-sources.jar netty-codec-http2-4.1.32.Final.jar netty-codec-memcache-4.1.32.Final-sources.jar netty-codec-memcache-4.1.32.Final.jar netty-codec-redis-4.1.32.Final-sources.jar netty-codec-redis-4.1.32.Final.jar netty-codec-socks-4.1.32.Final-sources.jar netty-codec-socks-4.1.32.Final.jar netty-codec-stomp-4.1.32.Final-sources.jar netty-codec-stomp-4.1.32.Final.jar netty-common-4.1.32.Final-sources.jar netty-common-4.1.32.Final.jar netty-example-4.1.32.Final-sources.jar netty-example-4.1.32.Final.jar netty-handler-4.1.32.Final-sources.jar netty-handler-4.1.32.Final.jar netty-handler-proxy-4.1.32.Final-sources.jar netty-handler-proxy-4.1.32.Final.jar netty-resolver-4.1.32.Final-sources.jar netty-resolver-4.1.32.Final.jar netty-tcnative-2.0.20.Final-osx-x86_64.jar netty-tcnative-2.0.20.Final-sources.jar netty-transport-4.1.32.Final-sources.jar netty-transport-4.1.32.Final.jar netty-transport-native-epoll-4.1.32.Final-linux-x86_64.jar netty-transport-native-epoll-4.1.32.Final-sources.jar netty-transport-native-kqueue-4.1.32.Final-osx-x86_64.jar netty-transport-native-kqueue-4.1.32.Final-sources.jar netty-transport-native-unix-common-4.1.32.Final-sources.jar netty-transport-native-unix-common-4.1.32.Final.jar netty-transport-rxtx-4.1.32.Final-sources.jar netty-transport-rxtx-4.1.32.Final.jar netty-transport-sctp-4.1.32.Final-sources.jar netty-transport-sctp-4.1.32.Final.jar netty-transport-udt-4.1.32.Final-sources.jar netty-transport-udt-4.1.32.Final.jar

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值