netty源码深入研究(从客户端入手)第一篇
来源:互联网 发布:网络广告公司铭心 编辑:程序博客网 时间:2024/05/22 03:43
研究源码的前提建立在:
1、首先我们得知道怎么用这个框架
2、其次以用法为入口进行源码研究
ok,我们首先找到入口函数,官方文档有详细介绍,官方地址:点击打开链接,官方文档有提供的详细例子:
public static void main(String[] args) throws Exception { String host = args[0]; int port = Integer.parseInt(args[1]); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); // (1) b.group(workerGroup); // (2) b.channel(NioSocketChannel.class); // (3) b.option(ChannelOption.SO_KEEPALIVE, true); // (4) b.handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new TimeClientHandler()); } }); // Start the client. ChannelFuture f = b.connect(host, port).sync(); // (5) // Wait until the connection is closed. f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); } }
这就是官方提供的一个简单的客户端demo,大体上可以了解到,创建客户端必须先创建Bootstrap(英文意思是引导的意思),将
EventLoopGroup的创建绑定到Bootstrap,暂且叫他事件群管理,接下来绑定通道b.channel(NioSocketChannel.class),然后b.option(ChannelOption.SO_KEEPALIVE, true);开始设置socket属性,再初始化管道设置,最后连接。
现在我们点进
connect
实现链接的第一步
publicChannelFuture connect(SocketAddress remoteAddress) { if (remoteAddress == null) { throw newNullPointerException("remoteAddress"); } /** * 判断有没有设置handler,没有抛异常程序结束 */ validate(); returndoResolveAndConnect(remoteAddress, config.localAddress()); } validate方法主要判断初始化的一些必要参数用户有没有设置 if (group == null) { throw new IllegalStateException("group notset"); } if (channelFactory == null) { throw new IllegalStateException("channel orchannelFactory not set"); } if (config.handler() == null) { throw new IllegalStateException("handler notset"); }
继续走会进入 private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) { 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 { // 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 { // Directly obtain the cause and do a null check so we only need one volatile read in case of a // failure. 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(); doResolveAndConnect0(channel, remoteAddress, localAddress, promise); } } }); return promise; } }
跟进initAndRegister这个方法注册方法很重要, 连接的时候注册初始化
final ChannelFuture initAndRegister() { Channel channel = null; try {channelFactory//利用channelFactory创建通道channel ,也就是上面所传入的NioSocketChannel.class channel = channelFactory.newChannel();//开始初始化channel跟进 init(channel); } catch (Throwable t) { if (channel != null) { // channel can be null if newChannel crashed (eg SocketException("too many open files")) channel.unsafe().closeForcibly(); } // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t); } /** * 注册通道 */ ChannelFuture regFuture = config().group().register(channel); if (regFuture.cause() != null) { if (channel.isRegistered()) { channel.close(); } else { channel.unsafe().closeForcibly(); } } return regFuture; }
//进入init()方法继续 void init(Channel channel) throws Exception { /** * 把初始化handler句柄加入管道里 * 用默认的DefaultChannelPipeline管道对象 */ ChannelPipeline p = channel.pipeline(); p.addLast(config.handler()); final Map<ChannelOption<?>, Object> options = options0(); //把属性的一些设置(如前面设置的 bootStrap.option(ChannelOption.SO_KEEPALIVE, true);bootStrap.option(ChannelOption.TCP_NODELAY, true);)设置到configuration里面 synchronized (options) { setChannelOptions(channel, options); }/**attrs属性也设置到config配置里面*/ final Map<AttributeKey<?>, Object> attrs = attrs0(); synchronized (attrs) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue()); } } }
这个方法最重要的代码是 ChannelPipeline p = channel.pipeline(); p.addLast(config.handler());这两句,向管道里面添加上面注册的handler句柄,那么handelr是什么呢,暂且把他理解为处理数据的不同管道,你加载多少个管道数据就会一级级的去处理。这是源码提供的图形我想这个应该看起来更具体。
果然还是有图更具体,数据通过这个管道处理器一级一级的去处理数据,打个比方,我与服务端定一个自己的数据协议,那么socket传给我们的是byte数据最终我们会必须得转换为我们需要的类,这时候我们自定义一个编码器(通过一系列封装最终发送服务器所需要的数据),一个解码器(通过一系列解析数据,最终拼接成我们所需要的数据)即可。我们接着继续往下走进addLast,既然知道channel是NioSocketChannel了,那么找到
protected AbstractChannel(Channel parent) { . pipeline = newChannelPipeline(); } protected DefaultChannelPipeline newChannelPipeline() { return new DefaultChannelPipeline(this); }那么管道一目了然了默认就是DefaultChannelPipeline,进入 public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) { final AbstractChannelHandlerContext newCtx; synchronized (this) { //检查handler是否合法 checkMultiplicity(handler);//handler的包装类 newCtx = newContext(group, filterName(name, handler), handler);//将newCtx 加入队列中 addLast0(newCtx);//从上面我们看出init方法在注册方法前面所以此时registered=false if (!registered) { newCtx.setAddPending(); callHandlerCallbackLater(newCtx, true); return this; } EventExecutor executor = newCtx.executor(); if (!executor.inEventLoop()) { newCtx.setAddPending(); executor.execute(new Runnable() { @Override public void run() { callHandlerAdded0(newCtx); } }); return this; } } callHandlerAdded0(newCtx); return this; }// 进入 private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) { assert !registered;//第一次added肯定为true,那么task =PendingHandlerAddedTask(此类实现了runnble接口) PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx); PendingHandlerCallback pending = pendingHandlerCallbackHead; if (pending == null) { pendingHandlerCallbackHead = task; } else { // Find the tail of the linked-list. while (pending.next != null) { pending = pending.next; } pending.next = task; } }
最后走callHandlerAdded0(newCtx)方法,进入瞧瞧是干什么的,麻蛋,类实在太多了,最终调用的是
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) { try { ctx.handler().handlerAdded(ctx); ctx.setAddComplete();}}那么我们找到handler的handlerAdded,也就是ChannelInitializer类的handlerAdded方法 public void handlerAdded(ChannelHandlerContext ctx) throws Exception { if (ctx.channel().isRegistered()) { //最终调用我们的子类的抽象方法实现类 initChannel(ctx); } }
也就是它
bootStrap.handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {//加入我们自己的管道ch.pipeline().addLast("encoder", new Encoder());ch.pipeline().addLast("decoder", new Decoder());ch.pipeline().addLast("handler",new ClientHandler(NettyCore.this));}});
继续回到前面,初始化调用完之后开始注册,注册通道
ChannelFuture regFuture = config().group().register(channel); if (regFuture.cause() != null) { if (channel.isRegistered()) { channel.close(); } else { channel.unsafe().closeForcibly(); } }
此时的group为NioEventLoopGroup,直接查看它的register方法
public ChannelFuture register(Channel channel) { return next().register(channel); }
经过一系列调用,最终调用了NioEventLoop的register方法,这设计的就有点巧妙了,初始化NioEventLoopGroup的时候调用父 类的构造函数
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { if (nThreads <= 0) { throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads)); } /** * 传入的线程池为空就采用默认的 */ if (executor == null) { executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); }//根据线程的数量创建多个EventExecutor,也就是newChild方法返回的NioEventLoop(每个NioEventLoop都持有相同的线程池,持有不同的队列) children = new EventExecutor[nThreads]; for (int i = 0; i < nThreads; i ++) { boolean success = false; try { children[i] = newChild(executor, args); success = true; } catch (Exception e) { // TODO: Think about if this is a good exception type throw new IllegalStateException("failed to create a child event loop", e); } finally { if (!success) { for (int j = 0; j < i; j ++) { children[j].shutdownGracefully(); } for (int j = 0; j < i; j ++) { EventExecutor e = children[j]; try { while (!e.isTerminated()) { e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS); } } catch (InterruptedException interrupted) { // Let the caller handle the interruption. Thread.currentThread().interrupt(); break; } } } } }//创建NioEventLoop的选择器,每次重新注册的时候,根据规则选取一个NioEventLoop chooser = chooserFactory.newChooser(children);//添加回调 final FutureListener<Object> terminationListener = new FutureListener<Object>() { @Override public void operationComplete(Future<Object> future) throws Exception { if (terminatedChildren.incrementAndGet() == children.length) { terminationFuture.setSuccess(null); } } }; for (EventExecutor e: children) { e.terminationFuture().addListener(terminationListener); } Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length); Collections.addAll(childrenSet, children); readonlyChildren = Collections.unmodifiableSet(childrenSet); }chooser是怎么样的呢
private static final class GenericEventExecutorChooser implements EventExecutorChooser { private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; GenericEventExecutorChooser(EventExecutor[] executors) { this.executors = executors; } //每次返回随机集合里面的NioEventLoop public EventExecutor next() { return executors[Math.abs(idx.getAndIncrement() % executors.length)]; } }}
继续进入NioEventLoop的注册方法
public ChannelFuture register(Channel channel) { return register(new DefaultChannelPromise(channel, this)); }
给channel套一个封装类
public ChannelFuture register(final ChannelPromise promise) { ObjectUtil.checkNotNull(promise, "promise");最后又转到unsafe去注册,妈的,想死! promise.channel().unsafe().register(this, promise); return promise; }
最后终于在 NioSocketChannel的内部类 NioSocketChannelUnsafe里找到注册方法
/** * 注册ChannelPromise */ @Override public final void register(EventLoop eventLoop, final ChannelPromise promise) {//进行各方面的检测 if (eventLoop == null) { throw new NullPointerException("eventLoop"); }//不能重复注册 if (isRegistered()) { promise.setFailure(new IllegalStateException("registered to an event loop already")); return; } if (!isCompatible(eventLoop)) { promise.setFailure( new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName())); return; } 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); } } }
方法最终调用doRegister
得到selectionKey */ @Override protected void doRegister() throws Exception { boolean selected = false; for (;;) { try {//注册nio的选择事件,默认0选择所有的事件(读就绪,已连接,写就绪等) selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this); return; } catch (CancelledKeyException e) { if (!selected) { // Force the Selector to select now as the "canceled" SelectionKey may still be // cached and not removed because no Select.select(..) operation was called yet. eventLoop().selectNow(); selected = true; } else { // We forced a select operation on the selector before but the SelectionKey is still cached // for whatever reason. JDK bug ? throw e; } } } }
那么最后就剩开始连接服务器了,连接服务器的最终调用方法:
private static void doConnect( final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromie) { // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up // the pipeline in its channelRegistered() implementation. final Channel channel = connectPromise.channel(); channel.eventLoop().execute(new Runnable() { @Override public void run() { if (localAddress == null) { channel.connect(remoteAddress, connectPromise); } else { channel.connect(remoteAddress, localAddress, connectPromise); } connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } }); }管道最终调用connect方法,调来调去又回到NioSocketChannel调用方法
protectedboolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress)throws Exception {
if (localAddress != null) {
doBind0(localAddress);
}
boolean success = false;
try {
boolean connected =SocketUtils.connect(javaChannel(), remoteAddress);
if (!connected) {
selectionKey().interestOps(SelectionKey.OP_CONNECT);
}
success = true;
return connected;
} finally {
if (!success) {
doClose();
}
}
}
好了,整个连接走通了,下一篇讲解,数据解析,发送的逻辑管道顺序,本人第一次写帖子,以前实在是很懒去写贴子,写了之后才知道其中的辛酸,和不容易,思路也可以更好的撸清,以后要坚持写帖子了,如果你喜欢这篇文章,请转载。
- netty源码深入研究(从客户端入手)第一篇
- netty源码深入研究(从客户端入手)第四篇(读写超时详解)
- netty源码深入研究(从客户端入手)第二篇(详解读消息的管道处理流程)
- netty源码深入研究(从客户端入手)第三篇(详解写消息的管道处理流程)
- 准备研究netty源码
- [Unity3D] unity3d:Unity3d客户端开发,要深入学习,更需要先从哪方面入手?
- cocos2d-x box2d物理引擎深入研究 第一篇续
- redis —— 第一篇 开始入手
- react native实现原理解析(从源码入手,nice)
- 第一篇:netty的介绍
- Netty源码分析:客户端连接
- netty源码分析之客户端
- 从源码入手理解Window和WindowManager
- Netty框架代码(源码)客户端与服务器
- Netty 源码分析之 一 客户端创建(Bootstrap )
- netty(十七)源码分析之客户端创建
- Netty源码分析(二)—客户端初始化
- 仿陌陌客户端源码研究
- python操作yaml文件
- cassandra compaction; how to process tombstone data
- 去除警告信息
- opencv-python中 boundingRect(cnt)以及cv2.rectangle用法
- iOS OC与Swift 项目调试神器
- netty源码深入研究(从客户端入手)第一篇
- java中枚举作用(用途)
- 微信API接口文档
- 51nod 1562 玻璃切割(线段树区间合并)
- Android学习笔记十九之Menu菜单
- 雷鸣的游戏人生(二) --- 新手报到
- 柯里化
- 开发手记--EditText maxLines无效和显示明文密码问题
- Android学习笔记二十之Toast吐司、Notification通知、PopupWindow弹出窗