深入Jetty源码之Buffer

概述

在Jetty中Buffer是对Java中Stream IO中的buffer和NIO中的buffer的抽象表示,它主要用于缓存连接中读取和写入的数据。在Jetty中,对每个连接使用Buffer从其InputStream中读取字节数据,或将处理后的响应字节写入OutputStream中,从而Jetty其他模块在处理请求和响应数据时直接和Buffer打交道,而不需要关注底层IO流。 

Buffer接口定义

Jetty中Buffer接口定义如下:

public interface Buffer extends Cloneable {
    // 基于Buffer主要用于向当前连接读写数据,因而它定义了两个核心的方法:readFrom和writeTo。其中readFrom方法从InputStream中读取字节数据,writeTo方法将响应字节写入OutputStream中,即向Connection中读取和写入数据,写完后清理当前Buffer。
    int readFrom(InputStream in, int max) throws IOException;
    void writeTo(OutputStream out) throws IOException;

    // 在Buffer从InputStream(Connection)中读取数据后,Buffer接口还提供了很多不同的方法用于从Buffer中读取或写入字节数据。

    // Jetty中的Buffer是一个FIFO的字节队列,它的设计类似NIO中Buffer的设计:每次get操作都从getIndex开始,并且getIndex会向前移动读取的字节数的长度;每次的peek操作也getIndex开始,但是peek操作不会使getIndex向前移动;每次put操作都从putIndex开始,并且putIndex会向前移动写入的字节数的长度;每次poke操作也会从putIndex开始,但是poke操作不会使putIndex向前移动;mark操作会以getIndex作为基准设置markIndex的值,从而在reset时会将getIndex重置到之前mark过的位置;capacity表示该Buffer最多可存储的字节数,而length表示从getIndex到putIndex还存在的字节数。并且Buffer永远保证以下关系总是成立:markIndex<=getIndex<=putIndex<=capacity

    byte get();
    int get(byte[] b, int offset, int length);
    Buffer get(int length);
    int getIndex();

    void mark();
    void mark(int offset);
    int markIndex();

    byte peek();
    byte peek(int index);
    Buffer peek(int index, int length);
    int peek(int index, byte[] b, int offset, int length);

    int poke(int index, Buffer src);
    void poke(int index, byte b);
    int poke(int index, byte b[], int offset, int length);

    int put(Buffer src);
    void put(byte b);
    int put(byte[] b,int offset, int length);
    int put(byte[] b);
    int putIndex();

    int length();
    void clear();
    void reset();
    void setGetIndex(int newStart);
    void setMarkIndex(int newMark);
    void setPutIndex(int newLimit);

    // 一个Buffer还有独立的两种状态:access级别和volatile。
access级别有:IMMUTABLE,表示当前Buffer所有的Index和内容都不能被改变;READONLY,表示当前Buffer是只读的,即getIndex和markIndex可以被改变,而putIndex以及Buffer内容不可以;READWRITE,表示所有的Index以及Buffer的内容可以被改变。
volatile状态表示当前Buffer是否会通过其他路径被修改,默认情况下,ByteArrayBuffer、DirectNIOBuffer等是NON_VOLATILE状态,而View是VOLATILE状态(除非View内部的Buffer是IMMUTABLE)。VOLATILE的状态感觉不是一个比较严谨的概念,比如对一个View它是VOLATILE的,但是在这种情况下,它内部包装的Buffer应该也变成VOLATILE状态,并且在所有的View被回收后,其内部包装的Buffer应该重新变成NON_VOLATILE状态。要实现这种严谨逻辑应该是可以做的到的,只是会比较麻烦,而且貌似也没必要,因而Jetty并没有尝试着去这样做。

    // 返回包含当前Buffer从getIndex到putIndex内容的Buffer,并且返回的Buffer不可以被其他路径修改。如果当前Buffer是NON_VOLATILE,则直接返回当前Buffer(这个实现是不严谨的,因为在这种情况下,其实有两个Buffer实例可以修改同一份数据),否则,克隆一个新的Buffer,这样对新的Buffer的修改不会影响原Buffer的内容。
    Buffer asNonVolatileBuffer();
    // 返回一个只读的Buffer(View),该只读的Buffer的读取不会影响原来Buffer的Index。
    Buffer asReadOnlyBuffer();
    // 拷贝一个不可修改的ByteBuffer。
    Buffer asImmutableBuffer();
    // 返回一个可修改的Buffer,并且对返回的Buffer的内容修改会影响原有的Buffer。
    Buffer asMutableBuffer();

    // 当前Buffer是否不可被修改,即Buffer内容和所有Index都不能被修改。
    boolean isImmutable();
    // 当前Buffer是否是只读的。
    boolean isReadOnly();
    // 是否当前Buffer内容可以通过其他路径被修改,比如View一般情况下是VOLATILE状态(除非View内部的Buffer是IMMUTABLE)。
   boolean isVolatile();
    
