nio server启动的第一步,都是要创建一个serverSocketChannel,我截取一段启动代码,一步步分析:

public void afterPropertiesSet() throws Exception {
// 创建rpc工厂
ThreadFactory threadRpcFactory = new NamedThreadFactory("NettyRPC ThreadFactory");
//执行worker线程池数量
int parallel = Runtime.getRuntime().availableProcessors() * 2;
// boss
EventLoopGroup boss = new NioEventLoopGroup(1);
// worker
EventLoopGroup worker = new NioEventLoopGroup(parallel,threadRpcFactory, SelectorProvider.provider());
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker).channel(NioServerSocketChannel.class)
.childHandler(new MessageRecvChannelInitializer(handlerMap))
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture future = bootstrap.bind(serverAddress, Integer.valueOf(port)).sync();
System.out.printf("[author chenjianye] Netty RPC Server start success ip:%s port:%s\n", serverAddress, port); // 注册zookeeper服务
serviceRegistry.register(serverAddress + ":" + port);
// wait
future.channel().closeFuture().sync();
} finally {
worker.shutdownGracefully();
boss.shutdownGracefully();
}
}
入口就在
ChannelFuture future = bootstrap.bind(serverAddress, Integer.valueOf(port)).sync();

顺序往下执行,直到AbstractBootstrap类的doBind方法:
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = this.initAndRegister();
final Channel channel = regFuture.channel();
if(regFuture.cause() != null) {
return regFuture;
} else if(regFuture.isDone()) {
ChannelPromise promise1 = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise1);
return promise1;
} else {
final AbstractBootstrap.PendingRegistrationPromise promise = new AbstractBootstrap.PendingRegistrationPromise(channel, null);
regFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if(cause != null) {
promise.setFailure(cause);
} else {
promise.executor = channel.eventLoop();
} AbstractBootstrap.doBind0(regFuture, channel, localAddress, promise);
}
});
return promise;
}
}
=============================================
这个方法需要重点分析,我做个标记,doBind方法分析如下: 1.final ChannelFuture regFuture = this.initAndRegister();
final ChannelFuture initAndRegister() {
// 通过channel工厂反射创建ServerSocketChannel,并创建了作用于serverSocketChannel的channelPipeline管道
// 这个管道维护了ctx为元素的双向链表,到目前为止,pipeline的顺序为:head(outBound) ----> tail(inbound)
Channel channel = this.channelFactory().newChannel(); try {
     // 这个init方法第一个主要是设置channel的属性,我不细说了
// 第二个作用是增加了inbound处理器,channelInitializer,里面有一个initChannel()方法会在特定时刻被触发,什么时候被触发,后面我会说到。
this.init(channel);
} catch (Throwable var3) {
channel.unsafe().closeForcibly();
return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
}    // 到了这里,就开始执行register逻辑,这个是关键,我把register的代码贴出来,跟着后面的代码继续看,跳转到2。
ChannelFuture regFuture = this.group().register(channel);
if(regFuture.cause() != null) {
if(channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
} return regFuture;
}
2.AbstractChannel里的register方法:
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if(eventLoop == null) {
throw new NullPointerException("eventLoop");
} else if(AbstractChannel.this.isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
} else if(!AbstractChannel.this.isCompatible(eventLoop)) {
promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
} else {
AbstractChannel.this.eventLoop = eventLoop;
// 到这里还是主线程启动,所以会执行else,启动了boss线程,register0方法跳转到3
if(eventLoop.inEventLoop()) {
this.register0(promise);
} else {
try {
eventLoop.execute(new OneTimeTask() {
public void run() {
AbstractUnsafe.this.register0(promise);
}
});
} catch (Throwable var4) {
AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var4);
}
} }
}
3.register0方法
private void register0(ChannelPromise promise) {
try {
if(!promise.setUncancellable() || !this.ensureOpen(promise)) {
return;
} boolean t = this.neverRegistered;
// 这个方法主要是 this.selectionKey = this.javaChannel().register(this.eventLoop().selector, 0, this);
        AbstractChannel.this.doRegister();
this.neverRegistered = false;
AbstractChannel.this.registered = true; // 回调之前doBind方法的listener,boss线程添加了新的任务:bind任务
this.safeSetSuccess(promise); // 这个方法东西内容很多
// 第一步:this.head.fireChannelRegistered()没有什么实质内容,最终执行到inbound的next.invokeChannelRegistered()方法
// 第二步:根据上文,目前的pipeline顺序为head---->initializer---->tail
// 第三步:执行initializer
// 第四步:添加一个新的inbound处理器,ServerBootstrapAcceptor,此时的顺序为:head---->initizlizer---->ServerBootstrapAcceptor---->tail
// 第五步:移除initizlizer,此时pipeline顺序为:head---->ServerBootstrapAcceptor---->tail
// 第六步:么有什么实质内容,这个方法就算执行结束了
AbstractChannel.this.pipeline.fireChannelRegistered(); // 这里channel还没有绑定,所以isActive()方法返回false,不会继续执行,目前boss线程还剩下bind任务
if(t && AbstractChannel.this.isActive()) {
AbstractChannel.this.pipeline.fireChannelActive();
}
} catch (Throwable var3) {
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var3);
} }
4.bind任务
bind任务是一个outbound,所以会按照tail---->head的顺序执行,目前只有head是outbound。
headHandler最终会执行AbstractUnsafe的bind方法:
AbstractChannel.this.doBind(localAddress);
public final void bind(SocketAddress localAddress, ChannelPromise promise) {
if(promise.setUncancellable() && this.ensureOpen(promise)) {
if(Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.isRoot()) {
AbstractChannel.logger.warn("A non-root user can\'t receive a broadcast packet if the socket is not bound to a wildcard address; binding to a non-wildcard address (" + localAddress + ") anyway as requested.");
} boolean wasActive = AbstractChannel.this.isActive(); try {
       //由于我们是nioServerSocketChannel,所以:this.javaChannel().socket().bind(localAddress, this.config.getBacklog()); AbstractChannel.this.doBind(localAddress); } catch (Throwable var5) {
            this.safeSetFailure(promise, var5);
this.closeIfClosed();
return;
}      // 此时已经绑定了,所以isActive()返回true,执行
if(!wasActive && AbstractChannel.this.isActive()) {
// 重新往boss线程加入了任务
this.invokeLater(new OneTimeTask() {
public void run() {
AbstractChannel.this.pipeline.fireChannelActive();
}
});
} this.safeSetSuccess(promise);
}
}
public ChannelPipeline fireChannelActive() {
// 来回调用,貌似这里没有什么实质内容
this.head.fireChannelActive();
if(this.channel.config().isAutoRead()) {
// 这个是outBound,最终会触发head
     // head会执行 this.unsafe.beginRead(),最终会执行abstractNioChannel里的doBeginRead()方法,最终会执行到5
        this.channel.read();
} return this;
}
5.
protected void doBeginRead() throws Exception {
if(!this.inputShutdown) {
SelectionKey selectionKey = this.selectionKey;
if(selectionKey.isValid()) {
this.readPending = true;
int interestOps = selectionKey.interestOps();
if((interestOps & this.readInterestOp) == 0) {
selectionKey.interestOps(interestOps | this.readInterestOp);
} }
}
}
终于看到我们熟悉的东西了,最终把selectionKey的interestOps设置为SelectionKey.OP_ACCEPT。
												

