文档章节

HttpClient 中文官方教程----第二章连接管理-只收录,未测试

诺岚
 诺岚
发布于 2017/08/30 15:46
字数 3825
阅读 10
收藏 0
点赞 0
评论 0

 


第二章连接管理

2.1。连接持久性

建立从一个主机到另一个主机的连接的过程是相当复杂的,并且涉及两个端点之间的多个分组交换,这可能是相当耗时的。连接握手的开销可能很大,特别是对于小型HTTP消息。如果可以重新使用开放式连接来执行多个请求,则可以实现更高的数据吞吐量。

HTTP / 1.1表示每个默认情况下HTTP连接可以重复使用多个请求。符合HTTP / 1.0标准的端点也可以使用一种机制来明确地传达其优先级,以保持连接的活跃性并将其用于多个请求。HTTP代理还可以在一段时间内保持空闲连接的活动,以防后续请求需要连接到同一目标主机。保持连接的能力通常被称为连接持久性。HttpClient完全支持连接持久性。

2.2。HTTP连接路由

HttpClient能够直接或经由可能涉及多个中间连接(也称为跳)的路由建立与目标主机的连接。HttpClient将路由的连接区分为普通,隧道和分层。使用多个中间代理将连接隧道传送到目标主机称为代理链接。

平原路线是通过连接到目标或第一个也是唯一的代理建立的。隧道路线是通过连接到第一个隧道并通过代理链隧道进行目标建立的。没有代理的路由不能被隧道掘进。通过在现有连接上分层协议来建立分层路由。协议只能通过隧道分层到目标,或通过无代理的直接连接分层。

2.2.1。路线计算

RouteInfo接口表示关于涉及一个或多个中间步骤或跳的目标主机的确定路由的信息。HttpRoute是一个具体的实现,RouteInfo不能改变(是不可变的)。HttpTrackerRouteInfoHttpClient内部使用的可变实现,用于跟踪终端路由目标的剩余跳数。HttpTracker可以在成功执行路由目标的下一跳之后进行更新。HttpRouteDirector是一个帮助类,可用于计算路由中的下一步。这个类由HttpClient内部使用。

HttpRoutePlanner是表示基于执行上下文计算到给定目标的完整路由的策略的接口。HttpClient附带两个默认HttpRoutePlanner实现。SystemDefaultRoutePlanner是基于java.net.ProxySelector。默认情况下,它将从系统属性或运行应用程序的浏览器中选取JVM的代理设置。该DefaultProxyRoutePlanner实现不会使用任何Java系统属性,也不使用任何系统或浏览器代理设置。它总是通过相同的默认代理计算路由。

2.2.2。安全HTTP连接

如果在两个连接端点之间传输的信息无法被未授权的第三方读取或篡改,则HTTP连接可以被认为是安全的。SSL / TLS协议是确保HTTP传输安全性最广泛使用的技术。然而,也可以采用其他加密技术。通常,HTTP传输是通过SSL / TLS加密连接分层的。

2.3。HTTP连接管理器

2.3.1。管理连接和连接管理器

HTTP连接是复杂,有状态,线程不安全的对象,需要正确管理才能正常工作。HTTP连接一次只能由一个执行线程使用。HttpClient采用一个特殊的实体来管理被称为HTTP连接管理器的HTTP连接的访问​​,并由该HttpClientConnectionManager接口表示。HTTP连接管理器的目的是用作新的HTTP连接的工厂,以管理持久连接的生命周期并同步对持久连接的访问​​,确保只有一个线程可以一次访问连接。内部HTTP连接管理器可以处理实例ManagedHttpClientConnection作为管理连接状态并控制I / O操作执行的真实连接的代理。如果托管连接被释放或被其消费者明确关闭,则底层连接将从其代理分离,并返回给管理员。即使服务使用者仍然持有对代理实例的引用,但是它不再能够执行任何I / O操作或者有意或无意地改变实际连接的状态。

这是从连接管理器获取连接的示例:

