美文网首页
Apache HttpAsyncClient 源码分析

Apache HttpAsyncClient 源码分析

作者: ntjsz | 来源:发表于2017-09-21 16:58 被阅读0次

    Apache HttpAsyncClient 4.1.2

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpasyncclient</artifactId>
        <version>4.1.2</version>
    </dependency>
    

    Class 继承图

    InternalHttpAsyncClient

    api使用者使用的 HttpClient

    InternalHttpAsyncClient
    HttpAsyncRequestExecutor
    HttpAsyncRequestExecutor
    InternalIODispatch
    InternalIODispatch
    PoolingNHttpClientConnectionManager
    PoolingNHttpClientConnectionManager
    DefaultConnectingIOReactor

    建立连接用的boss reactor,一个client只有一个


    DefaultConnectingIOReactor
    BaseIOReactor

    处理读写的worker reactor,一个client可以有多个


    BaseIOReactor
    CPool

    TCP连接池,不是线程池


    CPool
    ManagedNHttpClientConnectionImpl

    一条TCP连接


    ManagedNHttpClientConnectionImpl
    IOSessionImpl

    一对 HTTP Request/Response 所使用的会话上下文
    attributes 中持有 ManagedNHttpClientConnectionImpl 引用等

    IOSessionImpl

    Class 依赖关系

    Class Diagram

    常驻线程

    Reactor Thread 负责 connect
    Worker Thread 负责 read write

    时序图

    Main Thread Sequence Diagram Reactor Thread Sequence Diagram Worker Thread Sequence Diagram

    一些默认参数

    PoolingNHttpClientConnectionManager
    • defaultMaxPerRoute = 2
      每一个 local IP => remoteIP : port 为一个route,在向http服务器单一(ip,port)对发送请求时,这个参数控制了可以建立的tcp连接上限
    • maxTotal 20
    IOReactorConfig
    • selectInterval = 1000
      selector interval (ms)
    • soTimeout = 0
      socket上返回response的timeout上限
    • soKeepAlive = false
      ??虽然默认为false,但实际效果好像是true
    • soReuseAddress = false
      ??虽然默认为false,但实际效果好像是true

    Demo测试

    前提
    • windows 10环境下
    • IoThreadCount设为3 (实际环境可默认为CPU核心数量)
    • MaxConnPerRoute 设为4
    • 发送7个请求
    实际情况
    1. 在发送7个请求,服务器均未回复时。
      共建立4个tcp连接,散列到3线程的3个selector上监听,如图1。
      断点于(execute:340, AbstractMultiworkerIOReactor)
      CPoolleasingRequests 为3,leased 为4,如图2。
      断点如上,调用栈为(execute:192, PoolingNHttpClientConnectionManager)
      图1
      图2
    2. 返回一个回复后,端口号未变,SocketChannelImpl改变
      CPoolleasingRequests 为2,leased 为4
    3. 在server只回复0-1两个请求时,client端同步等待2-5号的response,6号的请求不会发出
    部分源码执行过程
    1. HttpGet 写入了IOSessionImpl 的outputBuffer中,具体位置层次如图
      ByteBuffer 的 pos=0 lim=140 代表有140个字节在buffer中未发出
      其中 OP_WRITE 已注册到 interestOps 中,等待其ready后,后续代码会执行channel.write(this.buffer)。至此,请求已发出
      至于 SelectionKey 是如何ready的,就要去分析nio的源码了

      buffer content
      • TCP在连接建立完成后,控制权通过DefaultConnectingIOReactor.addChannel()从BossReactor转入BaseIOReactorBaseIOReactorBaseIOReactor.processNewChannels()中注册OP_READ
      • BaseIOReactor.processNewChannels()sessionRequest.completed(session)通过层层回调,AbstractClientExchangeHandler.requestConnection()方法中定义的匿名类中的completed()
        new FutureCallback<NHttpClientConnection>() {
        
                       @Override
                       public void completed(final NHttpClientConnection managedConn) {
                           connectionAllocated(managedConn);
                       }
        
                       @Override
                       public void failed(final Exception ex) {
                           connectionRequestFailed(ex);
                       }
        
                       @Override
                       public void cancelled() {
                           connectionRequestCancelled();
                       }
        
                   });
        
        CPoolProxy.requestOutput();=>NHttpConnectionBase.requestOutput()
        最终通过this.session.setEvent(EventMask.WRITE);注册OP_WRITE
        BaseIOReactor.processNewChannels()函数同时完成了 OP_READOP_WRITE 的注册
      • Http请求发送完毕,即!this.outbuf.hasData(),会将OP_WRITE去注册this.session.clearEvent(EventMask.WRITE);
        虽然OP_WRITE已经ready,但由于不在interestOps中,不会被select()出来
        readyCount = this.selector.select(this.selectTimeout)
        readyCount = 0
    结论
    • 同一route上的http请求数量受限于 maxPerRoute, 与本地打开的、向同一对端(ip:port)的端口号数量相同。每一请求使用 IOSessionImpl 保存对话上下文,并附到 SelectionKey 上。
    • Async HttpClient无法做到全异步,无法完全复用socket,由于HTTP/1.1的原生限制,没有特征值用来识别HTTP报文,因此必须同步等待Response。
    • 可以通过HTTP/2.0
      或者自行编写HttpClient,将特征值注入HTTP头或Body中来修复此缺陷。

    Demo 代码地址: https://github.com/ntjsz/http-client-demo/

    相关文章

      网友评论

          本文标题:Apache HttpAsyncClient 源码分析

          本文链接:https://www.haomeiwen.com/subject/nnlgsxtx.html