httpclient5源码分析

httpclient5源码分析

初始化client

  • 常驻线程为IOReactorWorker 默认cpu核心个数(select读写事件)
  • 以下为初始化client示例代码
IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
                .setSoTimeout(Timeout.ofMilliseconds(100))
                .setSelectInterval(TimeValue.ofMilliseconds(50))
                .build();

        PoolingAsyncClientConnectionManager build = PoolingAsyncClientConnectionManagerBuilder.create()
                .setPoolConcurrencyPolicy(PoolConcurrencyPolicy.LAX)
                .setMaxConnPerRoute(6).build();

        RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
                .setConnectTimeout(500, TimeUnit.MILLISECONDS)
                .setConnectionRequestTimeout(500, TimeUnit.MILLISECONDS)
                .setResponseTimeout(5000, TimeUnit.MILLISECONDS)
                .build();

        Main.client = HttpAsyncClients.custom()
                .setIOReactorConfig(ioReactorConfig)
                .setConnectionManager(build)
                .setDefaultRequestConfig(config)
                .disableAutomaticRetries()
                .build();
		
        client.start();
  • client.start()
 //默认启动Runtime.getRuntime().availableProcessors()个线程
 //threads[i] = (threadFactory != null ? threadFactory : THREAD_FACTORY).newThread(new IOReactorWorker(dispatcher));
 public final void start() {
        if (this.status.compareAndSet(IOReactorStatus.INACTIVE, IOReactorStatus.ACTIVE)) {
            for (int i = 0; i < this.threads.length; i++) {
            //启动IOReactorWorker类run方法	
                this.threads[i].start();
            }
        }
    }
  • IOReactorWorker类run调用SingleCoreIOReactor的doExecute()方法,至此启动n个select,循环调用select获取I/O事件
void doExecute() throws IOException {
        while (!Thread.currentThread().isInterrupted()) {
			//获取IO事件
            final int readyCount = this.selector.select(this.selectTimeoutMillis);

            if (getStatus().compareTo(IOReactorStatus.SHUTTING_DOWN) >= 0) {
                if (this.shutdownInitiated.compareAndSet(false, true)) {
                    initiateSessionShutdown();
                }
                closePendingChannels();
            }
            if (getStatus() == IOReactorStatus.SHUT_DOWN) {
                break;
            }

            // 处理 selected I/O events 
            1.client发起请求会触发该方法(可写)
            2.response返回会触发该方法(可读)
            if (readyCount > 0) {
                processEvents(this.selector.selectedKeys());
            }

            validateActiveChannels();

            // Process closed sessions
            processClosedSessions();

            // If active process new channels
            if (getStatus() == IOReactorStatus.ACTIVE) {
                processPendingChannels();
                //处理请求,下边会有介绍
                processPendingConnectionRequests();
            }

            // Exit select loop if graceful shutdown has been completed
            if (getStatus() == IOReactorStatus.SHUTTING_DOWN && this.selector.keys().isEmpty()) {
                break;
            }
            if (getStatus() == IOReactorStatus.SHUT_DOWN) {
                break;
            }
        }
    }
  • 发起请求示例代码如下:
  • 主要关注client.execute()方法
