上一个文章说了http连接的缓存池,以及借出的过程,借出使用的是HttoRoute,先根据route定位到RouteSpecificPool,然后从RouteSpecificPool中借出一个httpConnection,借出后都是用一个Entry包装的,entry里面含有httpRouter+httpConnection。这一篇说一下借出之前的过程以及借出后的使用操作。
缓存的对象是CPool,他的使用是在HttpClientConnectionManager里面,这个类从他的名字就能看出来是管理HttpConnection的,他的实现类一般都是用的PoolingHttpClientConnectionManager,即使用缓存的httpConnection管理器,这个类的里面就会用到了CPool类,在创建一个PoolingHttpClientConnectionManager的时候可以发现:
public PoolingHttpClientConnectionManager( final HttpClientConnectionOperator httpClientConnectionOperator, final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory, final long timeToLive, final TimeUnit tunit) { super(); this.configData = new ConfigData();//这个是一些配置参数,都是用于socket连接以及http通信的参数,后面会详细写 this.pool = new CPool(new InternalConnectionFactory(this.configData, connFactory), 2, 20, timeToLive, tunit);//如果没有传递一个CPool,则要生产一个默认的,默认的里面会会设置每个Route最多可以有两个connection,一个CPool最多可以开10个connection,第一个参数是用于创建一个httpConnection的,暂时先忽略第一个参数 this.pool.setValidateAfterInactivity(5000);//设置校验的最大时间,上一篇文章中说了如果一个PoolEnty的updateTime + 校验时间<当前时间,就要检查一下是否可以,如果没有设置校验时间默认是5秒。 this.connectionOperator = Args.notNull(httpClientConnectionOperator, "HttpClientConnectionOperator");//ConnectionOperato是管理具体的http连接的,比如使用schema解析端口,dns确定ip,然后将本地一个socket和远端的socket进行连接 this.isShutDown = new AtomicBoolean(false); } /** * Visible for test. */ PoolingHttpClientConnectionManager( final CPool pool,//也可以指定一个CPool,这样就不会那些配置很低的连接了 final Lookup<ConnectionSocketFactory> socketFactoryRegistry,//这个类更简单,是用于确定socket类型的,如果是http的就是创建一个socket,如果是https的就会创建一个加密的socket. final SchemePortResolver schemePortResolver, final DnsResolver dnsResolver) { super(); this.configData = new ConfigData(); this.pool = pool; this.connectionOperator = new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver);//功能同上 this.isShutDown = new AtomicBoolean(false); }
看完了构造方法可以发现他里面创建了一个CPool,并更加熟悉了CPool的配置,CPool里面含有一个connectionFactory,用于创建http连接,不过这个过于底层,直接和http的请求的细节相关联,所以我没有看,而且不影响应用层的理解;在创建了CPool之后还可以使用方法修改pool的配置,比如;
@Override public void setMaxTotal(final int max) {//修改总的连接的爽 this.pool.setMaxTotal(max); } @Override public void setDefaultMaxPerRoute(final int max) {//修改默认的route对应的httpConnection的数量,如果某个route没有配置的话 this.pool.setDefaultMaxPerRoute(max); } @Override public void setMaxPerRoute(final HttpRoute route, final int max) {//指定某个route最大的connection的数量 this.pool.setMaxPerRoute(route, max); }
既然这个类管理了一个CPool,那么借出connection的方法就在这个类里面了,方法是:requestConnection
public ConnectionRequest requestConnection(final HttpRoute route,final Object state) {//根据httpRoute借出一个,返回的并不是一个PoolEnty或者是connection,而是用一个ConnectionReuqet封装了, Args.notNull(route, "HTTP route"); final Future<CPoolEntry> future = this.pool.lease(route, state, null);//从pool中借出一个,此方法可能会借出一个,也可能等待,等待后可能最终会借出一个,也可能是超时。 return new ConnectionRequest() { @Override public boolean cancel() { return future.cancel(true); } @Override public HttpClientConnection get(final long timeout,final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException {//再次调用get方法才会获得最终的connection,里面会调用leaseConnection方法 return leaseConnection(future, timeout, tunit); } }; } protected HttpClientConnection leaseConnection(final Future<CPoolEntry> future, final long timeout,final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { final CPoolEntry entry; try { entry = future.get(timeout, tunit); if (entry == null || future.isCancelled()) { throw new InterruptedException(); } Asserts.check(entry.getConnection() != null, "Pool entry with no connection"); return CPoolProxy.newProxy(entry);//这个方法将借出的CpoolEntry进行了封装,封装为一个HttpClientConnection, } catch (final TimeoutException ex) { throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool"); } }
对于连接的获取是在 ClientExecChain里面,httpClient自带了很多的exec,也就是http请求的发起对象,我就看了一个最简单的:MinimalClientExec,这个是最简单的http请求的执行器,仅仅执行最基础的操作,不会进行失败重连,如果有错误也不会处理而是抛出来,对于这个类和httpConnection、httpConnectionFactory一样,不要过多的去深究,因为内部的网络通信和http的具体协议都在这里面了,对于我们的应用层是没有必要知道这些的。仅仅看看他的exec方法,这个方法就是执行http请求的方法
@Override public CloseableHttpResponse execute(final HttpRoute route,final HttpRequestWrapper request,final HttpClientContext context,final HttpExecutionAware execAware) throws IOException, HttpException { rewriteRequestURI(request, route); final ConnectionRequest connRequest = connManager.requestConnection(route, null);//从连接池里面获得connection if (execAware != null) {//不用看,没懂什么意思,但是不妨碍应用层面的了解 if (execAware.isAborted()) { connRequest.cancel(); throw new RequestAbortedException("Request aborted"); } else { execAware.setCancellable(connRequest); } } final RequestConfig config = context.getRequestConfig();//获得这次请求的配置,这里面含有三个时间,下面有 final HttpClientConnection managedConn; try { final int timeout = config.getConnectionRequestTimeout();//从连接池中获得connection的超时时间 managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS); } catch(final InterruptedException interrupted) { Thread.currentThread().interrupt(); throw new RequestAbortedException("Request aborted", interrupted); } catch(final ExecutionException ex) { Throwable cause = ex.getCause(); if (cause == null) { cause = ex; } throw new RequestAbortedException("Request execution failed", cause); } final ConnectionHolder releaseTrigger = new ConnectionHolder(log, connManager, managedConn);//用一个对象封装connection,这个对象增加了是否可用以及可用时间的功能,以方便重用这个connection,所以起名叫做releaseTrigger try { if (execAware != null) {// if (execAware.isAborted()) { releaseTrigger.close(); throw new RequestAbortedException("Request aborted"); } else { execAware.setCancellable(releaseTrigger); } } if (!managedConn.isOpen()) {//刚创建出来的connection是没有open的,没有关联socket,此时要建立tcp连接 final int timeout = config.getConnectTimeout();//建立连接的超时时间 this.connManager.connect(managedConn,route,timeout > 0 ? timeout : 0,context);//这个地方就用到了连接的方法,下面有介绍 this.connManager.routeComplete(managedConn, route, context);//标志建立连接完成了 } final int timeout = config.getSocketTimeout();//socket的超时时间 if (timeout >= 0) { managedConn.setSocketTimeout(timeout); } //下面这一堆是用于发送http请求用的,不涉及应用层面,所以不用看 HttpHost target = null; final HttpRequest original = request.getOriginal(); if (original instanceof HttpUriRequest) { final URI uri = ((HttpUriRequest) original).getURI(); if (uri.isAbsolute()) { target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); } } if (target == null) { target = route.getTargetHost(); } context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target); context.setAttribute(HttpCoreContext.HTTP_REQUEST, request); context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn); context.setAttribute(HttpClientContext.HTTP_ROUTE, route); httpProcessor.process(request, context); final HttpResponse response = requestExecutor.execute(request, managedConn, context); httpProcessor.process(response, context); // The connection is in or can be brought to a re-usable state. if (reuseStrategy.keepAlive(response, context)) {//这里就看到了1.0和1.1的区别,1.1是keepalive的,而1.0则不是 // Set the idle duration of this connection final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);//判断剩余的时间是多少,从他的具体实现来看是查看了一下一个叫做timeout的heade来确认的,所以我猜测是由服务端确定的这个数字。 releaseTrigger.setValidFor(duration, TimeUnit.MILLISECONDS);//标记这个connection在多少时间内不需要校验。 releaseTrigger.markReusable();//设置为可重用 } else {//如果是1.0的,则标记这个连接是不可复用的 releaseTrigger.markNonReusable(); } // check for entity, release connection if possible final HttpEntity entity = response.getEntity(); if (entity == null || !entity.isStreaming()) {//这里的isSteaming我一直没有理解,难不成有的http不是基于stream的?我做过的试验都不会进入这个方法。如果有人知道这里的原因,请联系我,我的qq是:1308567317 // connection not needed and (assumed to be) in re-usable state releaseTrigger.releaseConnection(); return new HttpResponseProxy(response, null); } else {//我做的试验都是进入这个,返回的这个代理对象增加了一些功能,比如在关闭inpustream的时候可以自动释放连接。下一篇会看这个对象。 return new HttpResponseProxy(response, releaseTrigger); } } catch (final ConnectionShutdownException ex) { final InterruptedIOException ioex = new InterruptedIOException( "Connection has been shut down"); ioex.initCause(ex); throw ioex; } catch (final HttpException ex) { releaseTrigger.abortConnection(); throw ex; } catch (final IOException ex) { releaseTrigger.abortConnection(); throw ex; } catch (final RuntimeException ex) { releaseTrigger.abortConnection(); throw ex; } }
上面说了如何使用connection的,大致的逻辑是先借一个,然后判断其有没有连接socket(如果是新建的就没有),如果没有就要调用connManager.connect(),即将当前的connection绑定到一个socket上。这个方法还是很关键的,看下PoolingHttpClientConnectionManager.connect方法
@Override public void connect(final HttpClientConnection managedConn,final HttpRoute route,final int connectTimeout,final HttpContext context) throws IOException {//给一个connection添加底层的socket连接,第一个参数是要添加socket的connection,第二个是route,第三个则是指定的tcp连接的连接超时时间。 final ManagedHttpClientConnection conn; synchronized (managedConn) { final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn);//因为借出去的是一个代理,所以要从代理里面获得里面的代理对西乡 conn = entry.getConnection(); } final HttpHost host; if (route.getProxyHost() != null) { host = route.getProxyHost(); } else { host = route.getTargetHost(); } final InetSocketAddress localAddress = route.getLocalSocketAddress(); SocketConfig socketConfig = this.configData.getSocketConfig(host);//获得socket的配置,比如缓冲大小、是否复用、linger时间 if (socketConfig == null) { socketConfig = this.configData.getDefaultSocketConfig(); } if (socketConfig == null) { socketConfig = SocketConfig.DEFAULT; } this.connectionOperator.connect(conn, host, localAddress, connectTimeout, socketConfig, context); }
上面的连接方法是委托给了 DefaultHttpClientConnectionOperator connectionOperator属性,这个DefaultHttpClientConnectionOperator 类就是将创建的httpConnection和某个socket关联起来,因为http最终还是要使用tcp连接,使用socket,那一块的功能代码都在这个类里面了。
看一下这个类的重要方法:
public class DefaultHttpClientConnectionOperator implements HttpClientConnectionOperator { static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry"; private final Log log = LogFactory.getLog(getClass()); private final Lookup<ConnectionSocketFactory> socketFactoryRegistry; private final SchemePortResolver schemePortResolver; private final DnsResolver dnsResolver; public DefaultHttpClientConnectionOperator(final Lookup<ConnectionSocketFactory> socketFactoryRegistry, final SchemePortResolver schemePortResolver,final DnsResolver dnsResolver) { super(); Args.notNull(socketFactoryRegistry, "Socket factory registry"); this.socketFactoryRegistry = socketFactoryRegistry;//用于获得socketFactory,根据schema获得,如果是http的就返回一个能创建socket的registry,如果是https的就返回一个能创建加密socket的registry this.schemePortResolver = schemePortResolver != null ? schemePortResolver :DefaultSchemePortResolver.INSTANCE;//在连接的时候要指定端口,这个用于根据schema确定端口 this.dnsResolver = dnsResolver != null ? dnsResolver : SystemDefaultDnsResolver.INSTANCE;//用于根据dns确定ip } @SuppressWarnings("unchecked") private Lookup<ConnectionSocketFactory> getSocketFactoryRegistry(final HttpContext context) { Lookup<ConnectionSocketFactory> reg = (Lookup<ConnectionSocketFactory>) context.getAttribute(SOCKET_FACTORY_REGISTRY); if (reg == null) { reg = this.socketFactoryRegistry; } return reg; } //用于将创建的httpConnection和socket关联起来,因为http最终还是要靠tcp连接的,所以还是要使用socket,刚刚创建的httpConnection的isOpen返回的额是false,此时就要进行connect,进入这个方法, public void connect(final ManagedHttpClientConnection conn,//创建的连接,注意这个连接只是制定了连接了具体参数和怎么连接,并没有底层的socket,使用socket需要经过这个方法 final HttpHost host,//连接的远端服务器 final InetSocketAddress localAddress, final int connectTimeout,//连接的超时时间,即tcp的超时时间 final SocketConfig socketConfig,//建立socket连接的配置参数,比如socketTimeOUT,是否重用,soLinger,keepAlive,接受缓存和发送缓存大小 final HttpContext context) throws IOException {//httpContext在整个httpConnection的使用过程中使用 final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context); final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());//根据schema确定socketFactory的类型,分为http的和https的,获得一个socketFactory,用于产生一个socket if (sf == null) { throw new UnsupportedSchemeException(host.getSchemeName() +" protocol is not supported"); } final InetAddress[] addresses = host.getAddress() != null ?new InetAddress[] { host.getAddress() } : this.dnsResolver.resolve(host.getHostName()); final int port = this.schemePortResolver.resolve(host);//如果host中没有指定port则根据schema确定port,如果是http的就是80,https的是433 for (int i = 0; i < addresses.length; i++) { final InetAddress address = addresses[i]; final boolean last = i == addresses.length - 1; Socket sock = sf.createSocket(context);//产生一个socket 下面都是配置socket的参数 sock.setSoTimeout(socketConfig.getSoTimeout());//包的最大间隔时间 sock.setReuseAddress(socketConfig.isSoReuseAddress());//是否使用time_wait阶段的端口, sock.setTcpNoDelay(socketConfig.isTcpNoDelay()); sock.setKeepAlive(socketConfig.isSoKeepAlive()); final int linger = socketConfig.getSoLinger(); if (linger >= 0) { sock.setSoLinger(true, linger); } conn.bind(sock);//这个httpConnection使用这个socket final InetSocketAddress remoteAddress = new InetSocketAddress(address, port); if (this.log.isDebugEnabled()) { this.log.debug("Connecting to " + remoteAddress); } try { sock = sf.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);//建立连接,不过我没懂这里为啥要写两遍conn.bind(sock) conn.bind(sock); return; } catch (final SocketTimeoutException ex) { if (last) { throw new ConnectTimeoutException(ex, host, addresses); } } catch (final ConnectException ex) { if (last) { final String msg = ex.getMessage(); if ("Connection timed out".equals(msg)) { throw new ConnectTimeoutException(ex, host, addresses); } else { throw new HttpHostConnectException(ex, host, addresses); } } } catch (final NoRouteToHostException ex) { if (last) { throw ex; } } } } }
上面这个类的最终的作用就是创建一个socket然后和服务端进行连接,并将一个socket关联到一个httpConnection上,刚创建的httpConnection的isOpen方法是false,此时就要进行connect。这个部分的代码太深了,在BHttpConnectionBase中,这个类是所有httpConnection的基类,我没仔细看,我认为这个类已经和http协议涉及的太密切了,不再是应用层的东西了,所以就没有看。
这篇博客就是讲了httpConnection是如何使用的,主要就是从pool中借出一个来,然后添加底层的socket tcp连接,然后使用其发送请求,至于详细的发送请求的那一块直接略过了,因为那些是http自己的小细节,已经不属于我们的应用层了,所以就没看。
至此http的存储以及获取还有使用已经都看了,下一篇看一下httpConnection的归还以及关闭。
相关推荐
HttpClient是Apache HTTP Components项目中用于Java的一个重要的客户端HTTP连接库,广泛用于网络编程,特别是网络爬虫的开发。以下是对文件内容的详细解读和知识点的梳理: 1. HTTP客户端的基础知识 - HTTP请求:...
3. **客户端程序**:使用 HttpClient 发起 HTTP 请求来获取文件。 #### 三、关键概念解释 - **HttpClient**:Apache HttpClient 是 Apache HttpComponents 项目中的一个子项目,提供了一组高级APIs用于发送HTTP...
Apache HttpClient是一个广泛使用在Java应用程序中,用于执行HTTP协议相关的操作的开源库。本手册重点介绍了HttpClient 4.5.5版本的使用方法和相关知识点,为读者提供了一个详尽的学习和参考材料。 首先,在基础...
VB还可以进行网络编程,例如使用Winsock控件实现TCP/IP通信,或者使用HTTPClient类进行Web请求。这使得VB能够开发网络应用,如FTP客户端、邮件发送程序等。 十、API调用 VB可以通过 Declare 语句调用操作系统级别...
以下是对该书主要内容的详细解读: 1. **HTTP请求**: - **请求行**:HTTP请求的第一部分,包括方法(如GET或POST)、URL和HTTP版本号,用于告诉服务器如何处理请求。 - **请求报头**:包含了客户端信息、请求...
- 使用HttpClient:介绍Apache HttpClient库,及其在Android中的应用,包括GET和POST请求、处理响应数据等。 3. **异步网络请求** - AsyncTask:解析Android提供的异步任务类,用于在后台线程执行网络请求,避免...
10. **httpcore-4.0.1.jar**:这是Apache HttpCore库,提供HTTP协议的基本实现,包括连接管理、传输编码等,是HttpClient的基础组件。 这些库的集成使用,体现了Jeecms在构建过程中对性能优化、数据库交互、安全...
在Java编程语言中,发送短信是一项常见的功能,...以上就是使用Java发送短信涉及的关键知识点,包括API的使用、JSP集成、jar包管理以及文档解读等。实践中,你可能需要结合实际项目需求和特定的短信服务商进行调整。
- HTTP通信:使用HttpURLConnection或HttpClient进行网络请求,处理响应数据。 - JSON解析:学习Gson、Jackson等库,将JSON数据转换为Java对象。 - AsyncTask:异步任务处理,提供在UI线程中更新进度的方法。 - ...
开发者可能使用了ESP8266WiFi库来建立和维护Wi-Fi连接,使用HTTP客户端库如ESP8266HTTPClient来从网络获取天气API的数据。这些API通常提供JSON格式的天气信息,包括温度、湿度、风速等。代码会解析这些数据并将其...
- WebClient和HttpClient类:进行HTTP请求和响应处理。 12. **数据库操作** - ADO.NET:连接数据库、执行SQL语句、填充DataSet和DataView。 - LINQ to SQL:使用C#查询数据库,简化数据操作。 13. **设计模式**...
2. **设置请求属性**:你可以设置连接超时、读取超时,以及请求方法为GET。此外,还可以设置Accept头来指定服务器返回的数据类型。 ```java connection.setConnectTimeout(5000); connection.setReadTimeout...
- **知识点概述**:使用`HttpClient`发送GET请求。 - **详细解释**: - `HttpGet`类:用于构建HTTP GET请求。 - `Get`、`URLConnection`、`HttpPost`:这些不是用于构建GET请求的合适类或接口。 ### ...
3. **处理网络通信**:根据传输层协议,使用Java的Socket编程或者HttpClient库进行网络通信。接收服务器发送的HJ212数据,或者发送请求获取数据。 4. **异常处理**:在解析和通信过程中,可能会遇到各种错误,如...
- **网络通信**:他能有效地使用HTTP、Socket通信,以及XML和JSON解析,实现网络数据交换。 - **设计模式**:张利洁了解并能应用常见的设计模式,如工厂模式和单例模式,这有助于提高代码的灵活性和可复用性。 2....
4. `commons-httpclient.jar`:Apache Commons HttpClient,提供了HTTP客户端API,用于进行HTTP通信,Spring使用它来处理HTTP请求。 5. `commons-lang.jar`:Apache Commons Lang,包含了一些Java语言功能的扩展,...
- **解释**: 介绍了如何使用Apache HttpClient来处理与服务器之间通信过程中遇到的无效SSL证书问题。这通常涉及到信任管理器的配置和SSL上下文的设置。 ##### 食谱20: Java中日期时间的转换 - **知识点**: 日期时间...
2. HttpURLConnection与HttpClient的选择与使用。 3. JSON、XML数据的解析与序列化库的使用,如Jackson、Gson、Fastjson。 六、JVM内存管理 1. 堆内存与栈内存的区别,新生代与老年代的划分。 2. 分代垃圾收集器:...
本文主要针对W5500IO模块的用户使用手册进行解读,并提炼出其中的重要知识点。 ### W5500芯片简介 W5500芯片是一个带有全硬线化TCP/IP协议栈的以太网控制器芯片。它支持8个独立的socket通信,支持TCP, UDP, IPv4, ...
7. **图7:互联网应用技术-中间件.jpg**:中间件可能是重点,比如Tomcat、Jetty等应用服务器,Nginx反向代理服务器,以及Log4j、Apache HttpClient等工具的使用。 8. **图8:互联网应用技术-解决方案篇.jpg**:这...