netty源码分析之一:server的启动的更多相关文章

  1. Netty源码分析之服务端启动过程

    一.首先来看一段服务端的示例代码: public class NettyTestServer { public void bind(int port) throws Exception{ EventL ...

  2. Netty源码分析之服务端启动

    Netty服务端启动代码: public final class EchoServer { static final int PORT = Integer.parseInt(System.getPro ...

  3. Netty源码—一、server启动(1)

    Netty作为一个Java生态中的网络组件有着举足轻重的位置,各种开源中间件都使用Netty进行网络通信,比如Dubbo.RocketMQ.可以说Netty是对Java NIO的封装,比如ByteBu ...

  4. Netty源码分析第1章(Netty启动流程)---->第1节: 服务端初始化

    Netty源码分析第一章:  Server启动流程 概述: 本章主要讲解server启动的关键步骤, 读者只需要了解server启动的大概逻辑, 知道关键的步骤在哪个类执行即可, 并不需要了解每一步的 ...

  5. Netty源码分析第1章(Netty启动流程)---->第2节: NioServerSocketChannel的创建

    Netty源码分析第一章:  Server启动流程 第二节:NioServerSocketChannel的创建 我们如果熟悉Nio, 则对channel的概念则不会陌生, channel在相当于一个通 ...

  6. Netty源码分析第1章(Netty启动流程)---->第5节: 绑定端口

    Netty源码分析第一章:Netty启动步骤 第五节:绑定端口 上一小节我们学习了channel注册在selector的步骤, 仅仅做了注册但并没有监听事件, 事件是如何监听的呢? 我们继续跟第一小节 ...

  7. Netty源码分析第2章(NioEventLoop)---->第4节: NioEventLoop线程的启动

    Netty源码分析第二章: NioEventLoop   第四节: NioEventLoop线程的启动 之前的小节我们学习了NioEventLoop的创建以及线程分配器的初始化, 那么NioEvent ...

  8. Netty源码分析 (三)----- 服务端启动源码分析

    本文接着前两篇文章来讲,主要讲服务端类剩下的部分,我们还是来先看看服务端的代码 /** * Created by chenhao on 2019/9/4. */ public final class ...

  9. Netty源码分析第1章(Netty启动流程)---->第3节: 服务端channel初始化

    Netty源码分析第一章:Netty启动流程   第三节:服务端channel初始化 回顾上一小节的initAndRegister()方法: final ChannelFuture initAndRe ...

  10. Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用

    Netty源码分析第一章:Netty启动流程   第四节:注册多路复用 回顾下以上的小节, 我们知道了channel的的创建和初始化过程, 那么channel是如何注册到selector中的呢?我们继 ...

随机推荐

  1. Go 初体验 - 令人惊叹的语法 - defer.1 - 基本概念和用法

    在我们编程时,难免遇见 流.远程连接.文件等 io 操作,为了高性能,我们不得不打开和关系这些 io 对象. 在 java.C# 语言里这些打开和关闭的操作都需要程序员自己选择操作时机,一般是在 io ...

  2. php – Laravel 5查询关系导致“调用成员函数addEagerConstraints()on null”错误( 转载)

    php – Laravel 5查询关系导致“调用成员函数addEagerConstraints()on null”错误   我一直在尝试创建一个简单的用户管理系统,但在查询关系时不断遇到障碍.例如,我 ...

  3. [macOS] keychain的跳坑之旅!git拉取的权限问题

    故事背景,svn与git各有长处,不过git大势所趋吧,那就搞搞.git的服务端,是基于phabricator搭建的,关于它的资料自行google就好了.其实之前运维已经搭好了phabricator了 ...

  4. Django框架详细介绍---ORM相关操作---select_related和prefetch_related函数对 QuerySet 查询的优化

    Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化 引言 在数据库存在外键的其情况下,使用select_related()和pre ...

  5. C# 获取结构体的所有成员

    读取结构体的所有成员(Engine为结构体)    FieldInfo[] fieldInfos = typeof(Engine).GetFields();

  6. Spring Boot IoC 容器初始化过程

    1. 加载 ApplicationContextInializer & ApplicationListener 2. 初始化环境 ConfigurableEnvironment & 加 ...

  7. npm -i 与npm install -s与-d的区别

    npm i module_name -S = > npm install module_name --save 写入到 dependencies 对象 npm i module_name -D ...

  8. 在 Tomcat 中自定义 404 页面(简单配置)

      打开 Tomcat 中的 web.xml,(tomcat/conf/web.xml) 添加如下代码: <error-page>  <error-code>404</e ...

  9. web文件上传

    文件上传的步骤: 1.目前Java文件上传功能都是依靠Apache组织的commons-io, fileupload两个包来实现的: 2. http://commons.apache.org/下载io ...

  10. Docker Macvlan 介绍 or 工作原理

    Docker Macvlan Network Macvlan Network:属于Docker的网络驱动. Macvlan Network:Docker主机网卡接口逻辑上分为多个子接口,每个子接口标识 ...