SimpleHttpRequest httpRequest = SimpleHttpRequest.create(Method.GET.name(), url);
        final SimpleHttpRequest request = SimpleRequestBuilder.copy(httpRequest)
                .addParameter("name", "124")
                .build();
        RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
                .setConnectTimeout(1500, TimeUnit.MILLISECONDS)
                .setConnectionRequestTimeout(200, TimeUnit.MILLISECONDS)
                .setResponseTimeout(milli, TimeUnit.MILLISECONDS)
                .build();
        request.setConfig(config);
		//发起请求
        final Future<SimpleHttpResponse> future = client.execute(
                SimpleRequestProducer.create(request),
                SimpleResponseConsumer.create(),
                new FutureCallback<SimpleHttpResponse>() {

                    @Override
                    public void completed(final SimpleHttpResponse response) {
                        System.out.println(request + "->" + new StatusLine(response));
                        System.out.println(response.getBody().getBodyText());
                    }

                    @Override
                    public void failed(final Exception ex) {
                        long end = System.currentTimeMillis() - start;
                        System.out.println("fail" + end);
                        System.out.println(request + "->" + ex);
                    }

                    @Override
                    public void cancelled() {
                        System.out.println(request + " cancelled");
                    }

                });
  • execute(调用InternalAbstractHttpAsyncClient类的doExecute方法
  • 重点关注executeImmediate方法
protected <T> Future<T> doExecute(
            final HttpHost httpHost,
            final AsyncRequestProducer requestProducer,
            final AsyncResponseConsumer<T> responseConsumer,
            final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
            final HttpContext context,
            final FutureCallback<T> callback) {
        final ComplexFuture<T> future = new ComplexFuture<>(callback);
        try {
            if (!isRunning()) {
                throw new CancellationException("Request execution cancelled");
            }
            final HttpClientContext clientContext = context != null ? HttpClientContext.adapt(context) : HttpClientContext.create();
            requestProducer.sendRequest((request, entityDetails, c) -> {

                RequestConfig requestConfig = null;
                if (request instanceof Configurable) {
                    requestConfig = ((Configurable) request).getConfig();
                }
                if (requestConfig != null) {
                    clientContext.setRequestConfig(requestConfig);
                }

                setupContext(clientContext);

                final HttpRoute route = determineRoute(
                        httpHost != null ? httpHost : RoutingSupport.determineHost(request),
                        clientContext);
                final String exchangeId = ExecSupport.getNextExchangeId();
                clientContext.setExchangeId(exchangeId);
                if (LOG.isDebugEnabled()) {
                    LOG.debug("{} preparing request execution", exchangeId);
                }
                final AsyncExecRuntime execRuntime = createAsyncExecRuntime(pushHandlerFactory);

                final AsyncExecChain.Scheduler scheduler = this::executeScheduled;

                final AsyncExecChain.Scope scope = new AsyncExecChain.Scope(exchangeId, route, request, future,
                        clientContext, execRuntime, scheduler, new AtomicInteger(1));
                final AtomicBoolean outputTerminated = new AtomicBoolean(false);
                executeImmediate(
                        BasicRequestBuilder.copy(request).build(),
                        entityDetails != null ? new AsyncEntityProducer() {

                            @Override
                            public void releaseResources() {
                                requestProducer.releaseResources();
                            }

                            @Override
                            public void failed(final Exception cause) {
                                requestProducer.failed(cause);
                            }

                            @Override
                            public boolean isRepeatable() {
                                return requestProducer.isRepeatable();
                            }

                            @Override
                            public long getContentLength() {
                                return entityDetails.getContentLength();
                            }

                            @Override
                            public String getContentType() {
                                return entityDetails.getContentType();
                            }

                            @Override
                            public String getContentEncoding() {
                                return entityDetails.getContentEncoding();
                            }

                            @Override
                            public boolean isChunked() {
                                return entityDetails.isChunked();
                            }

                            @Override
                            public Set<String> getTrailerNames() {
                                return entityDetails.getTrailerNames();
                            }

                            @Override
                            public int available() {
                                return requestProducer.available();
                            }

                            @Override
                            public void produce(final DataStreamChannel channel) throws IOException {
                                if (outputTerminated.get()) {
                                    channel.endStream();
                                    return;
                                }
                                requestProducer.produce(channel);
                            }

                        } : null,
                        scope,
                        new AsyncExecCallback() {

                            @Override
                            public AsyncDataConsumer handleResponse(
                                    final HttpResponse response,
                                    final EntityDetails entityDetails) throws HttpException, IOException {
                                if (response.getCode() >= HttpStatus.SC_CLIENT_ERROR) {
                                    outputTerminated.set(true);
                                    requestProducer.releaseResources();
                                }
                                responseConsumer.consumeResponse(response, entityDetails, c,
                                        new FutureCallback<T>() {

                                            @Override
                                            public void completed(final T result) {
                                                future.completed(result);
                                            }

                                            @Override
                                            public void failed(final Exception ex) {
                                                future.failed(ex);
                                            }

                                            @Override
                                            public void cancelled() {
                                                future.cancel();
                                            }

                                        });
                                return entityDetails != null ? responseConsumer : null;
                            }

                            @Override
                            public void handleInformationResponse(
                                    final HttpResponse response) throws HttpException, IOException {
                                responseConsumer.informationResponse(response, c);
                            }

                            @Override
                            public void completed() {
                                if (LOG.isDebugEnabled()) {
                                    LOG.debug("{} message exchange successfully completed", exchangeId);
                                }
                                try {
                                    execRuntime.releaseEndpoint();
                                } finally {
                                    responseConsumer.releaseResources();
                                    requestProducer.releaseResources();
                                }
                            }

                            @Override
                            public void failed(final Exception cause) {
                                if (LOG.isDebugEnabled()) {
                                    LOG.debug("{} request failed: {}", exchangeId, cause.getMessage());
                                }
                                try {
                                    execRuntime.discardEndpoint();
                                    responseConsumer.failed(cause);
                                } finally {
                                    try {
                                        future.failed(cause);
                                    } finally {
                                        responseConsumer.releaseResources();
                                        requestProducer.releaseResources();
                                    }
                                }
                            }

                        });
            }, context);
        } catch (final HttpException | IOException | IllegalStateException ex) {
            future.failed(ex);
        }
        return future;
    }
  • executeImmediate方法
  • execChain执行调用链双向链表
  • AsyncRedirectExec类–>AsyncProtocolExec类–>AsyncConnectExec类–>HttpAsyncMainClientExec类
  • AsyncRedirectExec类<–AsyncProtocolExec类<–AsyncConnectExec类<–HttpAsyncMainClientExec类
void executeImmediate(
            final HttpRequest request,
            final AsyncEntityProducer entityProducer,
            final AsyncExecChain.Scope scope,
            final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
        execChain.execute(request, entityProducer, scope, asyncExecCallback);
    }
   //AsyncExecChainElement类execute方法
  public void execute(
            final HttpRequest request,
            final AsyncEntityProducer entityProducer,
            final AsyncExecChain.Scope scope,
            final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
        handler.execute(request, entityProducer, scope, next != null ? next::execute : null, asyncExecCallback);
    }
  • AsyncConnectExec会调用SingleCoreIOReactor类的connect方法,将sessionrequest放入requestQueue队列,以上SingleCoreIOReactor类的doExecute方法也就是select会调用
    processPendingConnectionRequests() 消费requestQueue队列处理请求。
  public Future<IOSession> connect(
            final NamedEndpoint remoteEndpoint,
            final SocketAddress remoteAddress,
            final SocketAddress localAddress,
            final Timeout timeout,
            final Object attachment,
            final FutureCallback<IOSession> callback) throws IOReactorShutdownException {
        Args.notNull(remoteEndpoint, "Remote endpoint");
        final IOSessionRequest sessionRequest = new IOSessionRequest(
                remoteEndpoint,
                remoteAddress != null ? remoteAddress : new InetSocketAddress(remoteEndpoint.getHostName(), remoteEndpoint.getPort()),
                localAddress,
                timeout,
                attachment,
                callback);
		//将sessionRequest插入到requestQueue队列
        this.requestQueue.add(sessionRequest);
        this.selector.wakeup();

        return sessionRequest;
    }
  • 消费requestQueue队列
private void processPendingConnectionRequests() {
        IOSessionRequest sessionRequest;
        //从requestQueue中获取待处理的请求
        for (int i = 0; i < MAX_CHANNEL_REQUESTS && (sessionRequest = this.requestQueue.poll()) != null; i++) {
            if (!sessionRequest.isCancelled()) {
                final SocketChannel socketChannel;
                try {
                	//创建channel也就是fd
                    socketChannel = SocketChannel.open();
                } catch (final IOException ex) {
                    sessionRequest.failed(ex);
                    return;
                }
                try {
                    //看以下会介绍
                    processConnectionRequest(socketChannel, sessionRequest);
                } catch (final IOException | SecurityException ex) {
                    Closer.closeQuietly(socketChannel);
                    sessionRequest.failed(ex);
                }
            }
        }
    }
  • processConnectionRequest
  • 主要是将channel注册到selector中,便于以上select循环监听读写事件,处理读写事件
private void processConnectionRequest(final SocketChannel socketChannel, final IOSessionRequest sessionRequest) throws IOException {
        validateAddress(sessionRequest.localAddress);
        validateAddress(sessionRequest.remoteAddress);

        socketChannel.configureBlocking(false);
        prepareSocket(socketChannel.socket());

        if (sessionRequest.localAddress != null) {
            final Socket sock = socketChannel.socket();
            sock.setReuseAddress(this.reactorConfig.isSoReuseAddress());
            sock.bind(sessionRequest.localAddress);
        }

        final SocketAddress targetAddress;
        final IOEventHandlerFactory eventHandlerFactory;
        if (this.reactorConfig.getSocksProxyAddress() != null) {
            targetAddress = this.reactorConfig.getSocksProxyAddress();
            eventHandlerFactory = new SocksProxyProtocolHandlerFactory(
                    sessionRequest.remoteAddress,
                    this.reactorConfig.getSocksProxyUsername(),
                    this.reactorConfig.getSocksProxyPassword(),
                    this.eventHandlerFactory);
        } else {
            targetAddress = sessionRequest.remoteAddress;
            eventHandlerFactory = this.eventHandlerFactory;
        }

        // Run this under a doPrivileged to support lib users that run under a SecurityManager this allows granting connect permissions
        // only to this library
        final boolean connected;
        try {
            connected = AccessController.doPrivileged(
                    (PrivilegedExceptionAction<Boolean>) () -> socketChannel.connect(targetAddress));
        } catch (final PrivilegedActionException e) {
            Asserts.check(e.getCause() instanceof  IOException,
                    "method contract violation only checked exceptions are wrapped: " + e.getCause());
            // only checked exceptions are wrapped - error and RTExceptions are rethrown by doPrivileged
            throw (IOException) e.getCause();
        }

		//将socketChannel注册到selector中
        final SelectionKey key = socketChannel.register(this.selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
        final InternalChannel channel = new InternalConnectChannel(key, socketChannel, sessionRequest, (k, sc, namedEndpoint, attachment) -> {
            final IOSession ioSession = new IOSessionImpl("c", k, sc);
            final InternalDataChannel dataChannel = new InternalDataChannel(
                    ioSession,
                    namedEndpoint,
                    ioSessionDecorator,
                    sessionListener,
                    closedSessions);
            dataChannel.upgrade(eventHandlerFactory.createHandler(dataChannel, attachment));
            dataChannel.setSocketTimeout(reactorConfig.getSoTimeout());
            return dataChannel;
        });
        if (connected) {
            channel.handleIOEvent(SelectionKey.OP_CONNECT);
        } else {
            key.attach(channel);
            sessionRequest.assign(channel);
        }
    }

  • SingleCoreIOReactor doExecute方法也就是processEvents方法
    • processEvents(this.selector.selectedKeys(),client start时启动处理事件最终会调用HttpAsyncMainClientExec类的produceRequest->commitRequest
  • commitRequest 发起请求,将数据写入channel
    • exchangeHandler.produceRequest((request, entityDetails, httpContext) -> commitRequest(request, entityDetails), context)
public void produceRequest(
                    final RequestChannel channel,
                    final HttpContext context) throws HttpException, IOException {
				
                clientContext.setAttribute(HttpClientContext.HTTP_ROUTE, route);
                clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
                httpProcessor.process(request, entityProducer, clientContext);
				//发起请求
                channel.sendRequest(request, entityProducer, context);
                if (entityProducer == null) {
                    messageCountDown.decrementAndGet();
                }
            }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值