美文网首页
httpclient 责任链执行分析

httpclient 责任链执行分析

作者: futureluck | 来源:发表于2018-11-19 15:23 被阅读0次

    最近在看Apache httpclient 源码,在发送请求的执行过程中,采用的是责任链模式。责任链设计模式在开源框架中很常见,比如netty 的pipeLine设计,servlet的filter。

    责任链

    以下引用网络释义,我觉得比较好理解的

    职责链模式(Chain of Responsibility):使多个对象都有机会处理同一个请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

    用责任链有什么好处呐?就是为了让代码更好的拥抱变化
    开闭原则:对扩展开放、对修改关闭。 当功能需要变化的时候,我们应该是通过扩展的方式来实现,而不是通过修改已有的代码来实现。

    执行步骤

    // 创建client对象,类似于我们打开一个浏览器
    CloseableHttpClient closeableHttpClient = HttpClients.custom().build();
    
    // 创建post方式请求对象,这个类似于我们打开的浏览器,再开个页面
    // http请求包括:请求行(request line)、请求头部(header)、空行和请求数据(body的数据)
    // 这个就是构造一个http请数据
    HttpPost httpPost = new HttpPost(url);
    
    // 解析http请求的body数据
    // http响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文
    CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
    HttpEntity httpResponseEntity = response.getEntity();
    String responseBody = EntityUtils.toString(httpResponseEntity, HttpConstants.UTF_8);
    
    // 分析CloseableHttpClient.execute();方法,最后执行指向的是
    InternalHttpClient.doExecute( final HttpHost target,final HttpRequest request,final HttpContext context);
    
    // InternalHttpClient的成员变量ClientExecChain execChain执行execute方法
    CloseableHttpResponse execute(
                HttpRoute route, //路由对象
                HttpRequestWrapper request, //请求包裹对象(request请求+目的地址)
                HttpClientContext clientContext, //上下文对象
                HttpExecutionAware execAware //接收线程中断信号的处理类
        ) throws IOException, HttpException;
    
    
    ClientExecChain接口下面的实现类

    默认的httpClient构造实现和调用过程
    1.实例过程,从上到下,下面的exec都把上面的exec实例作为属性实例化
    2.调用过程,从下而上调用


    实例过程和调用过程
    // HttpClientBuilder的builder方法关键代码
     ClientExecChain execChain = createMainExec(
                    requestExecCopy,
                    connManagerCopy,
                    reuseStrategyCopy,
                    keepAliveStrategyCopy,
                    new ImmutableHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
                    targetAuthStrategyCopy,
                    proxyAuthStrategyCopy,
                    userTokenHandlerCopy);
    
            execChain = decorateMainExec(execChain);
            execChain = new ProtocolExec(execChain, httpprocessorCopy);
            execChain = decorateProtocolExec(execChain);
    
            // Add request retry executor, if not disabled
            if (!automaticRetriesDisabled) {
                HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
                if (retryHandlerCopy == null) {
                    retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
                }
                execChain = new RetryExec(execChain, retryHandlerCopy);
            }
    
            HttpRoutePlanner routePlannerCopy = this.routePlanner;
            if (routePlannerCopy == null) {
                SchemePortResolver schemePortResolverCopy = this.schemePortResolver;
                if (schemePortResolverCopy == null) {
                    schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE;
                }
                if (proxy != null) {
                    routePlannerCopy = new DefaultProxyRoutePlanner(proxy, schemePortResolverCopy);
                } else if (systemProperties) {
                    routePlannerCopy = new SystemDefaultRoutePlanner(
                            schemePortResolverCopy, ProxySelector.getDefault());
                } else {
                    routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy);
                }
            }
            // Add redirect executor, if not disabled
            if (!redirectHandlingDisabled) {
                RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
                if (redirectStrategyCopy == null) {
                    redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
                }
                execChain = new RedirectExec(execChain, routePlannerCopy, redirectStrategyCopy);
            }
    
    
            return new InternalHttpClient(
                    execChain,
                    connManagerCopy,
                    routePlannerCopy,
                    cookieSpecRegistryCopy,
                    authSchemeRegistryCopy,
                    defaultCookieStore,
                    defaultCredentialsProvider,
                    defaultRequestConfig != null ? defaultRequestConfig : RequestConfig.DEFAULT,
                    closeablesCopy);
    

    执行过程,拿重试RetryExec示例

     @Override
        public CloseableHttpResponse execute(
                final HttpRoute route,
                final HttpRequestWrapper request,
                final HttpClientContext context,
                final HttpExecutionAware execAware) throws IOException, HttpException {
            Args.notNull(route, "HTTP route");
            Args.notNull(request, "HTTP request");
            Args.notNull(context, "HTTP context");
            final Header[] origheaders = request.getAllHeaders();
            for (int execCount = 1;; execCount++) {
                try {
                    // 调用下游exec的实例对象的execute方法
                    return this.requestExecutor.execute(route, request, context, execAware);
                } catch (final IOException ex) {
                    if (execAware != null && execAware.isAborted()) {
                        this.log.debug("Request has been aborted");
                        throw ex;
                    }
    
                  // 这里判断是否重试,如果重试,还是走到上面的for循环
                    if (retryHandler.retryRequest(ex, execCount, context)) {
                        if (this.log.isInfoEnabled()) {
                            this.log.info("I/O exception ("+ ex.getClass().getName() +
                                    ") caught when processing request to "
                                    + route +
                                    ": "
                                    + ex.getMessage());
                        }
                     
                        request.setHeaders(origheaders);
                     
                    } else {
                        // 不重试,则抛出异常
                        if (ex instanceof NoHttpResponseException) {
                            final NoHttpResponseException updatedex = new NoHttpResponseException(
                                    route.getTargetHost().toHostString() + " failed to respond");
                            updatedex.setStackTrace(ex.getStackTrace());
                            throw updatedex;
                        } else {
                            throw ex;
                        }
                    }
                }
            }
        }
    

    至此httpclient的执行模式则被讲完了,如果不用责任链的模式,举个例子,如果一个请求不用重试,或者自己新加某种责任。则只需要实现ClientExecChain,并在构造httpclient的时候,自己组装责任链,对原有代码无需修改。

    相关文章

      网友评论

          本文标题:httpclient 责任链执行分析

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