HttpClientContext context = HttpClientContext.create();
HttpClientConnectionManager connMrg = new BasicHttpClientConnectionManager();
HttpRoute route = new HttpRoute(new HttpHost("localhost", 80));
// Request new connection. This can be a long process
ConnectionRequest connRequest = connMrg.requestConnection(route, null);
// Wait for connection up to 10 sec
HttpClientConnection conn = connRequest.get(10, TimeUnit.SECONDS);
try {
    // If not open
    if (!conn.isOpen()) {
        // establish connection based on its route info
        connMrg.connect(conn, route, 1000, context);
        // and mark it as route complete
        connMrg.routeComplete(conn, route, context);
    }
    // Do useful things with the connection.
} finally {
    connMrg.releaseConnection(conn, null, 1, TimeUnit.MINUTES);
}

ConnectionRequest#cancel()如果需要,可以通过调用来过早终止连接请求。这将解除阻塞该ConnectionRequest#get()方法中的线程。

2.3.2。简单连接管理器

BasicHttpClientConnectionManager是一个简单的连接管理器,一次只维护一个连接。即使这个类是线程安全的,它只能被一个执行线程使用。BasicHttpClientConnectionManager将努力重新使用具有相同路由的后续请求的连接。但是,如果持久连接的路由与连接请求的路由不匹配,则会关闭现有连接并重新打开给定路由。如果连接已经被分配,则 java.lang.IllegalStateException抛出。

应该在EJB容器内使用此连接管理器实现。

2.3.3。池连接管理器

PoolingHttpClientConnectionManager是一个更复杂的实现,它管理一个客户端连接池,并能够服务于来自多个执行线程的连接请求。连接按每个路线合并。对于管理员已经具有在池中可用的持久连接的路由的请求将通过从池中租用连接而不是创建全新的连接来进行服务。

PoolingHttpClientConnectionManager在每个路由基础上总共保持连接的最大限制。每个默认情况下,这个实现将在给定的路由上创建不超过2个并发连接,而​​不再有20个连接。对于许多现实世界的应用程序,这些限制可能被证明是太限制的,特别是如果他们使用HTTP作为其服务的传输协议。

此示例显示如何调整连接池参数:

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// Increase max total connection to 200
cm.setMaxTotal(200);
// Increase default max connection per route to 20
cm.setDefaultMaxPerRoute(20);
// Increase max connections for localhost:80 to 50
HttpHost localhost = new HttpHost("locahost", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);

CloseableHttpClient httpClient = HttpClients.custom()
        .setConnectionManager(cm)
        .build();

2.3.4。连接管理器关机

当HttpClient实例不再需要并且即将超出范围时,重要的是关闭其连接管理器,以确保管理器保持活动的所有连接都被关闭,并释放由这些连接分配的系统资源。

CloseableHttpClient httpClient = <...>
httpClient.close();

2.4。多线程请求执行

当配备了一个池化连接管理器,如 PoolingClientConnectionManagerHttpClient可以同时使用多个执行线程执行多个请求。

PoolingClientConnectionManager将分配根据其配置的连接。如果给定路由的所有连接已经出租,则连接请求将被阻止,直到连接释放回池为止。可以确保连接管理器在连接请求操作中无限制地设置'http.conn-manager.timeout'为正值。如果连接请求在给定的时间段内不能被服务ConnectionPoolTimeoutException将被抛出。

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom()
        .setConnectionManager(cm)
        .build();

// URIs to perform GETs on
String[] urisToGet = {
    "http://www.domain1.com/",
    "http://www.domain2.com/",
    "http://www.domain3.com/",
    "http://www.domain4.com/"
};

// create a thread for each URI
GetThread[] threads = new GetThread[urisToGet.length];
for (int i = 0; i < threads.length; i++) {
    HttpGet httpget = new HttpGet(urisToGet[i]);
    threads[i] = new GetThread(httpClient, httpget);
}

// start the threads
for (int j = 0; j < threads.length; j++) {
    threads[j].start();
}

// join the threads
for (int j = 0; j < threads.length; j++) {
    threads[j].join();
}

