Spark-补充知识-Netty

说明

在Spark 1.3.1版本之前,‌Akka是实现RPC通信框架的主要技术,‌但随着时间的推移和技术的发展,‌Spark开始面临大块数据传输的问题,‌如Shuffle操作。‌为了解决这些问题,‌Spark从1.3.1版本开始引入了Netty通信框架。‌到了1.6.0版本,‌Netty完全取代了Akka,‌承担了Spark内部所有的RPC通信以及数据流传输任务。‌这大大提高了Spark的性能和效率,因此我们有必要来学习下Netty。

概述

Netty是一个NIO client-server(客户端服务器)框架

Netty是"quick and easy(高性能和简单易用)"的,且性能和可维护性都很强

整个Netty的API都是异步的,对网络应用来说,IO一般是性 能的瓶颈,使用异步IO可以较大程度上提高程序性能,

一、构建一个Netty项目

1、创建一个简单的Maven项目

给项目起个名字

选择自己本地的maven配置

编辑pom.xml添加Netty的maven配置

可以去https://mvnrepository.com/artifact/io.netty/netty-all搜索具体的版本配置,我选择的是下载最多的版本(4.1.42.Final)

复制代码
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.42.Final</version>
</dependency>

二、复制Netty官网的例子

https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/echo

1、EchoClient

java 复制代码
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import org.example.netty.official.util.ServerUtil;

/**
 * Sends one message when a connection is open and echoes back any received
 * data to the server.  Simply put, the echo client initiates the ping-pong
 * traffic between the echo client and server by sending the first message to
 * the server.
 */
public final class EchoClient {

    static final String HOST = System.getProperty("host", "127.0.0.1");
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.git
        final SslContext sslCtx = ServerUtil.buildSslContext();

        // Configure the client.
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .handler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
                     }
                    
                     p.addLast(new EchoClientHandler());
                 }
             });

            // Start the client.
            ChannelFuture f = b.connect(HOST, PORT).sync();

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down the event loop to terminate all threads.
            group.shutdownGracefully();
        }
    }
}

2、EchoClientHandler

java 复制代码
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * Handler implementation for the echo client.  It initiates the ping-pong
 * traffic between the echo client and server by sending the first message to
 * the server.
 */
public class EchoClientHandler extends ChannelInboundHandlerAdapter {

    private final ByteBuf firstMessage;