    // 除了以上的操作,Buffer还提供了一些其他用于操作Buffer内部字节的方法:

    //如果内部使用字节数组表示,返回该字节数组,否则,返回null。
    byte[] array();
    // 获取从getIndex到putIndex的字节数组,其长度为length。
    byte[] asArray();
    // 如果当前Buffer是对另一个Buffer的包装,则返回内部被包装的Buffer实例,否则返回当前Buffer本身。
    Buffer buffer();

    int capacity();
    // 返回当前Buffer剩余的空间,即capacity-putIndex。
    int space();
    // 清除Buffer内容,即设置getIndex和putIndex为0,以及markIndex为-1。
    // 整理Buffer内容,即将markIndex >= 0 ? min(getIndex, markIndex) : getIndex到putIndex的内容移动到Buffer的起始位置,同时修改相应的getIndex、markIndex、putIndex。
    void compact();
    // 当前Buffer是否有可用字节,即是否putIndex>getIndex。
    boolean hasContent();

    // 跳过n个字节,即getIndex增加min(remaining(), n)的值。
    int skip(int n);

    // 切割出当前Buffer从getIndex到putIndex的View,一般来说它是volatile的(除非它是immutable类型的Buffer)。
    Buffer slice();
    // 切割出当前Buffer从markIndex到putIndex的View,一般来说它是volatile的(除非它是immutable类型的Buffer)。
    Buffer sliceFromMark();
    Buffer sliceFromMark(int length);

    // 返回包含当前Buffer状态和内容的字符串。
    String toDetailString();
    boolean equalsIgnoreCase(Buffer buffer);
 }

类图


主要实现类

AbstractBuffer:
所有Buffer的基类,是对Buffer接口的基本实现。
ByteBuffer:
它继承自AbstractBuffer主要的非NIO的Buffer实现,内部使用字节数组做缓存,直接读InputStream和写OutputStream。
DirectNIOBuffer:
它实现了NIOBuffer接口,继承自AbstractBuffer,内部使用Direct的ByteBuffer做缓存,使用ReadableByteChannel和WritableByteChannel分别对InputStream(readFrom传入)和OutputStream(writeTo传入)包装,并在这两个方法中使用包装后的Channel读写数据。
IndirectNIOBuffer:
它继承自ByteBuffer,内部使用非direct的ByteBuffer做缓存,并且它也直接对InputStream和OutputStream读写。
RandomAccessFileBuffer:
它继承自AbstractBuffer,内部使用RandomAccessFile做缓存。
View:
它继承自AbstractBuffer,内部使用另一个Buffer作为缓存,并且对非IMMUTABLE的Buffer,很多时候,它是VOLATILE。View如其名,它是对内部Buffer的视图,对View内容以及Index的修改会影响内部Buffer中相应的值。

Buffers

Buffers是Buffer的抽象工厂,它用于创建Header的Buffer和Body的Buffer,并且可以根据给定的size获得相应的Buffer实例,在Buffer使用完成后,还可以通过returnBuffer方法将它归还个Buffers以复用。在创建Buffers子类时,可以将指定Header和Body各自Buffer的类型,从而在内部创建相应Buffer时会创建相应类型的Buffer,支持的Buffer类型有:BYTE_ARRAY、DIRECT、INDIRECT。

Jetty中有两个Buffers的实现:PooledBuffers和ThreadLocalBuffers。

PooledBuffers使用ConcurrentLinkedQueue构建各自的Header、Body、Other类型的Buffer池,它有一个maxSize值用于控制该Buffers中包含的所有类型的Buffer的总数。 ThreadLocalBuffers将Header、Body、Other类型的Buffer保存在ThreadLocal中。

Jetty还提供了BuffersFactory用于创建不同类型的Buffers:通过在参数中maxSize是否大于等于0以决定是使用PooledBuffers还是ThreadLocalBuffers。 

BufferCache/BufferDateCache

Jetty还为Buffer提供了两个特殊的类:BufferCache和BufferDateCache。

BufferCache
用于存储一个可以使用存储的String值、索引值等获取相应的Buffer实例,主要用于HttpHeaders、HttpMethods等一些预定义的值。
BufferDateCache
继承自DateCache,它存储了上一次使用一个long类型的date值格式化出的Buffer实例,从而实现部分复用(复用在同一秒得到的Request请求时创建的Buffer,因为时间也只能在这种情况下被复用,因而才会有这样的实现),在Request类中使用。

时间: 2024-09-15 15:03:27

深入Jetty源码之Buffer的相关文章

深入Jetty源码之Connector