虽然HttpClient实例是线程安全的,可以在多个执行线程之间共享,但强烈建议每个线程都维护自己的专用实例HttpContext 

static class GetThread extends Thread {

    private final CloseableHttpClient httpClient;
    private final HttpContext context;
    private final HttpGet httpget;

    public GetThread(CloseableHttpClient httpClient, HttpGet httpget) {
        this.httpClient = httpClient;
        this.context = HttpClientContext.create();
        this.httpget = httpget;
    }

    @Override
    public void run() {
        try {
            CloseableHttpResponse response = httpClient.execute(
                    httpget, context);
            try {
                HttpEntity entity = response.getEntity();
            } finally {
                response.close();
            }
        } catch (ClientProtocolException ex) {
            // Handle protocol errors
        } catch (IOException ex) {
            // Handle I/O errors
        }
    }

}

2.5。连接驱逐政策

经典阻塞I / O模式的主要缺点之一是网络套接字只能在I / O操作中阻塞时对I / O事件做出反应。当连接释放回管理器时,它可以保持活动,但是它无法监视套接字的状态并对任何I / O事件做出反应。如果连接在服务器端关闭,则客户端连接无法检测到连接状态的变化(并通过关闭其端口上的套接字进行适当的响应)。

HttpClient尝试通过测试连接是否“过时”来解决问题,因为在使用连接执行HTTP请求之前,它不再有效,因为它在服务器端已关闭。陈旧的连接检查不是100%可靠。唯一可行的解​​决方案,不涉及空闲连接的每个套接字模型的一个线程,是用于驱逐由于长时间不活动而被认为已过期的连接的专用监视器线程。监视器线程可以周期性地调用ClientConnectionManager#closeExpiredConnections()方法来关闭所有过期的连接,并从池中驱逐关闭的连接。它还可以选择调用ClientConnectionManager#closeIdleConnections()方法来关闭在给定时间段内空闲的所有连接。

public static class IdleConnectionMonitorThread extends Thread {
    
    private final HttpClientConnectionManager connMgr;
    private volatile boolean shutdown;
    
    public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
        super();
        this.connMgr = connMgr;
    }

    @Override
    public void run() {
        try {
            while (!shutdown) {
                synchronized (this) {
                    wait(5000);
                    // Close expired connections
                    connMgr.closeExpiredConnections();
                    // Optionally, close connections
                    // that have been idle longer than 30 sec
                    connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
                }
            }
        } catch (InterruptedException ex) {
            // terminate
        }
    }
    
    public void shutdown() {
        shutdown = true;
        synchronized (this) {
            notifyAll();
        }
    }
    
}

2.6。连接保持活着的策略

HTTP规范没有指定持久连接可能和应该保持活着多久。一些HTTP服务器使用非标准的Keep-Alive头连接到客户端,这段时间以秒为单位打算在服务器端保持连接。HttpClient可以使用这些信息。如果Keep-Alive响应中不存在标题,HttpClient会假定连接可以无限期地保持生效。然而,一般使用的许多HTTP服务器都配置为在一段不活动状态之后删除持久连接,以便节省系统资源,而不会通知客户端。如果默认策略过于乐观,则可能需要提供自定义的保持活动策略。

ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {

    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
        // Honor 'keep-alive' header
        HeaderElementIterator it = new BasicHeaderElementIterator(
                response.headerIterator(HTTP.CONN_KEEP_ALIVE));
        while (it.hasNext()) {
            HeaderElement he = it.nextElement();
            String param = he.getName();
            String value = he.getValue();
            if (value != null && param.equalsIgnoreCase("timeout")) {
                try {
                    return Long.parseLong(value) * 1000;
                } catch(NumberFormatException ignore) {
                }
            }
        }
        HttpHost target = (HttpHost) context.getAttribute(
                HttpClientContext.HTTP_TARGET_HOST);
        if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) {
            // Keep alive for 5 seconds only
            return 5 * 1000;
        } else {
            // otherwise keep alive for 30 seconds
            return 30 * 1000;
        }
    }

};
CloseableHttpClient client = HttpClients.custom()
        .setKeepAliveStrategy(myStrategy)
        .build();