    /**
     * Creates a client-side handler.
     */
    public EchoClientHandler() {
        firstMessage = Unpooled.buffer(EchoClient.SIZE);
        for (int i = 0; i < firstMessage.capacity(); i ++) {
            firstMessage.writeByte((byte) i);
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.writeAndFlush(firstMessage);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ctx.write(msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
       ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}

3、EchoServer

java 复制代码
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import org.example.netty.official.util.ServerUtil;

/**
 * Echoes back any received data from a client.
 */
public final class EchoServer {

    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        final SslContext sslCtx = ServerUtil.buildSslContext();

        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final EchoServerHandler serverHandler = new EchoServerHandler();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     //p.addLast(new LoggingHandler(LogLevel.INFO));
                     p.addLast(serverHandler);
                 }
             });

            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

4、EchoServerHandler

java 复制代码
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * Handler implementation for the echo server.
 */
@Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ctx.write(msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}

三、Server端启动流程图

启动流程调用还是挺繁琐的,如有错误的地方还请指出,谢谢

四、Server启动流程部分源码

1、EchoServer

java 复制代码
 public static void main(String[] args) throws Exception {
        //配置ssl
        final SslContext sslCtx = ServerUtil.buildSslContext();

        //配置服务
        //EventLoopGroup 继承了EventExecutorGroup 
        //允许注册 Channel,这些通道在事件循环期间被处理以供以后选择
        //实现了java的Reactor的反应堆模式
        //bossGroup 为1个线程
        //workerGroup 的线程个数默认为当前可用的处理器数量的2倍
        //DEFAULT_EVENT_LOOP_THREADS = Math.max(1,SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
        //在构造时也会将选择器初始化
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        //继承了ChannelInboundHandlerAdapter 用来处理接收的信息
        //它操作完会将消息给下一个Channel Handler
        final EchoServerHandler serverHandler = new EchoServerHandler();
        try {
            //用来引导ServerChannel
            ServerBootstrap b = new ServerBootstrap();
            //用来设置父子EventLoopGroup
            //bossGroup 用来接收客户端的连接并把状态改变的事件给childEventLoopGroup
            //workerGroup 用来处理所有的事件和IO
            b.group(bossGroup, workerGroup)
             //通过工厂、反射、泛型来设置channel 
             //NioServerSocketChannel 非阻塞
             //OioServerSocketChannel 阻塞
             .channel(NioServerSocketChannel.class)
             //指定ChannelOption 用于设置后面的Channel
             //将值放到一个LinkedHashMap中 (ChannelOption<T> -> T)
             .option(ChannelOption.SO_BACKLOG, 100)
             //设置用于处理请求的ChannelHandler 这是一个日志处理
             .handler(new LoggingHandler(LogLevel.INFO))
             //设置子ChannelHandler 也用于处理客户端的请求
             //ChannelInitializer 用于设置一个 ChannelPipeline
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     //获取分配的ChannelPipeline
                     //ChannelPipeline:
                     //一组处理或拦截{@link Channel}的入站事件和出站操作的{@link ChannelHandler}列表,实现了拦截过滤器模式的高级形式,使用户可以完全控制事件的处理方式以及管道中的{@link Channel Handler}如何相互交互。
                     //创建一个 pipeline:
                     //每个 Channel 都有自己的 pipeline,并在创建新 Channel 时自动创建。
                     //事件如何在管道中流动:
                     //Socket.read() -> Inbound Handler  1 -> Inbound Handler  2 -> ChannelHandlerContext.fireChannelRead() -> Inbound Handler  N 
                    //ChannelHandlerContext -> Outbound Handler  1 -> Outbound Handler  2 -> ChannelHandlerContext.write() -> Outbound Handler  M -> Socket.write()
                    //举个例子:有如下的pipeline: 这里使用了责任链模式
                    //ChannelPipeline p = ...;
                    //p.addLast("1", new InboundHandlerA());
                    //p.addLast("2", new InboundHandlerB());
                    //p.addLast("3", new OutboundHandlerA());
                    //p.addLast("4", new OutboundHandlerB());
                    //p.addLast("5", new InboundOutboundHandlerX());
                    //名称以Inbound开头的类意味着它是一个入站处理程序。
                    //名称以Outbound开头的类表示它是一个出站处理程序
                    //当事件进入入站时,处理程序评估顺序为1、2、3、4、5。
                    //当事件出站时,顺序为5、4、3、2、1
                    //ChannelPipeline会跳过某些 Handler 来缩减栈的深度
                    // 例如3和4没有实现{@link ChannelInboundHandler},因此入站事件的实际求值顺序将是:1、2和5。
                    //1和2没有实现{@link ChannelOutboundHandler},因此出站事件的实际求值顺序为:5、4和3
                    //其中5同时实现了ChannelInboundHandler 和 ChannelOutboundHandler 所以入站出站都会走
                    //将事件转发给下一个处理程序:
                    //处理程序必须调用ChannelHandlerContext中的事件传播方法,才能将事件转发给下一个处理程序。这些方法包括:
                    //入站事件传播方法:
                    //    ChannelHandlerContext.fireChannelRegistered()
                    //    ChannelHandlerContext.fireChannelActive()
                    //    ChannelHandlerContext.fireChannelRead(Object)
                    //    ChannelHandlerContext.fireChannelReadComplete()
                    //    ChannelHandlerContext.fireExceptionCaught(Throwable)
                    //    ChannelHandlerContext.fireUserEventTriggered(Object)
                    //    ChannelHandlerContext.fireChannelWritabilityChanged()
                    //    ChannelHandlerContext.fireChannelInactive()
                    //    ChannelHandlerContext.fireChannelUnregistered()
                    //出站事件传播方法:
                    //    ChannelHandlerContext.bind(SocketAddress, ChannelPromise)
                    //    ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise)
                    //    ChannelHandlerContext.write(Object, ChannelPromise)
                    //    ChannelHandlerContext.flush()
                    //    ChannelHandlerContext.read()
                    //    ChannelHandlerContext.disconnect(ChannelPromise)
                    //    ChannelHandlerContext.close(ChannelPromise)
                    //    ChannelHandlerContext.deregister(ChannelPromise)
                    //下面我们看看事件传播通常是如何完成的:
                    //    1、创建自己的 MyInboundHandler 继承 ChannelInboundHandlerAdapter
                    //      创建自己的 MyOutboundHandler 继承ChannelOutboundHandlerAdapter
                    //    2、创建一个pipeline ChannelPipeline pipeline = ch.pipeline();
                    //    3、将自定义的Handler添加到 pipeline
                    //        pipeline.addLast("decoder", new MyProtocolDecoder());
                    //        pipeline.addLast("encoder", new MyProtocolEncoder());
                    //线程安全:
                    //    可以随时添加或删除ChannelHandler,因为ChannelPipeline是线程安全的
                    //    例如,您可以在即将交换敏感信息时插入加密处理程序,并在交换后将其删除
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     //将自定义的EchoServerHandler 设置到管道的末尾
                     p.addLast(serverHandler);
                 }
             });

            //启动Server端
            ChannelFuture f = b.bind(PORT).sync();

            //等待服务器套接字关闭
            f.channel().closeFuture().sync();
        } finally {
            //关闭所有事件循环以终止所有线程。
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

2、NioEventLoopGroup

java 复制代码
public NioEventLoopGroup(int nThreads, Executor executor) {
        this(nThreads, executor, SelectorProvider.provider());
    }

//来自package java.nio.channels.spi
public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            //根据不同操作系统的jdk来创建选择器
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
}
//来自package sun.nio.ch;
//windows 的 jdk
public static SelectorProvider create() {
        return new WindowsSelectorProvider();
}
//macos 的 jdk
public static SelectorProvider create() {
        return new sun.nio.ch.KQueueSelectorProvider();
}
//solaris 的 jdk
public static SelectorProvider create() {
        String osname = AccessController
            .doPrivileged(new GetPropertyAction("os.name"));
        if (osname.equals("SunOS"))
            return createProvider("sun.nio.ch.DevPollSelectorProvider");
        if (osname.equals("Linux"))
            return createProvider("sun.nio.ch.EPollSelectorProvider");
        return new sun.nio.ch.PollSelectorProvider();
}

