天天看点

深入Jetty源码之Connector

深入Jetty源码之Connector

首先connector实现了lifecycle接口,在启动jetty服务器时,会调用其的start方法,用于初始化connector内部状态,并打开connector以接受客户端的请求(调用open方法);而在停止jetty服务器时会调用其stop方法,以关闭connector以及内部元件(如connection等)以及做一些清理工作。因而open、close方法是connector中用于处理生命周期的方法;对每个connector都有name字段用于标记该connector,默认值为:hostname:port;connector中还有server的引用,可以从中获取threadpool,并作为handler的容器被使用(在创建httpconnection时,server实例作为构造函数参数传入,并在handlerequest()方法中将解析出来的request、response传递给server注册的handler);connector还定义了一些用于配置当前connector的方法,如buffer size、max idle time、low resource max idle time,以及一些统计信息,如当前connector总共处理过的请求数、总共处理过的连接数、当前打开的连接数等信息。

connector的接口定义如下:

public interface connector extends lifecycle { 

    // connector名字,默认值hostname:port

    string getname();

    // 打开当前connector

    void open() throws ioexception;

    // 关闭当前connector

    void close() throws ioexception;

    // 对server的引用

    void setserver(server server);

    server getserver();

    // 在处理请求消息头时使用的buffer大小

    int getrequestheadersize();

    void setrequestheadersize(int size);

    // 在处理响应消息头时使用的buffer大小

    int getresponseheadersize();

    void setresponseheadersize(int size);

    // 在处理请求消息内容时使用的buffer大小

    int getrequestbuffersize();

    void setrequestbuffersize(int requestbuffersize);

    // 在处理响应消息内容使用的buffer大小

    int getresponsebuffersize();

    void setresponsebuffersize(int responsebuffersize);

    // 在处理请求消息时使用的buffer工厂

    buffers getrequestbuffers();

    // 在处理响应消息时使用的buffer工厂

    buffers getresponsebuffers();

    // user data constraint的配置可以是none、integral、confidential,对这三种值的解释:

    // none:a value of none means that the application does not require any transport guarantees.

    // integral:a value of integral means that the application requires the data sent between the client and server to be sent in such a way that it can't be changed in transit.

    // confidential:a value of confidential means that the application requires the data to be transmitted in a fashion that prevents other entities from observing the contents of the transmission on.

    // 如果配置了user-data-constraint为integral或confidential表示所有相应请求都会重定向到使用integral/confidential schema/port构建的新的url中。

    int getintegralport();

    string getintegralscheme();

    boolean isintegral(request request);

    int getconfidentialport();

    string getconfidentialscheme();

    boolean isconfidential(request request);

    // 在将httpconnection交给server中的handlers处理前,根据当前connector,自定义一些endpoint和request的配置,如设置endpoint的maxidletime,request的timestamp,

    // 清除selectchannelendpoint中的idletimestamp,检查forward头等。

    void customize(endpoint endpoint, request request) throws ioexception;

    // 主要用于selectchannelconnector中,重置selectchannelendpoint中的idletimestamp,即重新计时idle的时间。

   // 它在每个request处理结束,endpoint还未关闭,并且当前连接属于keep-alive类型的时候被调用。

    void persist(endpoint endpoint) throws ioexception;

    // 底层的链接实例,如serversocket、serversocketchannel等。

    object getconnection();

    //是否对"x-forwarded-for"头进行dns名字解析

    boolean getresolvenames();

    // 当前connector绑定的主机名、端口号等。貌似在connector的实现中没有一个默认的主机名。

    // 由于端口号可以设置为0,表示由操作系统随机的分配一个还没有被使用的端口,因而这里由localport用于存储connector实际上绑定的端口号;

    // 其中-1表示这个connector还未开启,-2表示connector已经关闭。

    string gethost();

    void sethost(string hostname);

    void setport(int port);

    int getport();

    int getlocalport();

    // socket的最大空闲时间,以及在资源比较少(如线程池中的任务数比最大可用线程数要多)的情况下的最大空闲时间,当空闲时间超过这个时间后关闭当前连接(socket)。

    int getmaxidletime();

    void setmaxidletime(int ms);

    int getlowresourcemaxidletime();

    void setlowresourcemaxidletime(int ms);

    // 是否当前connector处于lowresources状态,即线程池中的任务数比最大可用线程数要多

    public boolean islowresources();

    /* ------------------------以下是一些获取和当前connector相关的统计信息------------------------------------ */

    // 打开或关闭统计功能

    public void setstatson(boolean on);

    // 统计功能的开闭状态

    public boolean getstatson();

    // 重置统计数据,以及统计开始时间

    public void statsreset();

    // 统计信息的开启时间戳

    public long getstatsonms();

    // 当前connector处理的请求数

    public int getrequests();

    // 当前connector接收到过的连接数

    public int getconnections() ;

    // 当前connector所有当前还处于打开状态的连接数

    public int getconnectionsopen() ;

    // 当前connector历史上同时处于打开状态的最大连接数

    public int getconnectionsopenmax() ;

    // 当前connector所有连接的持续时间总和

    public long getconnectionsdurationtotal();

    // 当前connector的最长连接持续时间

    public long getconnectionsdurationmax();

    // 当前connector平均连接持续时间

    public double getconnectionsdurationmean() ;

    // 当前connector所有连接持续时间的标准偏差

    public double getconnectionsdurationstddev() ;

    // 当前connector的所有连接的平均请求数

    public double getconnectionsrequestsmean() ;

    // 当前connector的所有连接的请求数标准偏差

    public double getconnectionsrequestsstddev() ;

    // 当前connector的所有连接的最大请求数

    public int getconnectionsrequestsmax();

}

