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使用了很多设计模式来使得程序更加的灵活强大:

拦截过滤器模式

反应堆模式

责任链模式

工厂模式

适配器模式

相关推荐
LonelyProgramme1 分钟前
Flink定时器
大数据·flink
lucky_syq12 分钟前
Hive SQL和Spark SQL的区别?
hive·sql·spark
m0_7482448322 分钟前
StarRocks 排查单副本表
大数据·数据库·python
NiNg_1_23424 分钟前
Hadoop中MapReduce过程中Shuffle过程实现自定义排序
大数据·hadoop·mapreduce
B站计算机毕业设计超人28 分钟前
计算机毕业设计PySpark+Hadoop中国城市交通分析与预测 Python交通预测 Python交通可视化 客流量预测 交通大数据 机器学习 深度学习
大数据·人工智能·爬虫·python·机器学习·课程设计·数据可视化
李洋-蛟龙腾飞公司33 分钟前
HarmonyOS Next 应用元服务开发-分布式数据对象迁移数据文件资产迁移
分布式·华为·harmonyos
沛沛老爹39 分钟前
什么是 DevOps 自动化?
大数据·ci/cd·自动化·自动化运维·devops
喝醉酒的小白2 小时前
Elasticsearch(ES)监控、巡检及异常指标处理指南
大数据·elasticsearch·搜索引擎
lucky_syq2 小时前
Spark和Hadoop之间的区别
大数据·hadoop·spark
技术路上的苦行僧3 小时前
分布式专题(10)之ShardingSphere分库分表实战指南
分布式·shardingsphere·分库分表