说明
在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使用了很多设计模式来使得程序更加的灵活强大:
拦截过滤器模式
反应堆模式
责任链模式
工厂模式
适配器模式