2.7。连接插座工厂

HTTP连接在java.net.Socket内部使用对象来处理通过电线传输数据。但是,它们依赖于ConnectionSocketFactory接口来创建,初始化和连接套接字。这使HttpClient的用户能够在运行时提供应用程序特定的套接字初始化代码。 PlainConnectionSocketFactory是创建和初始化普通(未加密)套接字的默认工厂。

创建套接字和将其连接到主机的过程是去耦合的,以便在连接操作中阻塞时可以关闭套接字。

HttpClientContext clientContext = HttpClientContext.create();
PlainConnectionSocketFactory sf = PlainConnectionSocketFactory.getSocketFactory();
Socket socket = sf.createSocket(clientContext);
int timeout = 1000; //ms
HttpHost target = new HttpHost("localhost");
InetSocketAddress remoteAddress = new InetSocketAddress(
        InetAddress.getByAddress(new byte[] {127,0,0,1}), 80);
sf.connectSocket(timeout, socket, target, remoteAddress, null, clientContext);

2.7.1。安全套接字层

LayeredConnectionSocketFactoryConnectionSocketFactory接口的扩展。分层插座工厂能够在现有的普通套接字上创建套接字。套接字分层主要用于通过代理创建安全套接字。HttpClient附带SSLSocketFactory实现SSL / TLS分层。请注意HttpClient不使用任何自定义加密功能。它完全依赖于标准Java加密(JCE)和安全套接字(JSE)扩展。

2.7.2。与连接管理器集成

自定义连接套接字工厂可以与特定的协议方案(如HTTP或HTTPS)相关联,然后用于创建自定义连接管理器。

ConnectionSocketFactory plainsf = <...>
LayeredConnectionSocketFactory sslsf = <...>
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
        .register("http", plainsf)
        .register("https", sslsf)
        .build();

HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r);
HttpClients.custom()
        .setConnectionManager(cm)
        .build();

2.7.3。SSL / TLS自定义

HttpClient利用SSLConnectionSocketFactory创建SSL连接。SSLConnectionSocketFactory允许高度的定制。它可以javax.net.ssl.SSLContext作为参数的一个实例,并使用它来创建自定义配置的SSL连接。

KeyStore myTrustStore = <...>
SSLContext sslContext = SSLContexts.custom()
        .loadTrustMaterial(myTrustStore)
        .build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);

定制SSLConnectionSocketFactory意味着对SSL / TLS协议的概念有一定程度的了解,其详细说明超出了本文档的范围。有关详细说明和相关工具,请参阅Java™安全套接字扩展(JSSE)参考指南javax.net.ssl.SSLContext

2.7.4。主机名验证

除了信任验证和在SSL / TLS协议级别上执行的客户端认证之外,HttpClient可以可选地验证目标主机名是否与服务器的X.509证书中存储的名称匹配,一旦建立连接。此验证可以提供服务器信任资料的真实性的额外保证。该javax.net.ssl.HostnameVerifier接口表示主机名验证的策略。HttpClient附带两个javax.net.ssl.HostnameVerifier实现。重要提示:主机名验证不应与SSL信任验证混淆。

  • DefaultHostnameVerifier:  HttpClient使用的默认实现预期符合RFC 2818.主机名必须与证书指定的任何备用名称匹配,或者如果没有替代名称给出证书主体的最具体的CN。通配符可以发生在CN和任何主题中。

  • NoopHostnameVerifier: 该主机名验证程序基本上关闭主机名验证。它接受任何SSL会话为有效并与目标主机匹配。

默认情况下,HttpClient使用该DefaultHostnameVerifier实现。如果需要,可以指定不同的主机名验证器实现

SSLContext sslContext = SSLContexts.createSystemDefault();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
        sslContext,
        NoopHostnameVerifier.INSTANCE);