Connector概述 Connector是Jetty中可以直接接受客户端连接的抽象,一个Connector监听Jetty服务器的一个端口,所有客户端的连接请求首先通过该端口,而后由操作系统分配一个新的端口(Socket)与客户端进行数据通信(先握手,然后建立连接,但是使用不同的端口).不同的Connector实现可以使用不同的底层结构,如Socket Connector.NIO Connector等,也可以使用不同的协议,如Ssl Connector.AJP Connector,从而在不同的C

深入Jetty源码之Handler总述

Handler概述 Handler是Jetty中的核心接口,它用于处理所有连接以外的逻辑,比如对Servlet框架的实现,以及用户自定义的Handler等,它继承自LifeCycle和Destroyable接口,只有一个主要方法:handle,包含Request和Response实例.在深入Jetty源码之Connection中有写道在HttpConnection的handleRequest()方法中会最终调用Server的handle()或handleAsync()方法,并且传入HttpCon

深入Jetty源码之ContextHandler

概述 ContextHandler继承自ScopedHandler,它是Jetty中实现对一个Web Application的各种资源进行管理,并串联实现整个Servlet框架的类,比如它部分实现了ServletContext接口,并且在其doScope方法中为当前Request的执行提供了相应的环境,如设置servletPath.pathInfo.设置ServletContext到ThreadLocal中.在Jetty中,Servlet的执行流程和框架由ServletHandler实现,Sec

深入Jetty源码之Servlet框架及实现(ServletRequest、ServletResponse)

概述 Servlet是Server Applet的缩写,即在服务器端运行的小程序,而Servlet框架则是对HTTP服务器(Servlet Container)和用户小程序中间层的标准化和抽象.这一层抽象隔离了HTTP服务器的实现细节,而Servlet规范定义了各个类的行为,从而保证了这些"服务器端运行的小程序"对服务器实现的无关性(即提升了其可移植性).在Servlet规范有以下几个核心类(接口):ServletContext:定义了一些可以和Servlet Container交互的

深入Jetty源码之Connection

概述 当Jetty中的Connector收到一个客户端的连接时(ServerSocket或ServerSocketChannel的accept()方法返回),Connector会首先创建一个ConnectedEndPoint用于和连接的底层(Socket.Channel)打交道(读写数据),在创建的ConnectedEndPoint时会同时使用该EndPoint创建相应类型的Connection,然后会创建一个Task仍给线程池,最终线程池会启动一个线程启动这个Task,而在这个Task中调用C

深入Jetty源码之HttpGenerator

基于抽象和聚合原则,Jetty中需要一个单独的类来专门处理HTTP响应消息和请求消息的生成和发送,Jetty的作者将该抽象(接口)命名为Generator,它有两个实现类:HttpGenerator和NestedGenerator.其类图如下: Generator接口 HTTP请求消息分为请求行.消息报头.请求正文三部分,HTTP响应消息分为状态行.消息报头.消息正文.在HTTP请求消息和响应消息格式的唯一区别是请求行和状态行,因而只需要将这一行的内容区分开来,其他的可以共享逻辑. 请求行和响应

深入Jetty源码之Servlet框架及实现(AsyncContext、RequestDispatcher、HttpSession)

概述 Servlet是Server Applet的缩写,即在服务器端运行的小程序,而Servlet框架则是对HTTP服务器(Servlet Container)和用户小程序中间层的标准化和抽象.这一层抽象隔离了HTTP服务器的实现细节,而Servlet规范定义了各个类的行为,从而保证了这些"服务器端运行的小程序"对服务器实现的无关性(即提升了其可移植性).在Servlet规范有以下几个核心类(接口):ServletContext:定义了一些可以和Servlet Container交互的

深入Jetty源码之HttpParser

概述 Jetty作为HTTP服务器,服务器和客户端以HTTP协议格式通信,Jetty使用Parser(HttpParser)来抽象HTTP请求消息和响应消息的解析类引擎.在HttpParser实现中,它采用有限状态机算法:定义了21中状态,每解析一个字符,就根据当前的状态做相应的处理,并决定是否要迁移到下一个状态,直到HTTP请求消息或响应消息解析完成.HttpParser采用事件驱动机制,它定义了EventHandler类,用户可以通过注册的EventHandler实例获取相应的消息:请求行解

深入Jetty源码之EndPoint

概述 在Jetty中,使用Connector来抽象Jetty服务器对某个端口的监听.在Connector启动时,它会启动acceptors个Acceptor线程用于监听在Connector中配置的端口.对于客户端的每次连接,Connector都会创建相应的EndPoint来表示该连接,一般在创建EndPoint的同时会同时创建Connection,这里EndPoint用于和Socket打交道,而Connection用于在从Socket中读取到数据后的处理逻辑以及生成响应数据的处理逻辑. 不同的C