`
vinceall
  • 浏览: 10708 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Netty源码分析2---服务端读写流程

阅读更多

上次分析了服务端bind流程,今天继续看服务端读写流程。

 

术语:worker---NioWorker对象,BT---boss线程,IOT---worker线程,UT---用户线程

 

先说一下前提条件:所有与具体连接相关的IO操作都是由IOT负责完成的,并且handler也是在IOT执行的,所以才说耗时的操作要自己起线程,不要交给IOTIOT不是拿给你独占的。

 

服务端读

① bind并注册OP_ACCEPTselector后,BT一直不停轮询selection key,当有连接上来时acceptnew一个NioAcceptedSocketChannel对象channel,把它传给NioWorker.RegisterTask对象,投递给IOT,让IOT去注册OP_READ到它对应的workerselector上(注册时把channel当做attachment同时注册上去):

 

public void run() {

            SocketAddress localAddress = channel.getLocalAddress();

            SocketAddress remoteAddress = channel.getRemoteAddress();

            if (localAddress == null || remoteAddress == null) {

                if (future != null) {

                    future.setFailure(new ClosedChannelException());

                }

                close(channel, succeededFuture(channel));

                return;

            }

            try {

                if (server) {

                    channel.channel.configureBlocking(false);

                }

                channel.channel.register(

                        selector, channel.getRawInterestOps(), channel);

……

NioWorker内部类RegisterTask.run

 

然后IOT去轮询上面的selectorselection key

 

② 当收到数据时,selector返回selection key,从中可以获取注册时放进去的channel(个人认为attachment可以作为一种保存物理连接和逻辑连接映射关系的手段),然后从channel中读取数据。

 

③ NioWorker.read方法负责具体读数据,主要逻辑是调channel.read读数据,前面是根据预设的sizerecvBufferPool缓存池中获得一个ByteBuffer,以及设置字节顺序等操作。成功读完就清理ByteBuffer,把channelbuffer传给handler,然后fireMessageReceived。通常我们处理request的业务逻辑就放在这里,所以从这儿也可以看出确实是IOT在执行messageReceivedIOT耗不起啊。。。如果读失败则fireExceptionCaught

 

服务端写

写是IOT负责的,发起写数据请求的可能是IOTmessageReceived发起的写就是IOT本身)或者其它线程(如UT

 某线程在Netty层发起写操作,经过之前讲的down stream层层处理、转发后,最终到NioServerSocketPipelineSink.eventSunkàhandleAcceptedSocket,从MessageEvent中获取channel和消息,将消息放入channel对应的writeBufferQueue中,这一步实现了数据的入队操作。

 

 然后继续调worker.writeFromUserCode,其中对当前线程是否为IOT作了判断:

    1) 如果不是则投递一个writeTaskworker.taskQueue上,等IOT下次processTaskQueuepoll出来执行,这一步实现了writeTask的入队操作。

    2) 如果是则直接调write0写数据。

  这样设计的优势:可以避免当前线程是IOT时,投递task带来的线程切换开销,因为当前线程是IOT时,如果直接投递,则只能等下一次IOT获取到CPU进行循环时才能从taskQueue pollwriteTask了,这样的话既有线程切换开销,还会带来延迟,所以判断一次可以优化写的效率。

 

③ 如果是UT投递的任务,会调writeFromTaskLoop,另外还有个writeFromSelectorLoop,是当selectOP_WRITEIOT发起的,用户发起的写操作都是调用writeFromUserCode,这三个write方法最终都调用write0

 

④ write0内部流程:

    1) channel.writeLock加锁,锁住写操作,目的是防止其它线程调cleanUpWriteBuffer,从writeBufferQueuepoll,导致write0 poll不到任务(例如其它线程调channel.close就会去操作writeBufferQueue

    2) 检查channel.currentWriteEvent,若未被清空则说明之前的写操作还未完成,则继续从currentWriteBuffer中获取之前的byteBuf;若已清空则说明上次写操作成功完成,此时则从writeBufferQueuepollbyteBuf,然后将byteBuf包装为sendBuffer

    3) 根据预先配置的writeSpinCount,尝试多次写入数据,类似于自旋,这里作了写优化:当selectOP_WRITE,而在写入时返回0,不一定代表连接被关闭。在该情况下,一般可通过再次注册OP_WRITE等待下次select,但缺点是selectOS发起的系统调用,涉及到用户态和内核态的切换,开销大。所以这里的优化方式是通过自旋多尝试几次,尽量延迟注册。

    4) 发送完后清空currentWriteBuffer等,若未写完(可能kernel buffer满了)则设addOpWritechannel.writeSuspended标记,前者用于再次注册OP_WRITE,等kernel buffer可用时由发起writeFromSelectorLoop,并根据channel是否open确定是addOpWrite or removeOpWrite;后者控制的是用户发起的和taskQueue /selector发起的写操作,同时只能有一个。

    5) 最后根据当前线程是否为IOT,确定fireWriteComplete/fireWriteCompleteLater

注:write时判断若为IOT则直接写的缺点是当前线程若被中断会引起channel关闭,这个还不理解。。。

 

总的来说读写都不复杂,读比写简单,最近时间稍微多一点,简单分析了一下服务端的线程模型,有些自己的理解,也不晓得对不对,改天贴上来大家讨论。

 

本人辛苦分析、码字,请尊重他人劳动成果,转载不注明出处的诅咒你当一辈子一线搬砖工,嘿嘿~

欢迎讨论、指正~~

 

下篇预告:服务端线程模型分析

1
0
分享到:
评论

相关推荐

    netty源码深入分析

    《Netty源码深入分析》是由美团基础架构部的闪电侠老师所分享的一系列关于Netty源码解析的视频教程。以下将根据标题、描述、标签以及部分内容等信息,对Netty及其源码进行深入剖析。 ### Netty简介 Netty是基于...

    netty源码分析之服务端启动全解析

    Netty是一款高性能的网络应用程序框架,它使用Java编程语言开发,主要用于网络应用程序的快速和易于开发,支持TCP和UDP...通过对Netty源码的深入分析,可以更好地理解其工作机制,对开发高性能的网络应用有极大的帮助。

    netty源码解析视频

    #### 五、Netty源码分析实战案例 1. **ChannelHandlerContext与ChannelHandlerAdaptor详解**: - 分析`ChannelHandlerContext`的生命周期及其与`ChannelHandler`之间的交互方式。 - 深入理解`...

    Netty源码依赖包

    在深入探讨Netty源码依赖包的相关知识点之前,我们首先需要了解Netty的基本概念及其重要性。Netty是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器与客户端。它提供了丰富的...

    使用jboss netty 创建高性能webservice客户端及服务端

    不过,这只是基础,深入研究Netty源码可以帮助我们更好地理解和优化系统性能,例如了解其内部的事件调度机制、缓冲区管理以及线程模型等。 最后,标签中的"源码"提示我们要关注Netty的底层实现,通过阅读和分析源码...

    java Netty 框架例子源码.rar

    Java Netty 是一个高性能、异步...通过分析这些示例源码,我们可以深入理解 Netty 的工作原理,掌握如何在实际项目中构建高性能的网络应用。同时,这也将有助于我们了解如何处理异常、实现安全性以及优化网络通信性能。

    Netty框架网络编程实战-Netty_chat.zip

    - 分析项目源码,了解ServerBootstrap和Bootstrap的配置过程。 - 理解ChannelHandler和ChannelPipeline的事件处理机制。 - 学习如何编写自定义的Encoder和Decoder。 - 实践客户端和服务器的交互,调试运行聊天...

    netty源码深入剖析.txt

    《Netty源码深入剖析》一书旨在帮助读者深入了解Netty框架的工作原理和技术细节,从基础知识入手,逐步过渡到高级优化技巧,使开发者能够更好地掌握并应用Netty于实际项目中。 ### 一、Netty简介与核心特性 Netty...

    Netty框架源码(并且有详细的注释)

    Netty是一个高性能、异步事件驱动的网络应用框架...总的来说,这个压缩包提供了一个深入学习Netty源码的机会,通过阅读和实践,开发者能够掌握Netty的设计理念,提升网络编程的能力,并能根据需求定制自己的网络框架。

    netty5.0架构剖析和源码解读

    Netty源码的分析主要围绕服务端和客户端的创建、读写操作等方面进行。服务端的启动涉及到ServerBootstrap类,以及NioServerSocketChannel的注册,新的客户端接入,客户端连接的建立通过Bootstrap类实现。读写操作则...

    netty权威指南例子源码(第2-7章)

    以下是基于该书第二至第七章的源码分析,涵盖的关键知识点: 1. **NIO基础**: - Netty是基于Java NIO(非阻塞I/O)构建的,理解Java NIO的基本原理,包括通道(Channels)、缓冲区(Buffers)和选择器(Selectors...

    javanetty源码-java_study-:netty+java(源代码参考)

    通过阅读和分析Netty源码,开发者能够更好地理解其内部工作原理,优化网络应用,以及定制适合自己项目的特性和功能。对于Java开发者来说,深入学习Netty源码不仅可以提升网络编程技能,也有助于解决实际问题,比如...

    Netty3.x 源码解析

    由于Netty源码较庞大,所以文章建议通过分析几个关键组件来构建对整个框架的理解,从而避免在庞大的源码海洋中迷失方向。通过理解Netty的关键点,包括NIO的使用、事件处理机制、ChannelPipeline的运作以及Handler的...

    netty视频详解(90节大长篇)

    - **源码分析**: - **初始化过程**:分析如何创建Channel、EventLoop等组件,并配置ChannelPipeline。 - **事件循环机制**:深入了解EventLoop的工作原理,包括如何调度任务、处理I/O事件等。 - **编解码器设计*...

    socket长连接,netty服务器与android源码

    在分析源码时,应关注以下几个关键点: 1. Android客户端如何创建Socket连接并保持连接。 2. 如何在Android中处理网络错误和异常,确保长连接的稳定性。 3. Netty服务器如何处理新连接,以及数据的读写逻辑。 4. ...

    Netty权威指南 第二版 源码

    源码分析部分可能涵盖以下几个方面: 1. **Channel**:理解Channel接口及其实现类,如何表示网络连接,并进行读写操作。 2. **Bootstrap与ServerBootstrap**:这两者是启动客户端和服务端的配置类,涉及连接建立、...

    精通并发与netty视频教程(2018)视频教程

    47_Netty服务器与客户端编码模式回顾及源码分析准备 48_Netty与NIO系统总结及NIO与Netty之间的关联关系分析 49_零拷贝深入剖析及用户空间与内核空间切换方式 50_零拷贝实例深度剖析 51_NIO零拷贝彻底分析与Gather...

Global site tag (gtag.js) - Google Analytics