netty服务端客户端启动流程分析
服务端启动流程
我们回顾前面讲解的netty启动流程,服务端这边有两个EventLoopGroup,一个专门用来处理连接,一个用来处理后续的io事件
服务端启动还是跟nio一样,绑定端口进行监听,我们先来看绑定流程
// 绑定端口并同步阻塞直到绑定结束
ChannelFuture cf = serverBootstrap.bind(8080).sync(); private ChannelFuture doBind(final SocketAddress localAddress) {
// 注册一个NioServerScoketChannel
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
} if (regFuture.isDone()) {
// 注册完成 进行端口绑定,并返回一个异步绑定结果
ChannelPromise promise = channel.newPromise();
// 绑定端口
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// 未注册完成 返回带注册结果的promise
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 {
// 设置注册结果为true
promise.registered();
// 绑定端口
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
} final ChannelFuture initAndRegister() {
Channel channel = null;
try {
/**
* 通过我们之前设置的socket类型进行实例化
* serverBootstrap.channel(NioServerSocketChannel.class) 会返回一个ReflectiveChannelFactory工厂
**/
channel = channelFactory.newChannel(); // 1
// 初始化channel
init(channel); // 2
} catch (Throwable t) {
if (channel != null) {
// 发生异常 将内部nio中的channel进行关闭
channel.unsafe().closeForcibly();
// 返回一个结果失败的Promise
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
} // 将channel注册到bossEventLoopGroup上,里面会将channel注册到EventLoop中的selector
ChannelFuture regFuture = config().group().register(channel); // 3
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
} private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) { // 添加一个异步绑定任务
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
// 绑定!!!
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); // 4
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
对上述流程进行一个简单的说明: 先创建一个NioServerScoketChannel,然后进行初始化操作,然后注册到bossEventLoop中selector上,nio需要做的流程 netty都要做,然后进行绑定 返回一个绑定异步promise
绑定流程有4个比较重要的操作 我们来一一详解
1、实例化NioServerScoketChannel,内部会创建一个ServerSocketChannel 然后持有,同时创建unsafe和pipeline
/***********************NioServerSocketChannel**********************/
// selector提供者,提供创建selector、ServerSocketChannel、SocketChannel
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider(); public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
} private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
// 返回一个nio中的ServerSocketChannel
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
} public NioServerSocketChannel(ServerSocketChannel channel) {
// 调用父类构造
super(null, channel, SelectionKey.OP_ACCEPT);
// NioServerSocketChannel配置类
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
} /***********************AbstractNioChannel***********************/
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
// 将ServerSocketChannel设置为非阻塞
ch.configureBlocking(false);
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
} throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
/***********************AbstractChannel*************************/
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
// 创建一个unsafe 实例为NioMessageUnsafe类型
unsafe = newUnsafe();
// 创建pipeline,类型为DefaultChannelPipeline
pipeline = newChannelPipeline();
}
/***********************DefaultChannelPipeline*************************/
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;
}
2、初始化NioServerScoketChannel
/***********************ServerBootstrap*************************/
void init(Channel channel) throws Exception {
final Map<ChannelOption<?>, Object> options = options0();
// 设置我们之前设置的options serverBootstrap.option(ChannelOption<T> option, T value)
synchronized (options) {
setChannelOptions(channel, options, logger);
}
// 设置channel自定义属性
final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
channel.attr(key).set(e.getValue());
}
} ChannelPipeline p = channel.pipeline(); final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
} // 添加一个ChannelInitializer,用来注册后续的NioSocketChannel
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
} ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
// 这个handler就是后续监听客户端连接事件后,会将创建的NioSocketChannel 在这里进行注册
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
} private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
ServerBootstrapAcceptor(
final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
this.childGroup = childGroup;
this.childHandler = childHandler;
this.childOptions = childOptions;
this.childAttrs = childAttrs; enableAutoReadTask = new Runnable() {
@Override
public void run() {
channel.config().setAutoRead(true);
}
};
} @Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// NioServerScoketChannel在监听accpet后会创建一个NioSocketChannel 然后调用handler将该channel传递进来,handler那边只是简单的创建 并没有完成注册
final Channel child = (Channel) msg;
// 在serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>()添加的handler 加入到NioSocketChannel
child.pipeline().addLast(childHandler);
// 设置NioSocketChannel的Options
setChannelOptions(child, childOptions, logger);
// 设置NioSocketChannel的attr
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
} try {
// 将NioSocketChannel注册到workerEventLoopGroup上 然后添加一个监听器
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
} private static void forceClose(Channel child, Throwable t) {
child.unsafe().closeForcibly();
logger.warn("Failed to register an accepted channel: {}", child, t);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
final ChannelConfig config = ctx.channel().config();
if (config.isAutoRead()) {
// 将autoread设置为false,一秒后重新设置为true
config.setAutoRead(false);
ctx.channel().eventLoop().schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);
}
ctx.fireExceptionCaught(cause);
}
}
上图大致流程:将之前在ServerBootstrap设置的属性在这里相应设置,然后给NioServerScoketChannel添加一个处理器,这个处理器用来接收accept请求时将后续的NioSocketChannel注册到对应的workerEventLoop中,后续的io事件就交给NioSocketChannel来完成
3、channel注册到EventLoop,其实就是将channel注册到EventLoop中的selector上
/************************MultithreadEventLoopGroup*********************/
public ChannelFuture register(Channel channel) {
return next().register(channel);
} /***********************MultithreadEventExecutorGroup**********************/
public EventExecutor next() {
// 选择一个EventLoop
return chooser.next();
} /***********************SingleThreadEventLoop**********************/
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
} public ChannelFuture register(final ChannelPromise promise) {
// 拿到channel对应的Unsafe进行注册
promise.channel().unsafe().register(this, promise);
return promise;
} /***********************AbstractUnsafe**********************/
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) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
} private void register0(ChannelPromise promise) {
try {
// 判断当前注册promise是否被取消或者channel通道是否还处于打开状态
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
// 进行nio注册
doRegister();
neverRegistered = false;
registered = true; // 注册好后调用我们之前设置的channelInitializer.initChannel(channel) 方法
pipeline.invokeHandlerAddedIfNeeded();
// 设置状态成功
safeSetSuccess(promise);
// 注册成功事件传播
pipeline.fireChannelRegistered();
// 完成注册正好是active状态
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
} /***********************AbstractNioUnsafe**********************/
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// 将channel注册到EventLoop上的selector
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
eventLoop().selectNow();
selected = true;
} else {
throw e;
}
}
}
}
4、绑定端口,通过pipeline.bind发起绑定,绑定端口是出站事件,由tail像前进行传递,直到执行到head的bind()方法,然后通过其中的unsafe调用NioServerScoketChannel的doBind()进行绑定
/************************tailHandler*********************/
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeBind(localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeBind(localAddress, promise);
}
}, promise, null);
}
return promise;
} private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
bind(localAddress, promise);
}
} /************************headHandler*********************/
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
throws Exception {
unsafe.bind(localAddress, promise);
} /************************AbstractUnsafe*********************/
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop(); if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
} boolean wasActive = isActive();
try {
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
} if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
} safeSetSuccess(promise);
} /************************NioServerSocketChannel*********************/
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
服务端的启动流程就讲的差不多了,对上述流程大致进行一个梳理,进行端口绑定时首先对创建一个NioServerScoketChannel 用来处理accpet,然后注册到BossEventLoop上,通过给NioServerScoketChannel添加一个处理器 将来发生accpet时,将生成的NioSocketChannel注册到WorkerEventLoop上,后续的io事件就在该NioSocketChannel完成
客户端启动流程
客户端这边启动流程和服务端大致类似,连接ip端口时,创建一个用来和服务端通信的NioSocketChannel(内部包裹着SocketChannel),然后注册到对应的EventLoop上的selector开启事件监听
ChannelFuture cf = bootstrap.connect("127.0.0.1", 8080).sync();
cf.channel().closeFuture().sync(); /************************Bootstrap*********************/
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
// 创建一个NioSocketChannel然后进行注册,流程跟NioServerScoketChannel差不多,返回一个注册future
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel(); if (regFuture.isDone()) {
if (!regFuture.isSuccess()) {
return regFuture;
}
return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
} else {
// 还未注册完成 添加一个监听器 用作将来注册成功后进行连接操作
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();
doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
}
}
});
return promise;
}
} private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
final SocketAddress localAddress, final ChannelPromise promise) {
try {
final EventLoop eventLoop = channel.eventLoop();
final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop); //解析器无法解析ip地址或者已经被解析
if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
doConnect(remoteAddress, localAddress, promise);
return promise;
} final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress); if (resolveFuture.isDone()) {
// 解析完成 进行连接
final Throwable resolveFailureCause = resolveFuture.cause(); if (resolveFailureCause != null) {
// Failed to resolve immediately
channel.close();
promise.setFailure(resolveFailureCause);
} else {
// Succeeded to resolve immediately; cached? (or did a blocking lookup)
doConnect(resolveFuture.getNow(), localAddress, promise);
}
return promise;
} // 添加一个监听器用来将来地址解析成功然后进行连接
resolveFuture.addListener(new FutureListener<SocketAddress>() {
@Override
public void operationComplete(Future<SocketAddress> future) throws Exception {
if (future.cause() != null) {
channel.close();
promise.setFailure(future.cause());
} else {
doConnect(future.getNow(), localAddress, promise);
}
}
});
} catch (Throwable cause) {
promise.tryFailure(cause);
}
return promise;
} private static void doConnect(
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) { final Channel channel = connectPromise.channel();
// 提交一个连接任务到EventLoop上
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (localAddress == null) {
channel.connect(remoteAddress, connectPromise);
} else {
channel.connect(remoteAddress, localAddress, connectPromise);
}
// 添加一个关闭或失败的监听器,会将channel进行关闭
connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
}
});
} // 设置channel的options和attrs
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
p.addLast(config.handler()); final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
} final Map<AttributeKey<?>, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
}
}
至此我们服务端和客户端的启动流程都分析完了,我们前面说过EventLoop可以当做一个线程来执行, channel.eventLoop().execute(new Runnable(),后续的io事件监听和自定义任务处理都在EventLoop内执行,我们下节就来剖析其内部实现
netty服务端客户端启动流程分析的更多相关文章
- Netty服务端的启动源码分析
ServerBootstrap的构造: public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, Serve ...
- Netty服务端NioEventLoop启动及新连接接入处理
一 Netty服务端NioEventLoop的启动 Netty服务端创建.初始化完成后,再向Selector上注册时,会将服务端Channel与NioEventLoop绑定,绑定之后,一方面会将服务端 ...
- Unity进阶之ET网络游戏开发框架 02-ET的客户端启动流程分析
版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...
- Netty 服务端创建
参考:http://blog.csdn.net/suifeng3051/article/details/28861883?utm_source=tuicool&utm_medium=refer ...
- Netty 拆包粘包和服务启动流程分析
Netty 拆包粘包和服务启动流程分析 通过本章学习,笔者希望你能掌握EventLoopGroup的工作流程,ServerBootstrap的启动流程,ChannelPipeline是如何操作管理Ch ...
- 【转】Netty 拆包粘包和服务启动流程分析
原文:https://www.cnblogs.com/itdragon/archive/2018/01/29/8365694.html Netty 拆包粘包和服务启动流程分析 通过本章学习,笔者希望你 ...
- Netty之旅三:Netty服务端启动源码分析,一梭子带走!
Netty服务端启动流程源码分析 前记 哈喽,自从上篇<Netty之旅二:口口相传的高性能Netty到底是什么?>后,迟迟两周才开启今天的Netty源码系列.源码分析的第一篇文章,下一篇我 ...
- Netty 服务端启动过程
在 Netty 中创建 1 个 NioServerSocketChannel 在指定的端口监听客户端连接,这个过程主要有以下 个步骤: 创建 NioServerSocketChannel 初始化并注 ...
- 7.4 服务远程暴露 - 创建Exporter与启动netty服务端
为了安全:服务启动的ip全部使用10.10.10.10 远程服务的暴露总体步骤: 将ref封装为invoker 将invoker转换为exporter 启动netty 注册服务到zookeeper 订 ...
随机推荐
- python zip()函数用法
zip() --内建函数 zip([iterable, ...]) 它接受一系列可迭代的对象作为参数,将对象中对应的元素打包成一个个tuple(元组),然后返回由这些tuples组成的list(列表) ...
- js实现跳转的几种方式
1. window.open("url"); 2.用自定义函数 <script> function openWin(tag,obj) { obj.target=&quo ...
- 基于Dokcer搭建Redis集群搭建(主从集群)
最近陆陆续续有不少园友加我好友咨询 redis 集群搭建的问题,我觉得之前写的这篇 <基于Docker的Redis集群搭建> 文章一定是有问题了,所以我花了几分钟浏览之前的文章总结了下面几 ...
- 深入理解Java虚拟机(八)——类加载机制
是什么是类加载机制 Java虚拟机将class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程就是类加载机制. 类的生命周期 一个类从加载到内存 ...
- memcached session会话共享
1 安装依赖包yum install libevent livevent-devel nc -y 2 yum 安装memcachedyum install -y memcached 3 启动memec ...
- vue第十五单元(熟练使用vue-router插件)
第十五单元(熟练使用vue-router插件) #课程目标 1.掌握路由嵌套 2.掌握导航守卫 #知识点 #一.路由嵌套 很多时候,我们会在一个视口中实现局部页面的切换.这时候就需要到了嵌套路由. 也 ...
- 关于AES-CBC模式字节翻转攻击(python3)
# coding:utf-8 from Crypto.Cipher import AES import base64 def encrypt(iv, plaintext): if len(plaint ...
- B. Navigation System【CF 1320】
传送门 题目:简单理解就是,我们需要开车从s点到t点.车上有一个导航,如果当前点为x,则导航会自动为你提供一条从x到t的最短的路线(如果有多条,则随机选一条),每走到下一个点则会实时更新最短路线,当然 ...
- Spring-IOC基本使用
通过上篇文章大概知道ioc.DI的概念了,下面我们详细介绍一下 一.Spring IOC创建对象 IOC通过上文的介绍作用是控制创建对象的解释权,我们把代码重新看一下 //User.java publ ...
- 程序员你是如何使用镜像中心Harbor的?
背景 harbor即docker的私服:管理公司内部输出的镜像制品: 是VMware公司中国团队为企业用户设计的镜像注册服务器,用途:存储和分发docker镜像: 在官方的docker registr ...