2024年9月reactornetty源码(如何编译 netty 源码并导入android studio)

 更新时间:2024-09-21 08:44:51

  ⑴reactorty源码(如何编译ty源码并导入androidstudio

  ⑵如何编译ty源码并导入androidstudio

  ⑶修改AndroidStudio(以下简称AS)的内存配置因为在导入源码时需要消耗大量内存,所以先修改IDEA_HOME/bin/studio.vmoptions中-Xms和-Xmx的值。文档中使用的是m,可自行修改。二、配置AS的JDK、SDK在IDE中添加一个没有classpath的JDK,这样可以确保使用源码里的库文件并将其作为要使用的SDK的JavaSDK。三、生成导入AS所需配置文件(*.ipr)①编译源码(为了确保生成了.java文件,如R.java;如果编译过,则无需再次编译)②检查out/host/linux-x/framework/目录下是否有idegen.jar如果idegen.jar不存在,执行:mmmdevelopment/tools/idegen/在..的源码中会生成res.java的文件夹,导致idegen.jar运行时抛FileNotFoundException,这是idegen的代码不够严谨造成的。我的分享里有修改这个bug的patch,或者直接使用我分享的idegen.jar。③执行development/tools/idegen/idegen.sh等待出现类似下面的结果:Readexcludes:msTraversedtree:ms这时会在源码的根目录下生成android.ipr和android.iml两个IntelliJIDEA(AS是基于IntelliJIDEA社区版开发的)的配置文件

  ⑷ty源码解析———ChannelConfig和Attribute

  ⑸嗯,本文与其说是ChannelConfig、Attribute源码解析,不如说是对ChannelConfig以及Attribute结构层次的分析。因为这才是它们在ty中使用到的重要之处。

  ⑹在ty源码解析———服务端启动流程(下)中说过,当我们在构建NioServerSocketChannel的时候同时会构建一个NioServerSocketChannelConfig对象赋值给NioServerSocketChannel的成员变量config。

  ⑺而这一个NioServerSocketChannelConfig是当前NioServerSocketChannel配置属性的集合。NioServerSocketChannelConfig主要用于对NioServerSocketChannel相关配置的设置(如,网络的相关参数配置),比如,配置Channel是否为非阻塞、配置连接超时时间等等。

  ⑻NioServerSocketChannelConfig其实是一个ChannelConfig实例。ChannelConfig表示为一个Channel相关的配置属性的集合。所以NioServerSocketChannelConfig就是针对于NioServerSocketChannel的配置属性的集合。

  ⑼ChannelConfig是Channel所需的公共配置属性的集合,如,setAllocator(设置用于channel分配buffer的分配器)。而不同类型的网络传输对应的Channel有它们自己特有的配置,因此可以通过扩展ChannelConfig来补充特有的配置,如,ServerSocketChannelConfig是针对基于TCP连接的服务端ServerSocketChannel相关配置属性的集合,它补充了针对TCP服务端所需的特有配置的设置setBacklog、setReuseAddress、setReceiveBufferSize。

  ⑽DefaultChannelConfig作为ChannelConfig的默认实现,对ChannelConfig中的配置提供了默认值。

  ⑾接下来,我们来看一个设置ChannelConfig的流程:serverBootstrap.option(ChannelOption.SO_REUSEADDR,true);我们可以在启动服务端前通过ServerBootstrap来进行相关配置的设置,该选项配置会在Channel初始化时被获取并设置到Channel中,最终会调用底层ServerSocket.setReuseAddress方法来完成配置的设置。ServerBootstrap的init()方法:

  ⑿首先对option和value进行校验,其实就是进行非空校验。然后判断对应的是哪个常量属性,并进行相应属性的设置。如果传进来的ChannelOption不是已经设定好的常量属性,则会打印一条警告级别的日志,告知这是未知的channeloption。ty提供ChannelOption的一个主要的功能就是让特定的变量的值给类型化。因为从’ChannelOption《T》option’和’Tvalue’可以看出,我们属性的值类型T,是取决于ChannelOption的泛型的,也就属性值类型是由属性来决定的。

  ⒀这里,我们可以看到有个ChannelOption类,它允许以类型安全的方式去配置一个ChannelConfig。支持哪一种ChannelOption取决于ChannelConfig的实际的实现并且也可能取决于它所属的传输层的本质。

  ⒁可见ChannelOption是一个Consant扩展类,Consant是ty提供的一个单例类,它能安全去通过’==’来进行比较操作。通过ConstantPool进行管理和创建。常量由一个id和name组成。id:表示分配给常量的唯一数字;name:表示常量的名字。

  ⒂如上所说,Constant是由ConstantPool来进行管理和创建的,那么ConstantPool又是个什么样的类了?

  ⒃首先从constants中get这个name对应的常量,如果不存在则调用newConstant()来构建这个常量tempConstant,然后在调用constants.putIfAbsent方法来实现“如果该name没有存在对应的常量,则插入,否则返回该name所对应的常量。(这整个的过程都是原子性的)”,因此我们是根据putIfAbsent方法的返回来判断该name对应的常量是否已经存在于constants中的。如果返回为null,则说明当前创建的tempConstant就为name所对应的常量;否则,将putIfAbsent返回的name已经对应的常量值返回。(注意,因为ConcurrentHashMap不会允许value为null的情况,所以我们可以根据putIfAbsent返回为null则代表该name在此之前并未有对应的常量值)

  ⒄正如我们前面所说的,这个ConstantPool《ChannelOption《Object》》pool(即,ChannelOption常量池)是ChannelOption的一个私有静态成员属性,用于管理和创建ChannelOption。

  ⒅这些定义好的ChannelOption常量都已经存储数到ChannelOption的常量池(ConstantPool)中了。

  ⒆注意,ChannelOption本身并不维护选项值的信息,它只是维护选项名字本身。比如,“publicstaticfinalChannelOption《Integer》SO_RCVBUF=valueOf(“SO_RCVBUF“);”?这只是维护了“SO_RCVBUF”这个选项名字的信息,同时泛型表示选择值类型,即“SO_RCVBUF”选项值为Integer。

  ⒇好了,到目前为止,我们对ty的ChannelOption的设置以及底层的实现已经分析完了,简单的来说:ty在初始化Channel时会构建一个ChannelConfig对象,而ChannelConfig是Channel配置属性的集合。比如,ty在初始化NioServerSocketChannel的时候同时会构建一个NioServerSocketChannelConfig对象,并将其赋值给NioServerSocketChannel的成员变量config,而这个config(NioServerSocketChannelConfig)维护了NioServerSocketChannel的所有配置属性。比如,NioServerSocketChannelConfig提供了setConnectTimeoutMillis方法来设置NioServerSocketChannel连接超时的时间。同时,程序可以通过ServerBootstrap或Boostrap的option(ChannelOption《T》option,Tvalue)方法来实现配置的设置。这里,我们通过ChannelOption来实现配置的设置,ChannelOption中已经将常用的配置项预定义为了常量供我们直接使用,同时ChannelOption的一个主要的功能就是让特定的变量的值给类型化。因为从’ChannelOption《T》option’和’Tvalue’可以看出,我们属性的值类型T,是取决于ChannelOption的泛型的,也就属性值类型是由属性来决定的。

  ⒈一个attribute允许存储一个值的引用。它可以被自动的更新并且是线程安全的。其实Attribute就是一个属性对象,这个属性的名称为AttributeKey《T》key,而属性的值为Tvalue。

  ⒉我们可以通过程序ServerBootstrap或Boostrap的attr方法来设置一个Channel的属性,如:serverBootstrap.attr(AttributeKey.valueOf(“userID“),UUID.randomUUID().toString());当ty底层初始化Channel的时候,就会将我们设置的attribute给设置到Channel中:

  ⒊如上面所说,Attribute就是一个属性对象,这个属性的名称为AttributeKey《T》key,而属性的值为Tvalue。而AttributeKey也是Constant的一个扩展,因此也有一个ConstantPool来管理和创建,这和ChannelOption是类似的。

  ⒋Channel类本身继承了AttributeMap类,而AttributeMap它持有多个Attribute,这些Attribute可以通过AttributeKey来访问的。所以,才可以通过channel.attr(key).set(value)的方式将属性设置到channel中了(即,这里的attr方法实际上是AttributeMap接口中的方法)。

  ⒌AttributeKey、Attribute、AttributeMap间的关系:AttributeMap相对于一个map,AttributeKey相当于map的key,Attribute是一个持有key(AttributeKey)和value的对象。因此在map中我们可以通过AttributeKeykey获取Attribute,从而获取Attribute中的value(即,属性值)。

  ⒍Q:ChannelHandlerContext和Channel都提供了attr方法,那么它们设置的属性作用域有什么不同了?A:在ty.版本之前,它们两设置的属性作用域确实存在着不同,但从ty.版本开始,它们两设置的属性的作用域已经完全相同了。

  ⒎若文章有任何错误,望大家不吝指教:)

  ⒏圣思园《精通并发与ty》

  ⒐ty源码_UnpooledHeapByteBuf详解

  ⒑本篇文章我们讲解缓存区ByteBuf八大主要类型中两种,未池化堆缓冲区UnpooledHeapByteBuf和未池化不完全堆缓冲区UnpooledUnsafeHeapByteBufUnpooledHeapByteBuf是java堆缓冲区的实现,而且它推荐使用UnpooledByteBufAllocator.heapBuffer(int,int),Unpooled.buffer(int)和Unpooled.wrappedBuffer(byte)方式创建UnpooledHeapByteBuf,而不是直接调用它的构造方法new出来。有三个成员属性:它有两个构造方法,一个是创建的时候没有内容,一个创建的时候就带有内容数据。分配新的字节数组。替换缓存区的字节数组array,必须将tmpNioBuf设置为null。你会发现最后都是调用HeapByteBufUtil对应方法,这个类HeapByteBufUtil我们后面再说。根据目标缓存区dst类型不同,使用的方式也不同。FileChannel就是GatheringByteChannel的子类。都是调用HeapByteBufUtil对应方法,这个类HeapByteBufUtil我们后面再说。我们知道这个方法是在AbstractReferenceCountedByteBuf类中定义的,当引用计数变成的时候,就会调用这个deallocate()方法,释放持有的资源。仔细阅读UnpooledUnsafeHeapByteBuf源码,你会发现这个类很简单,它是UnpooledHeapByteBuf的子类,与UnpooledHeapByteBuf区别就两个方面。UnpooledUnsafeHeapByteBuf是通过PlatformDependent.allocateUninitializedArray创建数组,利用Unsafe来加快数据的访问。UnpooledUnsafeHeapByteBuf是通过UnsafeByteBufUtil工具类获取基本数据类型的数据。先明确一个概念,什么是大端,什么是小端。例如获取short就是通过右移位运算和或|位运算,实现数的拼接。利用左移位运算,将高位数据转换成byte类型存储;再使用(byte)类型强转,只保留低八位的数据存储。

  ⒒ty源码_内存管理(jemalloc)

  ⒓在内存管理(jemalloc)这篇文章中,我们介绍了在ty..版本之前使用的内存分配jemalloc算法。对比上面两图,jemalloc内存分配算法和jemalloc内存分配算法的确有很大不同啊。jemalloc算法将内存分为三种类型:先解释每个表头的含义:仔细观察表中数据,我们得出如下特点:我们知道内存规格内存大小的计算公式除第一组外,每一组的nDelta从开始,logGroup==logDelta+,并且每一组logGroup和logDelta都比上一组增加了;因此每一组第一个内存规格正好是上一个组第一个内存规格大小的两倍。这个类的作用就是处理jemalloc算法的内存大小分配的。

  ⒔reactor-ty中HttpClient对TcpClient的封装

  ⒕本文主要研究一下reactor-ty中HttpClient对TcpClien的封装reactor-ty-...RELEASE-sources.jar!/rea

  ⒖ty源码-内存泄漏检测toLeakAwareBuffer

  ⒗ty在实现ByteBuf时采用了引用计数法进行ByteBuf的回收,使用引用计数法进行回收的ByteBuf都扩展了AbstractReferenceCountedByteBuf类,在使用AbstractReferenceCountedByteBuf时需要调用AbstractReferenceCountedByteBuf.retain方法递增引用计数器,在使用完毕时则需要调用AbstractReferenceCountedByteBuf.release方法递减引用计数器,当计数器为时,会进行ByteBuf的回收工作:池化的ByteBuf不会进行实际的内存释放,会将占用的内存归还给内存池,非池化的ByteBuf则会直接释放内存(为了叙述简单,后面释放内存则指真正释放内存或者将内存归还给内存池。

  ⒘通过上面的描述可知,ByteBuf的正确回收依赖retain和release方法的正确调用,内存提前释放(即在使用ByteBuf时没有调用retain方法,导致提前释放应用会报错,用户也能及时感知到;但是如果使用完ByteBuf忘了调用release则会导致内存不能及时得到回收,造成内存泄漏,且内存泄漏用户无法及时感知,久而久之就会发生OOM。为了解决这种问题,ty采用了内存泄漏检测机制,发生内存泄漏时会通过日志将内存泄漏信息打印出来,报告给用户。

  ⒙ty的内存泄漏检测使用了WeakReference,即弱引用,了解过Java四种引用类型(强、软、弱、虚和引用队列(ReferenceQueue的读者知道,弱引用持有的对象会在虚拟机触发GC时(不管回收之后内存是否够用被回收掉,如果使用具有引用队列参数的构造函数实例化WeakReference时,弱引用持有的对象在GC被回收时,弱引用自身会被放入引用队列。

  ⒚为了后面能更好的理解ty内存泄漏检测的细节,下面先看几个弱引用的例子,在下面的几个例子中,我们使用的数据类和自定义的弱引用类子类如下:

  ⒛好了,三个例子已经介绍完毕,后面在介绍ty内存泄漏检测时就使用了这里的例子结果,在具体介绍时会和这里的例子一一对应。

  ty中将普通ByteBuf转为具有内存泄漏检测功能的ByteBuf是通过AbstractByteBufAllocator.toLeakAwareBuffer方法实现的,我们直接在Eclipse中看该方法的调用层次即可知道ty在哪里对ByteBuf进行了转换,该方法调用如下图所示:

  可见池化内存分配器在分配heap或者directByteBuf时都进行了转换,非池化内存分配器仅在分配directByteBuf时进行了转换。个人理解时采用池化内存需要特别关注内存释放,否则为了实现池化内存预先分配的一大块内存会因为没有释放被很快分配完,造成后面没有内存进行分配。非池化分配的直接内存也需要特别注意释放,放置内存泄漏;非池化分配的heap内存(其实就是一个byte数组则可以在对象被回收时同时被回收掉,发生内存泄漏的可能性较小。

  本节介绍ty中内存泄漏检测相关的类,仅做一个大致介绍,类中的重要方法我们放在后面介绍。

  主要负责使用track方法对指定的ByteBuf进行内存检测泄漏进行追踪,并返回负责追踪的ResourceLeakTracker类实例,同时在调用track方法时,也会根据指定的检测级别汇报最近的内存泄漏检测结果。该类由工厂类ResourceLeakDetectorFactory负责实例化,默认的实现为ResourceLeakDetector,在ResourceLeakDetectorFactory类的默认实现DefaultResourceLeakDetectorFactory中,也会根据用户是否配置了io.ty.customResourceLeakDetector来决定采用默认实现ResourceLeakDetector还是使用用户自定义的ResourceLeakDetector,用户自定义的ResourceLeakDetector必须是其子类。

  默认实现为DefaultResourceLeak,DefaultResourceLeak实现了ResourceLeakTracker和ResourceLeak接口,同时也继承了类WeakReference,是一个弱引用实现。首先,同上面例的结果一样,如果在使用ByteBuf时忘了调用AbstractReferenceCountedByteBuf.release方法,那么将不会调用DefaultResourceLeak.clear方法去手动清空该弱引用持有的实际对象,在发生GC时,会由垃圾收集器对弱引用持有的实际对象进行回收,即发生了内存泄漏,同时该弱引用自身也会被加入到引用队列中,该引用队列是ResourceLeakDetector的成员域,上面介绍ResourceLeakDetector类时说到该类会在用户track指定ByteBuf是汇报检测结果,该类的汇报数据来源就是引用队列。DefaultResourceLeak同时还提供了record方法可以让用户在指定时机选择调用,这个方法可以记录用户的调用轨迹(堆栈。Record同时也是一种单链表,在DefaultResourceLeak中就使用单链表记录用户的调用轨迹。

  DefaultResourceLeak供用户记录程序调用轨迹的类,也就是DefaultResourceLeak.record方法返回的对象,继承自Throwable,因此可以使用Throwable.getStackTrace方法获得调用轨迹信息,打印在内存泄漏报告中可以让用户更好的排除内存泄漏问题。

  在上面介绍ResourceLeakTracker时,说到其默认实现为DefaultResourceLeak,DefaultResourceLeak提供了record方法记录用户的调用轨迹,用户可在调用ByteBuf方法时调用record方法记录调用轨迹,调用的频率越多,后面在汇报内存泄漏情况时就能打印出越详细的信息,这样也能更方便的排查问题。

  ty提供了两个ByteBuf的封装类供选择,就对应不同的record调用频率,每个封装类都持有ResourceLeakTracker对象,ty根据配置的内存检测级别(下一节介绍相关配置参数使用不同的ByteBuf封装类。

  ty提供的两个ByteBuf封装类就是SimpleLeakAwarepositeByteBuf和AdvancedLeakAwarepositeByteBuf,AdvancedLeakAwarepositeByteBuf是SimpleLeakAwarepositeByteBuf的子类,SimpleLeakAwarepositeByteBuf类仅仅持有ResourceLeakTracker对象,但是看其源码,发现没有调用过record方法,所以只能知道是否发生了内存泄漏时,无法打印出任何调用轨迹信息。AdvancedLeakAwarepositeByteBuf作为SimpleLeakAwarepositeByteBuf的子类,在ByteBuf的多个方法中调用了record方法,所以在发生内存泄漏时,能够打印出比较详细的调用轨迹信息。

  在AdvancedLeakAwarepositeByteBuf类中使用了配置参数io.ty.leakDetection.acquireAndReleaseOnly来控制是否只是在调用增加或减少引用计数器的方法时才调用record方法记录调用轨迹,默认为false。AdvancedLeakAwarepositeByteBuf中retain和release方法因为改变了引用计数器就直接调用了record方法,而该类中的其他方法则根据io.ty.leakDetection.acquireAndReleaseOnly的配置决定是否调用record方法,这里为了节省篇幅就不列出AdvancedLeakAwarepositeByteBuf类中调用record的方法了,读者可自行查看。

  在介绍相关配置参数之前,我们先看下ty提供的内存泄漏检测级别:

  Level.ADVANCED和Level.PARANOID使用的ByteBuf包装类都是AdvancedLeakAwarepositeByteBuf,我们上面介绍ResourceLeakDetector类时提到该类使用track方法对指定的ByteBuf进行内存检测泄漏进行追踪,并返回负责追踪的ResourceLeakTracker类实例,同时在调用track方法时,也会根据指定的检测级别汇报最近的内存泄漏检测结果。如果内存泄漏检测级别为Level.PARANOID时则每次调用track方法都会进行内存泄漏报告;如果级别为Level.ADVANCED或者Level.SIMPLE则会以一定频率进行内存泄漏报告,而不是每次track都进行报告。

  是否关闭ty内存泄漏检测功能,默认为false。如果该参数配置为false,则默认的内存泄漏检测级别根据此参数的配置为Level.DISABLED,否则默认的级别为Level.SIMPLE。

  配置内存泄漏检测级别的参数,用于老版本的配置参数。

  新的内存泄漏检测级别参数,如果没有配置,则会采用老版本参数配置的级别作为最终配置。

  在第节介绍内存泄漏检测相关类时,我们介绍过DefaultResourceLeak提供了record方法记录用户的调用轨迹,如果当前保存的调用轨迹记录数Record大于参数io.ty.leakDetection.targetRecords配置的值,那么会以一定的概率(/^n删除头结点之后再加入新的记录,当然也有可能不删除头结点直接新增新的记录。

  上面介绍过,在AdvancedLeakAwarepositeByteBuf类中使用了配置参数io.ty.leakDetection.acquireAndReleaseOnly来控制是否只是在调用增加或减少引用计数器的方法时才调用record方法记录调用轨迹,默认为false。

  在介绍ResourceLeakDetector类时提到过,默认的ResourceLeakDetector类就是ResourceLeakDetector,但是用户可以使用参数io.ty.customResourceLeakDetector来决定采用默认实现ResourceLeakDetector还是使用用户自定义的ResourceLeakDetector。

  我们在第二节介绍了ty中将普通ByteBuf转为具有内存泄漏检测功能的ByteBuf是通过AbstractByteBufAllocator.toLeakAwareBuffer方法实现的。

  这里我们先看下该方法的源码:

  上面的源码中是调用AbstractByteBuf.leakDetector.track(buf)返回ResourceLeakTracker类对象的,这里我们看下默认的ResourceLeakDetector中track方法实现:

  我们看到AbstractByteBufAllocator.toLeakAwareBuffer对ResourceLeakDetector.track返回的DefaultResourceLeak和传入的ByteBuf对象进行封装,返回了具有内存泄漏检测功能的ByteBuf封装类SimpleLeakAwarepositeByteBuf或其子类AdvancedLeakAwarepositeByteBuf。如果应用程序在使用ByteBuf正确调用了retain和release方法,则在引用计数器为时,则会清除弱引用持有的实际对象,发生GC时,DefaultResourceLeak也不会被放入引用队列中(见前面第节例结果。

  但是如果应用程序在使用ByteBuf没有正确调用retain和release方法,则不会清除弱引用持有的实际对象,此时如果实际上已经没有强引用指向该ByteBuf,那么在发生GC时,垃圾收集器会回收该ByteBuf,而弱引用DefaultResourceLeak会被放入引用队列中(见前面第节例结果,加入到引用队列中的就是识别到的发生内存泄漏的ByteBuf。在ResourceLeakDetector.track方法中调用的reportLeak输出的就是引用队列中的弱引用DefaultResourceLeak:

  到这里,已经基本上介绍完ty内存检测的实现原理,下面我们再看下DefaultResourceLeak.record是如何记录调用轨迹的:

  最后我们再看下Record是如何输出调用轨迹的,前面我们说到Record继承自类Throwable,因此可使用getStackTrace方法获取实例化该对象时的调用轨迹,所以上面在输出内存泄漏报告时就调用了Record.toString方法:

  ty的Reactor多线程模型,NioEventLoop,ChannelPipeline简介

  如果在Google上搜索“ty高性能易用“,在找到的一大批文章,你大概率会看到这张图,外加关键字

  NIO,Reactor多线程模型,异步串行无锁化,堆外内存,pipeline,翻看完这些文章后可以让你对ty的原理有大致了解,但是ty如何实现这些的呢?本文将尽可能简单的解释ty中Reactor多线程的实现,如有错误感谢指出.

  Selector是NIO的重要组件,Selector上可以注册Channel.Channel在注册的时候会标注自己感兴趣的事件:

  Channel,通道,为了便于理解,我把它分为三类

  Reactor多线程模型可以分为三块

  mainReactor负责客户端接入

  aeptor负责将接入的连接移交给subReactor

  subReactor负责连接的读写

  ChannelPipeline的设计思想是责任链设计模式,是由ChannelHandlerContext组成的双向链表,,首尾固定为HeadContext和TailContext,它们作为哨兵存在.当我们添加一个ChannelHandler到ChannelPipeline时,会先包装成ChannelHandlerContext再添加进去.

  inbound事件传播

  客户端向服务端发送消息,这个流向就称为inbound.消息会从Head开始由左向右传递直到Tail,由Tail进行收尾操作

  outbound事件传播

  服务端向客户端发送信息,这个流向称为outbound,消息会从Tail开始由右向左传递知道Head,由Head进行收尾操作

  当某个ChannelHandler操作抛出异常,会从该handler开始向Tail传递.由Tail做收尾操作.

  学习ty,要理解Reactor模型,并把它和ty的实现结合起来,我学习ty的时候就因为这块认识不深刻,浪费了很多时间也没有成效,共勉

  认识Spring-Reactor框架

  我们都知道reactor模式的优缺点,也就是基于异步实现的,但是这只是模式,那么框架如何运作,如何优美,则是另一回事。最近在看spring-cloud-gateway,我被老外写的代码吸引了,一路到底就是reactor框架整合ty的那段代码。确实很优美。主要是reactor-ty那个包。确实写得不赖,写了tcp,udp客户端和服务器端,用户可以基于这个实现很多基于这个传输层的框架实现。废话不多说,先入门个rea上面这个就讲述了Moon和Flux的区别,其实就是one/more的区别.manyone=more,同时也可以分解.快速开始前,我们只需要加入maven依赖简单例子上面输出:

  ty核心技术及源码剖析-ty入站与出站机制

  ty的组件设计:ty的主要组件有Channel、EventLoop、ChannelFuture、ChannelHandler、ChannelPipe等。、ChannelHandler充当了处理入站和出站数据的应用程序逻辑的容器。例如,实现ChannelInboundHandler接口(或ChannelInboundHandlerAdapter),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。当要给客户端发送响应时,也可以从ChannelInboundHandler冲刷数据。业务逻辑通常写在一个或者多个ChannelInboundHandler中。ChannelOutboundHandler原理一样,只不过它是用来处理出站数据的。、ChannelPipeline提供了ChannelHandler链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务器端的数据会通过pipeline中的一些列ChannelOutboundHandler,并被这些Handler处理,反之则称为入站的。、当ty发送或者接受一个消息的时候,就将会发生一次数据转换。入站消息会被解码:从字节转换为另一种格式(比如Java对象);如果是出站消息,它会被编码成字节。、ty提供一些列实用的编解码器,他们都实现了ChannelInboundHandler或者ChannelOutboundHandler接口。在这些类中,channelRead方法已经被重写了。以入站为例,对于每个从入站Channel读取的消息,这个方法会被调用。随后,它将调用由解码器所提供的decode()方法进行解码,并将已经解码的字节转发给ChannelPipeline中的下一个ChannelInboundHandler。、关系继承图、由于不可能知道远程节点是否会一次性发送一个完整的信息,tcp有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,知道它准备好被处理。、一个关于ByteToMessageDecoder实例分析

  [ty源码分析]ByteBuf(一)

  ByteBuf通过两个指针协助读写操作,读操作使用readerIndex,写操作使用writerIndex.

  readerIndex、writerIndex初始值是,写入数据时writerIndex增加,读取数据时readerIndex增加,但是readerIndex不会超过writerIndex.

  读取之后,-readerIndex之间的空间视为discard的,调用discardReadByte方法可以释放这一部分空间,作用类似于ByteBuffer的pact方法.readerIndex-writerIndex之间的数据是可读的,等价于ByteBuffer中position-limit之间的数据.

  writerIndex-capacity之间的空间是可写的,等价于ByteBuffer中limit-capacity之间的空间.

  读只影响readerIndex、写只影响writerIndex,读写之间不需要调整指针位置,所以相较于NIO的ByteBuffer,可以极大的简化读写操作

  调用discardReadBytes会发生字节数组的内存复制,所以频繁调用会导致性能下降

  ByteBuf对write操作进行了封装,有ByteBuf的write操作负责进行剩余咳哟好难过空间的校验,如果可用缓冲区不足,ByteBuf会自动进行动态扩展。对于使用者而言不需要关心底层的校验和扩展细节,只需要不超过capacity即可

  对缓冲区进行读操作时,有的时候我们需要对之前的操作进行回滚,读操作并不会改变缓冲区的内容,回滚主要是重新设置索引信息

  Mark:将当前的位置指针被分到mark变量中

  Reset:恢复位置指针为mark中的变量值

  ByteBuf有readerIndex、writerIndex,所以有四个相应的方法

  markReaderIndex:将当前readerIndex备份到markedReaderIndex中

  resetReaderIndex:将当前readerIndex设置为markedReaderIndex

  markWriterIndex:将当前readerIndex备份到markedWriterIndex中

  resetWriterIndex:将当前readerIndex设置为markedWriterIndex

  slice:返回当前ByteBuf的可读子缓冲区,即从readerIndex到writerIndex的ByteBuf,返回的ByteBuf和原有缓冲区共享内容,但是维护独立的索引.当修改其中一个ByteBuf的内容时,另一个也会改变,即双方持有的是同一个对象的引用

  相比于PooledHeapByteBuf,UnpooledHeapByteBuf的实现更加简单,也不容易出现内存管理的问题,所以才性能满足的情况下,推荐使用UnpooledHeapByteBuf

  在I/O通信线程的读写缓冲区中使用DirectByteBuf,后端业务消息的编码使用HeapByteBuf,这样的组合性能最优

您可能感兴趣的文章:

相关文章