Netty是跨平台的,因为选择器依赖于JDK,JDK在不同操作系统都实现了适配

3、ServerBootstrap以及父类

1、group

java 复制代码
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
    //设置该属性为 childGroup 
    private volatile EventLoopGroup childGroup;

    //为父级(接受者)和子级(客户端)设置 EventLoopGroup
    //这些 EventLoopGroup 用于处理 ServerChannel 和 Channel 的所有事件和IO
    //父级主要处理客户端连接事件
    //子级主要处理来自客户端的读写事件
    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        //调用父类的group方法,也就是AbstractBootstrap.group()
        super.group(parentGroup);
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        //
        this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
        return this;
    }

}

//它是一个帮助类,可以轻松引导 Channel 。可以通过链式调用提供一种配置AbstractBootstrap的简单方法
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    //设置该属性为 parentGroup
    volatile EventLoopGroup group;

    public B group(EventLoopGroup group) {
        ObjectUtil.checkNotNull(group, "group");
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        this.group = group;
        return self();
    }


}

2、channel

java 复制代码
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {

    private volatile ChannelFactory<? extends C> channelFactory;

    //使用工厂模式+反射+泛型创建 channel
    public B channel(Class<? extends C> channelClass) {
        return channelFactory(new ReflectiveChannelFactory<C>(
                ObjectUtil.checkNotNull(channelClass, "channelClass")
        ));
    }

    //在调用bind() 后通过io.netty.channel.ChannelFactory 创建 channel
    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");
        }
        //在这里只是设置下channelFactory 并没有创建channel
        this.channelFactory = channelFactory;
        return self();
    }

}

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
    private final Constructor<? extends T> constructor;

    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        ObjectUtil.checkNotNull(clazz, "clazz");
        try {
            //设置构造器
            this.constructor = clazz.getConstructor();
        } catch (NoSuchMethodException e) {
        }
    }
}

3、option

java 复制代码
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {

    private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();

    //允许指定一个ChannelOption,该选项在 Channel 实例创建后用于它们
    //ChannelOption:
    //ChannelOption允许以类型安全的方式配置 Channel Config。支持哪个 ChannelOption 取决于 Channel Config 的实际实现,也可能取决于它所属的传输的性质。
    public <T> B option(ChannelOption<T> option, T value) {
        ObjectUtil.checkNotNull(option, "option");
        synchronized (options) {
            if (value == null) {
                options.remove(option);
            } else {
                options.put(option, value);
            }
        }
        return self();
    }

}