从4.4版本开始,HttpClient使用由Mozilla Foundation维护的公共后缀列表,以确保SSL证书中的通配符不能被滥用以应用于具有公共顶级域的多个域。HttpClient附带了在发布时检索的列表副本。列表的最新版本可以在https://publicsuffix.org/list/找到。制作清单的本地副本,并从其原始位置每天下载该列表不超过一次是非常建议的。

PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(
    PublicSuffixMatcher.class.getResource("my-copy-effective_tld_names.dat"));
DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);

可以使用null匹配器禁用对公共消息列表的验证。

DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(null);

2.8。HttpClient代理配置

即使HttpClient知道复杂的路由方案和代理链接,它只支持简单的直接或一跳代理连接开箱即用。

告诉HttpClient通过代理连接到目标主机的最简单方法是设置默认代理参数:

HttpHost proxy = new HttpHost("someproxy", 8080);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
CloseableHttpClient httpclient = HttpClients.custom()
        .setRoutePlanner(routePlanner)
        .build();

还可以指示HttpClient使用标准的JRE代理选择器来获取代理信息:

SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(
        ProxySelector.getDefault());
CloseableHttpClient httpclient = HttpClients.custom()
        .setRoutePlanner(routePlanner)
        .build();

或者,可以提供自定义RoutePlanner实现,以便完全控制HTTP路由计算的过程:

HttpRoutePlanner routePlanner = new HttpRoutePlanner() {

    public HttpRoute determineRoute(
            HttpHost target,
            HttpRequest request,
            HttpContext context) throws HttpException {
        return new HttpRoute(target, null,  new HttpHost("someproxy", 8080),
                "https".equalsIgnoreCase(target.getSchemeName()));
    }

};
CloseableHttpClient httpclient = HttpClients.custom()
        .setRoutePlanner(routePlanner)
        .build();
    }
}
     
     

© 著作权归作者所有

共有 人打赏支持
诺岚
粉丝 0
博文 88
码字总数 145046
作品 0
广州
程序员
Android:HttpClient研究

HttpClient4 用法 由HttpClient3 升级到 HttpClient_4 必看 摘要:HttpClient程序包是一个实现了 HTTP 协议的客户端编程工具包,要想熟练的掌握它,必须熟悉HTTP协议。一个最简单的调用如下:...

boonya
2015/03/27
0
2
HttpClient4.3教程 第二章 连接管理

HttpClient4.3教程 第二章 连接管理 2.1.持久连接 两个主机建立连接的过程是很复杂的一个过程,涉及到多个数据包的交换,并且也很耗时间。Http连接需要的三次握手开销很大,这一开销对于比较...

youthflies
2013/10/11
0
0
apache HttpClient学习系列--1

这两天开始准备做一个自己的网络爬虫,所以就各种找资料,找到了一个资料,讲的挺好的,用的就是HttpClient来写的,就在apache上下了jar包,准备自己编写,但是硬是找不到对应的类。上了apa...

无情小白龙
2014/03/15
0
0
读书《HttpClient 教程》

读书《HttpClient 教程》 前言 尽管java.net包提供了基本通过HTTP访问资源的功能,但它没有提供全面的灵活性和其它很多应用程序需要的功能。HttpClient就是寻求弥补这项空白的组件,通过提供...

放个屁
2015/05/04
0
0
解决HttpClient的FilePart上传文件中使用中文名称文件名乱码问题

String targetUrl = "http://localhost:8080/Test";

zenith
2010/06/11
0
0
httpClient4.2官方文档研究

前言 超文本传输协议(HTTP)也许是最常用的在互联网上使用的协议。 Web服务,支持网络设备和网络计算的增长继续扩大用户驱动的Web浏览器的HTTP协议之外的作用,同时增加了一些应用程序需要H...

harries
2015/08/24
0
0
HttpClient_4 用法 由HttpClient_3 升级到 HttpClient_4 必看

HttpClient程序包是一个实现了 HTTP 协议的客户端编程工具包,要想熟练的掌握它,必须熟悉 HTTP协议。一个最简单的调用如下:

落落的月
2012/05/11
0
0
使用单例模式实现自己的HttpClient工具类

引子 在Android开发中我们经常会用到网络连接功能与服务器进行数据的交互,为此Android的SDK提供了Apache的HttpClient来方便我们使用各种Http服务。你可以把HttpClient想象成一个浏览器,通过...

moz1q1
2015/04/01
0
0
HttpClient 4.3.6教程 第1章 基础 【翻译】

最近工作要用到httpclient,发现一个翻译,mark一下: HttpClient 4.3.6教程 第1章 基础 【翻译】

z_jordon
2015/05/09
0
0
java模拟HTTP请求(集合了网上搜来的各种)

Java发送http请求 (get 与 post方法请求) Java发送http请求 (get 与post方法请求),以下代码经本人亲自调试可用!可以直接使用之。 注意:通过BufferedReader 读取远程返回的数据时,必须设置...

Yason_Luo
2014/01/08
0
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

SpringBoot | 第十章:Swagger2的集成和使用

前言 前一章节介绍了mybatisPlus的集成和简单使用,本章节开始接着上一章节的用户表,进行Swagger2的集成。现在都奉行前后端分离开发和微服务大行其道,分微服务及前后端分离后,前后端开发的...

oKong
今天
5
0
Python 最小二乘法 拟合 二次曲线

Python 二次拟合 随机生成数据,并且加上噪声干扰 构造需要拟合的函数形式,使用最小二乘法进行拟合 输出拟合后的参数 将拟合后的函数与原始数据绘图后进行对比 import numpy as npimport...

阿豪boy
今天
1
0
云拿 无人便利店

附近(上海市-航南路)开了家无人便利店.特意进去体验了一下.下面把自己看到的跟大家分享下. 经得现场工作人员同意后拍了几张照片.从外面看是这样.店门口的指导里强调:不要一次扫码多个人进入....

周翔
昨天
1
0
Java设计模式学习之工厂模式

在Java(或者叫做面向对象语言)的世界中,工厂模式被广泛应用于项目中,也许你并没有听说过,不过也许你已经在使用了。 简单来说,工厂模式的出现源于增加程序序的可扩展性,降低耦合度。之...

路小磊
昨天
165
1
npm profile 新功能介绍

转载地址 npm profile 新功能介绍 npm新版本新推来一个功能,npm profile,这个可以更改自己简介信息的命令,以后可以不用去登录网站来修改自己的简介了 具体的这个功能的支持大概是在6这个版...

durban
昨天
1
0
Serial2Ethernet Bi-redirection

Serial Tool Serial Tool is a utility for developing serial communications, custom protocols or device testing. You can set up bytes to send accordingly to your protocol and save......

zungyiu
昨天
1
0
python里求解物理学上的双弹簧质能系统

物理的模型如下: 在这个系统里有两个物体,它们的质量分别是m1和m2,被两个弹簧连接在一起,伸缩系统为k1和k2,左端固定。假定没有外力时,两个弹簧的长度为L1和L2。 由于两物体有重力,那么...

wangxuwei
昨天
0
0
apolloxlua 介绍

##项目介绍 apolloxlua 目前支持javascript到lua的翻译。可以在openresty和luajit里使用。这个工具分为两种模式, 一种是web模式,可以通过网页使用。另外一种是tool模式, 通常作为大规模翻...

钟元OSS
昨天
2
0
Mybatis入门

简介: 定义:Mybatis是一个支持普通SQL查询、存储过程和高级映射的持久层框架。 途径:MyBatis通过XML文件或者注解的形式配置映射,实现数据库查询。 特性:动态SQL语句。 文件结构:Mybat...

霍淇滨
昨天
2
0
开发技术瓶颈期,如何突破

前言 读书、学习的那些事情,以前我也陆续叨叨了不少,但总觉得 “学习方法” 就是一个永远在路上的话题。个人的能力、经验积累与习惯方法不尽相同,而且一篇文章甚至一本书都很难将学习方法...

_小迷糊
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部