jetty中所有的connector都继承自abstractconnector,而它自身继承自httpbuffers,httpbuffers包含了request、response的buffer工厂的创建以及相应size的配置。abstractconnector还包含了name、server、maxidletime以及一些统计信息的引用,用于实现connector接口中的方法,只是一些get、set方法,不详述。除了connector接口相关的配置,abstractconnector还为定义了两个字段:_acceptqueuesize用于表示serversocket 或serversocketchannel中最大可等待的请求数,_acceptors用于表示用于调用serversocket或serversocketchannel中accept方法的线程数,建议这个数字小于或等于可用处理器的2倍。

在abstractconnector中还定义了acceptor内部类,它实现了runnable接口,在其run方法实现中,它将自己的thread实例赋值给abstractconnector中的_acceptorthread数组中acceptor号对应的bucket,并更新线程名为:<name> acceptor<index> <connector.tostring>。然后根据配置的_acceptorpriorityoffset设置当前线程的priority。只要当前connector处于running状态,并且底层链接实例不为null,不断的调用accept方法()。在退出的finally语句快中清理_acceptorthread数组中相应的bucket值为null,并将线程原来的name、priority设置回来。

abstractconnector实现了dostart()方法,它首先保证server实例的存在;然后打开当前connector(调用其open()方法),并调用父类的dostart方法(这里是httpbuffers,用于初始化对应的buffers);如果没有自定义的threadpool,则从server中获取threadpool;最后根据acceptors的值创建_acceptorthread数组,将acceptors个acceptor实例dispatch给threadpool。在dostop方法实现中,它首先调用close方法,然后对非server中的threadpool调用其stop方法,再调用父类的dostop方法清理buffers的引用,最后遍历_acceptorthread数据,调用每个thread的interrupt方法。

在各个子类的accept方法实现中,他们在获取客户端过来的socket连接后,都会对该socket做一些配置,即调用abstractconnector的configure方法,它首先设置socket的tcp_nodelay为true,即禁用nagle算法(关于禁用的理由可以参考:http://jerrypeng.me/2013/08/mythical-40ms-delay-and-tcp-nodelay/#sec-4-2,简单的,如果该值为false,则tcp的数据包要么达到tcp segment size,要么收到一个ack,才会发送出去,即有delay);然后如果设置了_solingertime,则开启socket中so_linger选项,否则,关闭该选项(so_linger选项用于控制关闭一个socket的行为,如果开启了该选项,则在关闭socket时会等待_solingertime时间,此时如果有数据还未发送完,则会发送这些数据;如果关闭了该选项,则socket的关闭会立即返回,此时也有可能继续发送未发送完成的数据,具体参考:http://blog.csdn.net/factor2000/article/details/3929816)。

在serversocket和serversocketchannel中还有一个so_reuseaddr的配置,一般来说当一个端口被释放后会等待两分钟再被使用,此时如果重启服务器,可能会导致启动时的绑定错误,设置该值可以让端口释放后可以立即被使用(具体参考:http://www.cnblogs.com/mydomain/archive/2011/08/23/2150567.html)。在abstractconnector中可以使用setreuseaddress方法来配置,默认该值设置为true。

abstractconnector中还实现了customize方法,它在forwarded设置为true的情况下设置相应的attribute:javax.servlet.request.cipher_suite, javax.servlet.request.ssl_session_id,以及request中对应host、server等头信息。这个逻辑具体含义目前还不是很了解。。。。

最后关于统计数据的更新方法,abstractconnector定义了如下方法:

protected void connectionopened(connection connection)

protected void connectionupgraded(connection oldconnection, connection newconnection)

protected void connectionclosed(connection connection)

有了abstractconnector的实现,socketconnector的实现就变的非常简单了,它保存了一个endpoint的set,表示所有在这个connector下正在使用的endpoint,然后是serversocket,在open方法中创建,并在getconnection()方法中返回,还有一个localport字段,当serversocket被创建时从serversocket实例中获取,并在getlocalport()方法中返回。在close方法中关闭serversocket,并设置localport为-2;在accept方法中,调用serversocket的accept方法,返回一个socket,调用configure方法对新创建的socket做一些基本的配置,然后使用该socket创建connectorendpoint,并调用其dispatch方法;在customize方法中,在调用abstractconnector的customize方法的同时还设置connectorendpoint的maxidletime,即设置socket的so_timeout选项,用于配置该socket的空闲可等待时间;在dostart中会先清理connectorendpoint的集合,而在dostop中会关闭所有还处于打开状态的connectorendpoint。

selectchannelconnector内部使用serversocketchannel,在open方法中创建serversocketchannel,配置其为非blocking模式,并设置localport值;在accept方法中调用serversocket的accept方法获得一个socketchannel,配置该channel为非blocking模式,调用abstractchannel的configure方法做相应socket配置,最后将该socketchannel注册给connectorselectmanager;在dostart方法中,它会初始化connectorselectmanager的selectsets值为acceptors值、maxidletime、lowresourceconnections、lowresourcesmaxidletime等值,并启动该manager,并dispatch acceptors个线程,不断的调用manager的doselect方法;在close方法中会先stop connectorselectmanager,然后关闭serversocketchannel,设置localport为-2;在customize方法中会清除selectchannelendpoint的idletimestamp,重置其maxidletime以及重置request中的timestamp的值;在persist方法中会重置selectchannelendpoint中idletimestamp的值。

blockingchannelconnector实现类似socketconnector,不同的是它使用serversocketchannel,并且其endpoint为blockingchannelendpoint。所不同的是它需要在dostart方法中启动一个线程不断的检查所有还在connections集合中的blockingchannelendpoint是否已经超时,每400ms检查一次,如果超时则关闭该endpoint。

其他的connector的实现都比较类似,而ssl相关的connector需要也只是加入了ssl相关的逻辑,这里不再赘述。

继续阅读