4、handler

java 复制代码
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {

    private volatile ChannelHandler handler;

    //用于处理请求的 ChannelHandler
    //处理I/O事件或拦截I/O操作,并将其转发给其 ChannelPipeline 中的下一个ChannelHandler。
    //子类型:
    //    ChannelHandler本身不提供很多方法,但通常必须实现它的一个子类型:
    //        ChannelInboundHandler 处理入站I/O事件
    //        ChannelOutboundHandler处理入站I/O事件
    //或者,为了方便,提供了以下适配器类:
    //    ChannelInboundHandlerAdapter 来处理入站I/O事件
    //    ChannelOutboundHandlerAdapter来处理出站I/O事件
    //    ChannelDuplexHandler 用于处理入站和出站事件 Duplex译为全工、复式、双工模式
    //上下文对象:
    //    ChannelHandler提供了一个 Channel HandlerContext对象。ChannelHandler应该通过上下文对象与其所属的Channel Pipeline进行交互。使用上下文对象,ChannelHandler可以向上游或下游传递事件,动态修改管道,或存储特定于处理程序的信息 AttributeKey。
    //状态管理:
    //    ChannelHandler通常需要存储一些有状态的信息。最简单和推荐的方法是使用成员变量
    //    因为处理程序实例有一个专用于一个连接的状态变量,所以必须为每个新通道创建一个新的处理程序实例,以避免未经身份验证的客户端可以获取机密信息的竞争情况:
    //    使用 ChannelInitializer.initChannel(Channel) 为每个通道创建一个新的处理程序实例。
    //使用AttributeKey:
    //    虽然建议使用成员变量来存储处理程序的状态,但出于某种原因,您可能不想创建许多处理程序实例。在这种情况下,您可以使用ChannelHandlerContext提供的AttributeKey
    //@Sharable注解:
    //    如果一个ChannelHandler被@Sharable注释,这意味着你可以只创建一次处理程序的实例,并将其多次添加到一个或多个ChannelPipeline中
    //    如果未指定此注解,则每次将其添加到管道时都必须创建一个新的处理程序实例,因为它具有非共享状态,如成员变量。
    public B handler(ChannelHandler handler) {
        this.handler = ObjectUtil.checkNotNull(handler, "handler");
        return self();
    }

}

5、childHandler

java 复制代码
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {

    private volatile ChannelHandler childHandler;

    //设置 ChannelHandler,用于为 Channel 的请求提供服务。
    public ServerBootstrap childHandler(ChannelHandler childHandler) {
        this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
        return this;
    }

}

6、bind

