一、服务启动官方example
// Echoes back any received data from a client.
*/
public final class EchoServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static final class Callabler implements Callable {
@Override
public Object call() throws Exception {
return null;
}
}
public static void main(String[] args) throws Exception {
// Configure SSL.
// 配置 SSL
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the server.
// 创建两个 EventLoopGroup 对象
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 创建 boss 线程组 用于服务端接受客户端的连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 创建 worker 线程组 用于进行 SocketChannel 的数据读写
// ((NioEventLoop) workerGroup.next()).threadProperties();
// Collection<Callabler> callablers = new ArrayList<Callabler>();
// for (int i = 0; i < 10; i++) {
// callablers.add(new Callabler());
// }
// Set<Callable<Boolean>> set = Collections.<Callable<Boolean>>singleton(new Callable<Boolean>() {
// @Override
// public Boolean call() throws Exception {
// return Boolean.TRUE;
// }
// });
// ((NioEventLoop) workerGroup.next()).invokeAny(set);
// Thread.sleep(Long.MAX_VALUE);
// 创建 EchoServerHandler 对象
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
// 创建 ServerBootstrap 对象
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup) // 设置使用的 EventLoopGroup
.channel(NioServerSocketChannel.class) // 设置要被实例化的为 NioServerSocketChannel 类
.option(ChannelOption.SO_BACKLOG, 100) // 设置 NioServerSocketChannel 的可选项
.handler(new LoggingHandler(LogLevel.INFO)) // 设置 NioServerSocketChannel 的处理器
// .handler(new IdleStateHandler(0, 1, 0, TimeUnit.SECONDS))
// .handler(new ChannelInitializer<Channel>() {
//
// @Override
// protected void initChannel(Channel ch) {
// final ChannelPipeline pipeline = ch.pipeline();
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new LoggingHandler(LogLevel.INFO));
}
});
// pipeline.addLast(new LoggingHandler(LogLevel.INFO));
pipeline.addLast(new ChannelOutboundHandlerAdapter() {
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
// super.bind(ctx, localAddress, promise);
if (true) {
throw new RuntimeException("测试异常");
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
});
//
pipeline.addLast(new ChannelInboundHandlerAdapter() {
// @Override
// public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// super.exceptionCaught(ctx, cause);
// }
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
throw new RuntimeException("测试异常");
}
});
// }
//
// })
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception { // 设置连入服务端的 Client 的 SocketChannel 的处理器
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
// p.addLast(new IdleStateHandler(0, 1, 0, TimeUnit.SECONDS));
p.addLast(new LineBasedFrameDecoder(Integer.MAX_VALUE));
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(serverHandler);
}
})
.childOption(ChannelOption.SO_SNDBUF, 5)
.childOption(ChannelOption.SO_LINGER, 100)
;
// Start the server.
// 绑定端口,并同步等待成功,即启动服务端
ChannelFuture f = b.bind(PORT).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println("测试下被触发");
}
}).sync();
// bossGroup.schedule(new Runnable() {
// @Override
// public void run() {
// System.out.println("执行定时任务");
// }
// }, 5, TimeUnit.SECONDS);
// f.channel().writeAndFlush("123").addListener(new ChannelFutureListener() {
// @Override
// public void operationComplete(ChannelFuture future) throws Exception {
// System.out.println("干啥呢");
// }
// });
// f.channel().close();
// Wait until the server socket is closed.
// 监听服务端关闭,并阻塞等待
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
// 优雅关闭两个 EventLoopGroup 对象
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
这里我们主要分析以下代码
// 绑定端口,并同步等待成功,即启动服务端
ChannelFuture f = b.bind(PORT).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println("测试下被触发");
}
}).sync();
跟踪bind方法调用代码
/**
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
继续跟踪bing调用
/**
* Create a new {@link Channel} and bind it.
*/
public ChannelFuture bind(SocketAddress localAddress) {
// 校验服务启动需要的必要参数
validate();
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
// 绑定本地地址( 包括端口 )
return doBind(localAddress);
}
然后看doBind方法调用
private ChannelFuture doBind(final SocketAddress localAddress) {
// 初始化并注册一个 Channel 对象,因为注册是异步的过程,所以返回一个 ChannelFuture 对象。
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) { // 若发生异常,直接进行返回。
return regFuture;
}
// 绑定 Channel 的端口,并注册 Channel 到 SelectionKey 中。
if (regFuture.isDone()) { // 未
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise); // 绑定
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println(Thread.currentThread() + ": PendingRegistrationPromise");
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise); // 绑定
}
}
});
return promise;
}
}
我们主要关注以下两个方法调用
// 初始化并注册一个 Channel 对象,因为注册是异步的过程,所以返回一个 ChannelFuture 对象。
final ChannelFuture regFuture = initAndRegister();
。。。。
doBind0(regFuture, channel, localAddress, promise); // 绑定
其实,从方法名上面我们已经可以略窥一二,init->初始化,register->注册,那么到底要注册到什么呢?联系到nio里面轮询器的注册,可能是把某个东西初始化好了之后注册到selector上面去,最后bind,像是在本地绑定端口号,带着这些猜测,
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 创建 Channel 对象
channel = channelFactory.newChannel();
// 初始化 Channel 配置
init(channel);
} catch (Throwable t) {
if (channel != null) { // 已创建 Channel 对象
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly(); // 强制关闭 Channel
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
// 注册 Channel 到 EventLoopGroup 中
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly(); // 强制关闭 Channel
}
}
//如果我们在这里,并且承诺没有失败,则会出现以下情况之一:
//1)如果我们尝试从事件循环中注册,则此时注册已完成。//也就是说,现在可以安全地尝试bind()或connect(),因为通道已经注册。
//2)如果我们尝试从另一个线程注册,则注册请求已成功//添加到事件循环的任务队列中,以便以后执行。//也就是说,现在尝试bind()或connect()是安全的:
//因为bind()或connect()将在*计划的注册任务执行*之后执行//因为register()、bind()和connect()都绑定到同一个线程。
return regFuture;
}
(ps:此段分析来自闪电侠https://www.jianshu.com/p/c5068caab217)
我们看到 initAndRegister() 做了几件事情
1.new一个channel
2.init这个channel
3.将这个channel register到某个对象
1.new一个channel
我们首先要搞懂channel的定义,netty官方对channel的描述如下
A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind
这里的channel,由于是在服务启动的时候创建,我们可以和普通Socket编程中的ServerSocket对应上,表示服务端绑定的时候经过的一条流水线
我们发现这条channel是通过一个 channelFactory new出来的,channelFactory 的接口很简单
public interface ChannelFactory<T extends Channel> extends io.netty.bootstrap.ChannelFactory<T> {
/**
* Creates a new channel.
*/
@Override
T newChannel();
}
就一个方法,我们查看channelFactory被赋值的地方
AbstractBootstrap.java
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return (B) this;
}
在这里被赋值,我们层层回溯,查看该函数被调用的地方,发现最终是在这个函数中,ChannelFactory被new出
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
这里,我们的demo程序调用channel(channelClass)方法的时候,将channelClass作为ReflectiveChannelFactory的构造函数创建出一个ReflectiveChannelFactory
demo端的代码如下:
.channel(NioServerSocketChannel.class);
然后回到本节最开始
channelFactory.newChannel();
我们就可以推断出,最终是调用到 ReflectiveChannelFactory.newChannel() 方法,跟进
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
@Override
public T newChannel() {
try {
return clazz.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
}
看到clazz.newInstance();,我们明白了,原来是通过反射的方式来创建一个对象,而这个class就是我们在ServerBootstrap中传入的NioServerSocketChannel.class
结果,绕了一圈,最终创建channel相当于调用默认构造函数new出一个 NioServerSocketChannel对象
接下来我们就可以将重心放到 NioServerSocketChannel的默认构造函数
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
private static ServerSocketChannel newSocket(SelectorProvider provider) {
//...
return provider.openServerSocketChannel();
}
通过SelectorProvider.openServerSocketChannel()创建一条server端channel,然后进入到以下方法
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
这里第一行代码就跑到父类里面去了,第二行,new出来一个 NioServerSocketChannelConfig,其顶层接口为 ChannelConfig,netty官方的描述如下
A set of configuration properties of a Channel.
基本可以判定,ChannelConfig 也是netty里面的一大核心模块,初次看源码,看到这里,我们大可不必深挖这个对象,而是在用到的时候再回来深究,只要记住,这个对象在创建NioServerSocketChannel对象的时候被创建即可
我们继续追踪到 NioServerSocketChannel 的父类
AbstractNioMessageChannel.java
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
}
继续往上追
AbstractNioChannel.java
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
//...
ch.configureBlocking(false);
//...
}
这里,简单地将前面 provider.openServerSocketChannel(); 创建出来的 ServerSocketChannel 保存到成员变量,然后调用ch.configureBlocking(false);设置该channel为非阻塞模式,标准的jdk nio编程的玩法
这里的 readInterestOp 即前面层层传入的 SelectionKey.OP_ACCEPT,接下来重点分析 super(parent);(这里的parent其实是null,由前面写死传入)
AbstractChannel.java
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
到了这里,又new出来三大组件,赋值到成员变量,分别为
id = newId();
protected ChannelId newId() {
return DefaultChannelId.newInstance();
}
id是netty中每条channel的唯一标识,这里不细展开,接着
unsafe = newUnsafe();
protected abstract AbstractUnsafe newUnsafe();
查看Unsafe的定义
Unsafe operations that should never be called from user-code. These methods are only provided to implement the actual transport, and must be invoked from an I/O thread
成功捕捉netty的又一大组件,我们可以先不用管TA是干嘛的,只需要知道这里的 newUnsafe方法最终属于类NioServerSocketChannel中
最后
pipeline = newChannelPipeline();
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
初次看这段代码,可能并不知道 DefaultChannelPipeline 是干嘛用的,我们仍然使用上面的方式,查看顶层接口ChannelPipeline的定义
A list of ChannelHandlers which handles or intercepts inbound events and outbound operations of a Channel
从该类的文档中可以看出,该接口基本上又是netty的一大核心模块
到了这里,我们总算把一个服务端channel创建完毕了,将这些细节串起来的时候,我们顺带提取出netty的几大基本组件,先总结如下
Channel
ChannelConfig
ChannelId
Unsafe
Pipeline
ChannelHander
初次看代码的时候,我们的目标是跟到服务器启动的那一行代码,我们先把以上这几个组件记下来,等代码跟完,我们就可以自顶向下,逐层分析,我会放到后面源码系列中去深入到每个组件
总结一下,用户调用方法 Bootstrap.bind(port) 第一步就是通过反射的方式new一个NioServerSocketChannel对象,并且在new的过程中创建了一系列的核心组件,仅此而已,并无他,真正的启动我们还需要继续跟