`
zhhphappy
  • 浏览: 121297 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Netty 源码阅读学习

 
阅读更多

背景 

 

最忌工作中接触到Netty相关应用场景,之前看过mima的部分源码,所以最近看了Netty的部分源码和学习其设计思想,做个简单的分享(学习代码为:Netty:3.6.3.FINALE)。

 

Netty概述

官方:Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

特点:和Mina,Grizzly一样都是基于nio的通信框架,事件驱动,异步非阻塞,高性能,高可靠性。

Mina和Neety的主导作者都是Trustin Lee. Mina是apache社区提供的一个基于NIO的高性能网络应用程序框架,Trustin Lee离开apache后加入redhat开启了Netty,所以两者的设计思路基本一致,目前Netty的更新更加频繁,文档也比较全。下图为netty官网上的一张体系结构图。



 

源码阅读:

先从Netty的ServerBootstrap初始化到bind()过程梳理

 

bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(serverBossTF),
                Executors.newCachedThreadPool(serverWorkerTF)));

bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
       public ChannelPipeline getPipeline() throws Exception {
              ChannelPipeline pipeline = new DefaultChannelPipeline();
              pipeline.addFirst("firewall", firewall);
              pipeline.addLast("decoder", new NettyProtocolDecoder());
              pipeline.addLast("encoder", new NettyProtocolEncoder());
              pipeline.addLast("handler", serverHandler);
              return pipeline;
       }
});

bootstrap.bind(new InetSocketAddress(bindHost, listenPort));

 

 

以上三步为:

1. 初始化ServerBootstrap,指定其ChannelFactory为NioServerSocketChannelFactory(用来创建NioServerSocketChannel)

2. 为bootstrap指定了PipelineFactory,所有Accepted Channel在创建时会使用该工厂创建一个pipeline处理事件。最后的serverHandler为自定义实现的Handler,最终负责处理请求相关业务逻辑等。

3. 绑定一个端口监听请求

从上面三步可以看到Netty的使用还是比较简单,可以自定义的扩展pipeline和实现自己的Handler。

整个过程屏蔽复杂的nio和多线程并发处理的细节,所以最近通过阅读源码了解Netty的部分实现。

 

NioServerSocketChannelFactory关键构造方法:

 

 

public NioServerSocketChannelFactory(BossPool<NioServerBoss> bossPool, WorkerPool<NioWorker> workerPool) {
       if (bossPool == null) {
              throw new NullPointerException("bossExecutor");
       }
       if (workerPool == null) {
              throw new NullPointerException("workerPool");
       }
       this.bossPool = bossPool;
       this.workerPool = workerPool;
       sink = new NioServerSocketPipelineSink();
}

 bossPool 通过new NioServerBossPool(bossExecutor, bossCount, null)创建,这里bossCount默认为1,这里的boss线程通过new NioServerBoss(executor, determiner)创建,在这里完成Selector.open();主要会处理accept事件。

 

workerPool 通过new NioWorkerPool(workerExecutor, workerCount)创建,workerCount为当前可以cpu数×2(Runtime.getRuntime().availableProcessors() * 2 ),通过new NioWorker(executor, determiner)创建,每个worker持有一个Selector;主要处理数据读写操作。

sink NioServerSocketPipelineSink 接收并处理下行(downstream)末端的ChannelEvent,主要负责执行最终ServerSocketChannel绑定以及注册OP_ACCEPT到Selector上。

 

接下来看bootstrap.bind()

// ServerBootstrap.java --> bindAsync()
// 初始一个Binder,Binder继承自SimpleChannelUpstreamHandler,接收并处理上行的ChannelEvent
Binder binder = new Binder(localAddress);
// 初始化一个bossPipeline,之前初始化那个pipeline是工作线程中用的
ChannelPipeline bossPipeline = pipeline();
// 将binder放入pipline中
bossPipeline.addLast("binder", binder);
... ...
// 获取ServerBootstrap初始化时候初始的NioServerSocketChannelFactory来完成服务端channel创建,创建出的是一个NioServerSocketChannel
Channel channel = getFactory().newChannel(bossPipeline);

 NioServerSocketChannel

// 这是真正java nio的Channel,Netty包装了一层channel的概念
socket = ServerSocketChannel.open();
// 设置为非阻塞模式
socket.configureBlocking(false);
// 将open事件作为一个上行事件丢到pipeline中(由对应的handler来handle事件) 
// 这里的handler从前面的初始化来看就是交给binder来处理,binder继承自SimpleChannelUpstreamHandler,可以处理上行事件
fireChannelOpen(this);

 Binder --> channelOpen()

// 执行Channel的bind, 在pipeline中发送一个BOUND的下行事件
evt.getChannel().bind(localAddress)

 DefaultChannelPipeline

// 这里的sink是初始化时上面提到的NioServerSocketPipelineSink
getSink().eventSunk(this, e);

 NioServerSocketPipelineSink --> eventSunk() --> handleServerSocket()

// 这里的boss是初始化时设置的 NioServerBoss
((NioServerBoss) channel.boss).bind(channel, future, (SocketAddress) value);

 在boss的bind方法中new了一个内部的RegisterTask来完成真正的ServerSocket.bind()及注册到selector上,并唤醒Selector

NioServerBoss --> RegisterTask

//java NIO ServerSocketChannel.bind()操作
channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog());
bound = true;
// 更新future
future.setSuccess();
// 出发一个绑定成功的事件
fireChannelBound(channel, channel.getLocalAddress());
// 向selector中注册OP_ACCEPT事件
channel.socket.register(selector, SelectionKey.OP_ACCEPT, channel);

 接下来boss线程和worker线程就要开始干活接客了

NioServerBoss 继承自 AbstractNioSelector,其run方法:

public void run() {
       ...
       for (;;) {
              ...
              // 执行 selector.select(SELECT_TIMEOUT)
              // 阻塞等待客户端连接
              int selected = select(selector);
              ....
              
              // 当请求到达唤醒select,处理具体的事件
              // 该抽象方法由NioServerBoss实现
              process(selector);
       }
       ...
}

protected void process(Selector selector) {
       // 获取select出来的SelectionKey,逐个accept
       Set<SelectionKey> selectedKeys = selector.selectedKeys();
       for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {
              NioServerSocketChannel channel = (NioServerSocketChannel) k.attachment();
              
              // accept connections in a for loop until no new connection is ready
        for (;;) {
                     // NIO 的accept,建立和客户端之间的socket连接
                     SocketChannel acceptedSocket = channel.socket.accept();
                     ....
                     registerAcceptedChannel(channel, acceptedSocket, thread);
              }
              ....
       }
}

//将accept成功的SocketChannel交给工作线程处理读写操作 
private static void registerAcceptedChannel(NioServerSocketChannel parent, SocketChannel acceptedSocket,
       Thread currentThread) {
       ....
       ChannelSink sink = parent.getPipeline().getSink();
       ChannelPipeline pipeline =
                     parent.getConfig().getPipelineFactory().getPipeline();
       NioWorker worker = parent.workerPool.nextWorker();
       worker.register(new NioAcceptedSocketChannel(
                     parent.getFactory(), pipeline, parent, sink
                     , acceptedSocket,
                     worker, currentThread), null);
       ....
}

 

总结

以上过程梳理了Netty初始化的主流程,以及NIO服务端的初始化过程(以上红色部分);顺序梳理完一遍源码,反过来总结下Netty的设计和结构。

代码梳理过程中涉及的核心接口及作用

 

 

Bootstrap:Netty框架启动的工具类,分client,server和udp3种,其核心功能是初始化主channel和pipeline。

ChannelFactory:创建Channel的工厂。针对不同场景,netty提供了各种ChannelFactory,比如ServerChannelFactory用来创建server端的Channel。

Channel:封装了java.nio.channels包中Channel的相关实现,封装了各种IO操作。

ChannelEvent:Netty中几乎所有的操作,都是以事件(ChannelEvent)驱动。事件处理都是通过Channels类的静态方法调用开始的,将事件、channel传递给 channel持有的Pipeline进行处理。ChannelEvent分为两种,upstream events和downstream events。Upstream events指由底层传到高层的事件,一般指socket层发生某些事件通知到应用程序,比如当socket接受到数据后会发送UpstreamMessageEvent事件,交给ChannelHandler来处理。Downstream events指由高层传到底层的事件,一般指用户主动发起的操作,比如用户调用channel.write(buffer),则会生成DownstreamMessageEvent事件,经过ChannelHandler处理后,交给socket层发起通信。

ChannelHandler:真正的处理类,一般由应用程序自己实现。主要分2个子接口ChannelUpstreamHandler和ChannelDownstreamHandler,分别处理UpstreamEvent和DownstreamEvent。

ChannelPipeline:就是由ChannelHandler(ChannelHanlderContext)组成的双向链表。如果是upstream events,则经过ChannelPipeline中由前往后的所有ChannelUpstreamHandler依次处理,最后事件被丢弃。如果是downstream events,则经过ChannelPipeline中由后往前的所有ChannelDownstreamHandler依次处理,最后事件被转交给ChannelSink。

ChannelSink:所有的downstream events经过ChannelPipeline处理后,最后由ChannelSink的eventSunk方法处理。它是管理IO线程和事件处理的桥梁。

ChannelPipeline的处理流程(源码注释中的流程图)

 

 

 

服务端初始化和接收请求抽象流程

 

 

 

Netty中的Reactor模式

使用Doug Lea大师在Scalable IO in Java中的一张图来说明:

 

 

这里的mainReactor对应了Netty中的NioServerBoss(AbstractNioSelector.run()),被分配到bossPool中执行 ,selector.select(SELECT_TIMEOUT)阻塞。

有客户端连接请求被唤醒后执行accept,然后通过worker.register() 交给subReactor的ThreadPool执行(注册OP_READ到NioWorker的Selector中);在netty里为workerPool,其中Worker的线程数由WorkerPoo大小决定,从上面初始化代码中看到默认是可用cpu个数*2。

当客户端有数据写入时,NioWorker持有的Selector被唤醒执行read事件,后会触发messageReceived事件,交给pipeline中的handler执行。

这里事件驱动的关键Selector在不同的操作系统中有不同的实现,系统级的select和pool原理都是通过轮询内核中等待中的FD,轮询到就绪的FD后写用户空间,然后唤醒阻塞的Selector;java1.5版本开始支持epoll,epoll则是在等待的FD上注册回调函数(真正的事件驱动),根本的提升了NIO的性能。

 

 

参考资源

Netty官网:http://netty.io/

Mina官网:http://mina.apache.org/

Trustin Lee :LinkedIn: http://www.linkedin.com/in/trustin

Doug Lea: Scalable IO in Java: http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf

Netty高性能: http://www.infoq.com/cn/articles/netty-high-performance

Netty线程模型:http://www.infoq.com/cn/articles/netty-threading-model

Netty-Mina:http://ifeve.com/netty-mina-in-depth-1/

Select,poll,epoll: http://www.cnblogs.com/xuxm2007/archive/2011/08/15/2139809.html

  • 大小: 48.6 KB
  • 大小: 79.7 KB
  • 大小: 61.6 KB
  • 大小: 56.1 KB
  • 大小: 69.1 KB
1
1
分享到:
评论
3 楼 萨琳娜啊 2018-07-10  
Java读源码之Netty深入剖析
网盘地址:https://pan.baidu.com/s/11kYm2fhJg5CkxIkv0Etvbw 密码: kipg
备用地址(腾讯微云):https://share.weiyun.com/5Bs3HcR 密码:uu95be

JavaCoder如果没有研究过Netty,那么你对Java语言的使用和理解仅仅停留在表面水平,如果你要进阶,想了解Java服务器的深层高阶知识,Netty绝对是一个必须要过的门槛。

本课程带你从一个Socket例子入手,一步步深入探究Netty各个模块的源码,深入剖析Netty的工作流程和源码设计,让你不但“真懂”也要“会用”
2 楼 小灯笼 2018-06-06  
Netty源码剖析视频教程
网盘地址:https://pan.baidu.com/s/1bvqEWNLisEcJ6OCHYhlcsw 密码: f4e4
备用地址(腾讯微云):http://url.cn/5R2xYI4 密码:OmbopD
1 楼 smart152829 2017-07-01  

很不错的文章,最近看了看了夜行侠老师讲的Netty深入浅出源码剖析,里面用到了长连接高并发,在测试的时候,性能真的比之前的tomcat那些容器好几十倍

相关推荐

    netty源码剖析视频.zip

    第一部分,深入浅出Netty源码剖析,这一章节将带领学习者逐步揭开Netty的神秘面纱。Netty是Java领域里一个高性能、异步事件驱动的网络应用框架,广泛应用于分布式系统、云计算以及大数据传输等领域。在这一部分,...

    Netty全套学习资源(包括源码、笔记、学习文档等)

    通过阅读源码,我们可以了解到 Netty 如何实现高效的网络通信,例如它的非阻塞 I/O 模型、事件驱动架构、内存池管理以及编码解码器等核心组件。其中,Channel、EventLoop、ByteBuf 等关键类是理解 Netty 的基础,而 ...

    netty源码分析教程视频

    一个netty的入门教程以及源码分析视频,适合刚学习的人

    netty源码深入分析

    通过本课程的学习,开发者不仅能够掌握如何使用Netty开发高性能的应用程序,还能够学会如何阅读和理解其源代码,从而更好地进行定制化开发和故障排查。 ### Netty核心组件解析 1. **Channel**:Netty中的Channel是...

    netty源码最新版

    总的来说,通过研究 Netty 的源码,开发者不仅可以掌握网络编程的最佳实践,还能学习到许多 Java 高级特性和设计模式的应用,从而提升自身的编程技能。无论是开发高性能的网络应用,还是进行系统优化,Netty 源码都...

    netty源码和相关中文文档

    通过学习 Netty 的源码,开发者可以更好地理解和优化其在网络通信中的性能表现,比如减少内存分配、提高并行度以及优化编码解码过程等。同时,配合中文文档,可以降低学习门槛,让开发者更快地掌握 Netty 的使用技巧...

    Netty-4.1.97.Final源码

    最后,学习Netty源码不仅可以帮助你深入理解网络编程,还能提升你在并发、内存管理、性能优化等方面的技术水平。对于Java程序员来说,这是一项非常有价值的投资,能够使你在解决复杂问题时游刃有余。 总之,Netty-...

    【项目实战】Netty源码剖析&NIO;+Netty5各种RPC架构实战演练三部曲视频教程(未加密)

    通过对Netty源码的深入剖析以及NIO技术的学习,我们不仅能够理解Netty内部的工作原理,还能掌握如何利用Netty高效地开发高性能的网络应用程序。尤其是在RPC架构领域,Netty凭借其强大的功能和灵活的设计成为了构建...

    Netty 完整依赖的jar包, 你只需要下载netty源码,再添加这些jar就可以编译通过了

    在描述中提到的"只需要下载netty源码,再添加这些jar就可以编译通过了",这意味着你需要获取Netty的源代码仓库,通常可以从GitHub等开源平台获得。源代码包含了Netty的所有模块和组件,可以让你深入了解其内部工作...

    netty3.2源代码

    这个“netty3.2源代码”包含了 Netty 框架的3.2版本的源码,让我们有机会深入理解其内部机制。 Netty 的核心特性在于它的异步事件驱动模型。这种模型允许程序处理多个连接同时进行,而不是像传统的同步模型那样,每...

    NIO+Netty5视频教程与Netty源码剖析视频教程

    《NIO+Netty5视频教程与Netty源码剖析视频教程》是一份全面解析网络编程框架Netty的教育资源,旨在帮助学习者深入理解和应用NIO(非阻塞I/O)以及Netty5的核心技术。该教程分为两个主要部分,分别针对理论分析和实战...

    Netty源码依赖包

    通过对Netty源码依赖包的学习,不仅可以帮助开发者更好地理解Netty的设计理念和技术细节,还能在实际工作中解决具体问题时提供强大的支持。无论是想要深入了解Netty的内部机制还是希望通过学习优秀的开源项目来提升...

    netty源码解析视频

    ### Netty源码解析知识点概览 #### 一、Netty简介与应用场景 - **Netty**是一款由JBOSS提供的高性能的...以上是对“netty源码解析视频”教程涉及的主要知识点进行的详细梳理和解释,希望对你学习Netty源码有所帮助。

    netty源码 4.*版本

    通过阅读 Netty 源码,我们可以学习到以下知识点: - 理解 Java NIO 的工作原理,如何利用 Selector 监听多个 Channel 的事件。 - 掌握 ByteBuf 的内存管理策略,如何避免不必要的内存拷贝。 - 学习如何构建 ...

    netty源码包

    netty源码包,可以本地搭建netty源码环境,学习nio模式

    netty4.0源码,netty例子,netty api文档

    这个压缩包包含的是Netty 4.0.0.CR3版本的相关资源,包括源码、示例以及API文档,对于学习和理解Netty的工作原理以及如何在实际项目中应用Netty非常有帮助。 首先,让我们来详细探讨一下Netty的核心特性: 1. **...

    netty权威指南和源码

    压缩包子文件 "netty权威指南完整版+源码" 提供了完整的书籍资源和Netty的源代码,这将帮助开发者在理论学习的同时,结合实际代码进行实践,加深理解。 现在,让我们详细讨论Netty的一些核心知识点: 1. **事件...

    Netty in Action 中文版 源代码

    通过学习《Netty in Action》中文版源代码,读者不仅可以理解Netty的基本用法,还能掌握如何构建高性能、低延迟的网络应用。作者的本地优化让这份资料更加适合中国开发者,使得理解Netty背后的原理和实践变得更加...

    netty_learn_netty_源码.zip

    通过阅读和分析这个“netty_learn_netty_源码.zip”中的源代码,你可以深入了解Netty如何实现这些功能,以及它是如何优化网络通信效率的。此外,你还可以学习到Netty如何处理异常、优雅地关闭连接、线程安全等问题,...

Global site tag (gtag.js) - Google Analytics