java 复制代码
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {

    //创建一个Channel并绑定它
    public ChannelFuture bind(int inetPort) {
        return bind(new InetSocketAddress(inetPort));
    }

    public ChannelFuture bind(SocketAddress localAddress) {
        validate();
        return doBind(ObjectUtil.checkNotNull(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()
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            //Future的注册几乎总是已经完成,但以防万一。
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        promise.setFailure(cause);
                    } else {
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }

    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            //上面通过 ServerBootstrap 设置的 channelFactory 创建 channel
            channel = channelFactory.newChannel();
            //初始化channel
            init(channel);
        } catch (Throwable t) {
        }

        //使用此 EventLoop注册一个Channel。注册完成后,返回的 ChannelFuture将收到通知。
        //这里获取的group就是上面通过group()方法设置的 bossGroup
        //调用SingleThreadEventLoop.register(channel)
        //ChannelFuture :
        //    异步 Channel I/O操作的结果。
        //    Netty中的所有I/O操作都是异步的。这意味着任何I/O调用都将立即返回,并且不能保证所请求的I/O操作在调用结束时已完成。相反,您将返回一个 ChannelFuture 实例,该实例为您提供有关I/O操作结果或状态的信息。
        //    ChannelFuture 要么是 未完成,要么是完成。当I/O操作开始时,会创建一个新的future对象。新的future最初是未完成的------它既没有成功,也没有失败,也没有取消,因为I/O操作尚未完成。如果I/O操作成功完成、失败或取消,则未来将标记为已完成,并包含更具体的信息,例如失败的原因。请注意,即使是失败和取消也属于已完成状态。
        //状态:
        //未完成:
        //    isDone() = false
        //    isSuccess() = false
        //    isCancelled() = false
        //    cause() = null
        //完成且成功:
        //    isDone() = true
        //    isSuccess() = true
        //完成但失败:
        //    isDone() = true
        //    cause() = non-null
        //完成但取消:
        //    isDone() = true
        //    isCancelled() = true
        //提供了各种方法,让我们检查I/O操作是否已完成,等待完成,并检索I/O操作的结果。它还允许您添加ChannelFutureListener,以便在I/O操作完成时收到通知。
        //Netty更喜欢 addListener(GenericFutureListener) 来代替 await() 以便在I/O操作完成时收到通知并执行任何后续任务
        //addListener(GenericFutureListener) 是非阻塞的。它只是将指定的 ChannelFutureLister添加到Channel future 中,当与future 相关的I/O操作完成时,I/O线程将通知侦听器。 ChannelFutureLister产生了最佳的性能和资源利用率,因为它根本不会阻塞,
        //相比之下, await() 是一个阻塞操作。一旦被调用,调用者线程就会阻塞,直到操作完成。使用 await()实现顺序逻辑更容易,但调用线程在I/O操作完成之前会不必要地阻塞,并且线程间通知的成本相对较高。此外,在特定情况下有可能出现死锁,
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }

        //如果我们在这里并且promise没有失败,则属于以下情况之一:
        //1、如果我们尝试从事件循环中注册,则此时注册已完成。即,现在尝试bind()或connect()是安全的,因为通道已注册。
        //2、如果我们尝试从另一个线程注册,则注册请求已成功添加到事件循环的任务队列中,以供以后执行。即,现在尝试bind()或connect()是安全的:
        //    因为bind()或connect()将在执行计划的注册任务后执行
        //    因为register()、bind()和connect()都绑定到同一个线程。
        return regFuture;
    }


    private static void doBind0(
            final ChannelFuture regFuture, final Channel channel,
            final SocketAddress localAddress, final ChannelPromise promise) {

        //在触发channelRegistered()之前调用此方法。为用户处理程序提供在channelRegistered()实现中设置管道的机会。
        channel.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                if (regFuture.isSuccess()) {
                    //绑定到给定的SocketAddress
                    //异步实现,将标志位设置成 MASK_BIND 即 1 << 9 即 512
                    channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                } else {
                    promise.setFailure(regFuture.cause());
                }
            }
        });
    }

}
java 复制代码
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {

    void init(Channel channel) {
        //设置 channel操作
        setChannelOptions(channel, newOptionsArray(), logger);
        //设置 channel 属性
        setAttributes(channel, newAttributesArray());

        //创建ChannelPipeline 
        ChannelPipeline p = channel.pipeline();

        //设置采用 子线程组 来作为处理 channel 中的数据
        //通过group() 设置的 子线程组
        final EventLoopGroup currentChildGroup = childGroup;
        //通过handler() 设置的 子handler
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions = newOptionsArray(childOptions);
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs = newAttributesArray(childAttrs);
        final Collection<ChannelInitializerExtension> extensions = getInitializerExtensions();

        //管道套管道
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                //管道中执行的第一个handler是异步向管道添加ServerBootstrapAcceptor
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs,
                                extensions));
                    }
                });
            }
        });
        if (!extensions.isEmpty() && channel instanceof ServerChannel) {
            ServerChannel serverChannel = (ServerChannel) channel;
            for (ChannelInitializerExtension extension : extensions) {
                try {
                    //在初始化给定的服务器侦听器通道后,由ServerBootstrap调用。监听器通道负责调用accept(2)系统调用,并生成子通道。(accept(2):监听socket的文件描述符)
                    //允许该方法修改管道中的处理程序、通道属性或通道选项。该方法必须避免进行任何I/O操作或关闭通道。
                    //重写此方法以添加您自己的回调逻辑。默认实现什么也不做。
                    //    调用StubChannelInitializerExtension方法
                    //    再调用lastSeenListenerChannel.set(channel);
                    //FastThreadLocalThread 是 ThreadLocal}的一种特殊变体,可以产生更高的访问性能。
                    extension.postInitializeServerListenerChannel(serverChannel);
                } catch (Exception e) {
                    logger.warn("Exception thrown from postInitializeServerListenerChannel", e);
                }
            }
        }
    }

}

