HttpClient Fluent API

Apache 官方提供了一个封装了HttpClient的API(Fluent API),底层采用HTTP连接池技术提高了性能。官方文档

一、官方例子

As of version of 4.2 HttpClient comes with an easy to use facade API based on the concept of a fluent interface. Fluent facade API exposes only the most fundamental functions of HttpClient and is intended for simple use cases that do not require the full flexibility of HttpClient. For instance, fluent facade API relieves the users from having to deal with connection management and resource deallocation.

// Execute a GET with timeout settings and return response content as String.
Request.Get("http://somehost/")
        .connectTimeout(1000)
        .socketTimeout(1000)
        .execute().returnContent().asString();

在4.2版本中,HttpClient提供了一个基于流式(链式调用)接口概念的易于使用的facade API。
Fluent facade API只公开HttpClient最基本的功能,并适用于不需要HttpClient完全灵活性的简单用例。例如,fluent facade API使用户不必处理连接管理和资源分配。

// Execute a POST with the 'expect-continue' handshake, using HTTP/1.1,
// containing a request body as String and return response content as byte array.
Request.Post("http://somehost/do-stuff")
        .useExpectContinue()
        .version(HttpVersion.HTTP_1_1)
        .bodyString("Important stuff", ContentType.DEFAULT_TEXT)
        .execute().returnContent().asBytes();
// Execute a POST with a custom header through the proxy containing a request body
// as an HTML form and save the result to the file
Request.Post("http://somehost/some-form")
        .addHeader("X-Custom-header", "stuff")
        .viaProxy(new HttpHost("myproxy", 8080))
        .bodyForm(Form.form().add("username", "vip").add("password", "secret").build())
        .execute().saveContent(new File("result.dump"));

One can also use Executor directly in order to execute requests in a specific security context whereby authentication details are cached and re-used for subsequent requests.

Executor executor = Executor.newInstance()
        .auth(new HttpHost("somehost"), "username", "password")
        .auth(new HttpHost("myproxy", 8080), "username", "password")
        .authPreemptive(new HttpHost("myproxy", 8080));

executor.execute(Request.Get("http://somehost/"))
        .returnContent().asString();

executor.execute(Request.Post("http://somehost/do-stuff")
        .useExpectContinue()
        .bodyString("Important stuff", ContentType.DEFAULT_TEXT))
        .returnContent().asString();

还可以直接使用Executor(执行器)在特定的安全上下文中执行请求,从而缓存身份验证细节并为后续请求重用。
Response handling(响应处理):

The fluent facade API generally relieves the users from having to deal with connection management and resource deallocation. In most cases, though, this comes at a price of having to buffer content of response messages in memory. It is highly recommended to use ResponseHandler for HTTP response processing in order to avoid having to buffer content in memory.

Document result = Request.Get("http://somehost/content")
        .execute().handleResponse(new ResponseHandler<Document>() {

    public Document handleResponse(final HttpResponse response) throws IOException {
        StatusLine statusLine = response.getStatusLine();
        HttpEntity entity = response.getEntity();
        if (statusLine.getStatusCode() >= 300) {
            throw new HttpResponseException(
                    statusLine.getStatusCode(),
                    statusLine.getReasonPhrase());
        }
        if (entity == null) {
            throw new ClientProtocolException("Response contains no content");
        }
        DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
            ContentType contentType = ContentType.getOrDefault(entity);
            if (!contentType.equals(ContentType.APPLICATION_XML)) {
                throw new ClientProtocolException("Unexpected content type:" +
                    contentType);
            }
            String charset = contentType.getCharset();
            if (charset == null) {
                charset = HTTP.DEFAULT_CONTENT_CHARSET;
            }
            return docBuilder.parse(entity.getContent(), charset);
        } catch (ParserConfigurationException ex) {
            throw new IllegalStateException(ex);
        } catch (SAXException ex) {
            throw new ClientProtocolException("Malformed XML document", ex);
        }
    }

    });

fluent facade API通常使用户不必处理连接管理和资源分配。不过,在大多数情况下,这是以必须在内存中缓冲响应消息的内容为代价的。强烈建议将ResponseHandler用于HTTP响应处理,以避免必须在内存中缓冲内容。

二、扩展说明

1.加入maven依赖

  <dependency>
            <!--fluent-hc是HttpClient基于流式API封装的客户端-->
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>fluent-hc</artifactId>
            <version>4.5.8</version>
  </dependency>

2.查看源码
打开Request对象的execute方法可以看到是采用Executor执行器的HttpClient对象来执行请求。Request的execute方法如下:

  public Response execute() throws ClientProtocolException, IOException {
        return new Response(this.internalExecute(Executor.CLIENT, (HttpContext)null));
    }

Executor执行器的源码如下:

public class Executor {
    static final PoolingHttpClientConnectionManager CONNMGR;
    static final HttpClient CLIENT;
    private final HttpClient httpclient;
    private volatile AuthCache authCache;
    private volatile CredentialsProvider credentialsProvider;
    private volatile CookieStore cookieStore;
    
    public static Executor newInstance() {
        return new Executor(CLIENT);
    }

    public static Executor newInstance(HttpClient httpclient) {
        return new Executor(httpclient != null ? httpclient : CLIENT);
    }

    Executor(HttpClient httpclient) {
        this.httpclient = httpclient;
        this.authCache = new BasicAuthCache();
    }
  ... ...
    static {
        SSLConnectionSocketFactory ssl = null;

        try {
            ssl = SSLConnectionSocketFactory.getSystemSocketFactory();
        } catch (SSLInitializationException var7) {
            try {
                SSLContext sslcontext = SSLContext.getInstance("TLS");
                sslcontext.init((KeyManager[])null, (TrustManager[])null, (SecureRandom)null);
                ssl = new SSLConnectionSocketFactory(sslcontext);
            } catch (SecurityException var4) {
                ;
            } catch (KeyManagementException var5) {
                ;
            } catch (NoSuchAlgorithmException var6) {
                ;
            }
        }

        Registry<ConnectionSocketFactory> sfr = RegistryBuilder.create().register("http",     PlainConnectionSocketFactory.getSocketFactory()).register("https", ssl != null ? ssl :  SSLConnectionSocketFactory.getSocketFactory()).build();
        CONNMGR = new PoolingHttpClientConnectionManager(sfr);
        //将每个路由的默认最大连接数设置为100
        CONNMGR.setDefaultMaxPerRoute(100);
        //设置最大连接数
        CONNMGR.setMaxTotal(200);
        CONNMGR.setValidateAfterInactivity(1000);
        CLIENT = HttpClientBuilder.create().setConnectionManager(CONNMGR).build();
    }

可以看到Executor执行器的CLIENT对象是从连接池中获取的HttpClient对象。采用HTTP连接池技术降低了频繁建立HTTP连接的时间开销,减少了TCP连接建立和释放时socket通信服务器端资源的浪费,同时支持更高的并发量。

3.调用例子

    @GetMapping("/emps")
    public String findAllEmps() {
        try {
            return Request.Get("http://localhost:8888/emps")
                    .connectTimeout(5000) // 创建连接的最长时间  建立连接的超时时间
                    .socketTimeout(20*1000) // 数据传输的最长时间20s 等待服务端响应数据的超时时间。
                    .execute().returnContent().asString();
        } catch (IOException e) {
            LOG.error("error", e);
        }
        return  null;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值