4、SingleThreadEventLoop

java 复制代码
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {


    public ChannelFuture register(Channel channel) {
        return register(new DefaultChannelPromise(channel, this));
    }

    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

}

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {


    public final void register(EventLoop eventLoop, final ChannelPromise promise) {

            AbstractChannel.this.eventLoop = eventLoop;

            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    //异步执行注册
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    
                }
            }
    }

    private void register0(ChannelPromise promise) {
            try {
                //检查通道是否仍然打开,因为在寄存器调用不在eventLoop的同时,通道可能会关闭
                // call was outside of the eventLoop
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                boolean firstRegistration = neverRegistered;
                doRegister();
                neverRegistered = false;
                registered = true;

                //确保在实际通知promise之前调用handlerAdd(...)。这是必要的,因为用户可能已经通过ChannelFutureListener中的管道触发了事件。
                pipeline.invokeHandlerAddedIfNeeded();
    
                safeSetSuccess(promise);
                //注册channel 修改ChannelHandlerMask中标志位为MASK_CHANNEL_REGISTERED = 1<<1 即 2
                pipeline.fireChannelRegistered();
                //仅在channel从未注册的情况下激活channel。这可以防止在信道被注销和重新注册时触发多个信道活动。
                if (isActive()) {
                    if (firstRegistration) {
                        //修改ChannelHandlerMask中标志位为MASK_CHANNEL_ACTIVE= 1<<3 即 8
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        //此通道之前已注册,并设置了autoRead()。这意味着我们需要重新开始读取,以便处理入站数据。
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                // 直接关闭通道,避免FD泄漏。
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
    }

    public final void beginRead() {
            assertEventLoop();

            try {
                //安排读取操作
                //AbstractNioChannel.doBeginRead();
                doBeginRead();
            } catch (final Exception e) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        调用pipeline的异常处理
                        pipeline.fireExceptionCaught(e);
                    }
                });
                close(voidPromise());
            }
     }

}

5、AbstractNioChannel

java 复制代码
//使用基于选择器的方法的 Channel 实现的抽象基类。
public abstract class AbstractNioChannel extends AbstractChannel {
    
    protected void doBeginRead() throws Exception {
        //调用了Channel.read()或ChannelHandlerContext.read()
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;

        final int interestOps = selectionKey.interestOps();
        //比较下是否是读操作
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

}

五、总结

Netty的启动过程同样做了selector的设置以及更丰富和灵活的channel设置、端口的绑定操作。其本质上也是对java NIO 的封装,从源码中我们还看到了Netty使用了很多设计模式来使得程序更加的灵活强大:

拦截过滤器模式

反应堆模式

责任链模式

工厂模式

适配器模式

相关推荐
IT小哥哥呀41 分钟前
电池制造行业数字化实施
大数据·制造·智能制造·数字化·mom·电池·信息化
Xi xi xi1 小时前
苏州唯理科技近期也正式发布了国内首款神经腕带产品
大数据·人工智能·经验分享·科技
yumgpkpm1 小时前
华为鲲鹏 Aarch64 环境下多 Oracle 、mysql数据库汇聚到Cloudera CDP7.3操作指南
大数据·数据库·mysql·华为·oracle·kafka·cloudera
祈祷苍天赐我java之术2 小时前
Redis 数据类型与使用场景
java·开发语言·前端·redis·分布式·spring·bootstrap
UMI赋能企业2 小时前
制造业流程自动化提升生产力的全面分析
大数据·人工智能
TDengine (老段)3 小时前
TDengine 数学函数 FLOOR 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
猫林老师4 小时前
HarmonyOS线程模型与性能优化实战
数据库·分布式·harmonyos
派可数据BI可视化5 小时前
商业智能BI 浅谈数据孤岛和数据分析的发展
大数据·数据库·数据仓库·信息可视化·数据挖掘·数据分析
jiedaodezhuti5 小时前
Flink性能调优基石:资源配置与内存优化实践
大数据·flink
Lx3527 小时前
Flink窗口机制详解:如何处理无界数据流
大数据