本文接着上一篇博客继续分析,Connection是如何获取的,先说点基础的上篇分析到MainClientExec类#execute()是主要的执行方法,再看看改方法里面的连接时如何获取的。
下面来看HttpClientConnection怎么生成的。核心的接口就是HttpConnectionFactory
看看 connManager.requestConnection(route, userToken)是由BasicHttpClientConnectionManager的#requestConnection()执行
最终执行的是ManagedHttpClientConnectionFactory的#create()方法
这里要注意,因为返回的是一个连接的对象(构造方法实例化对象),是比较复杂的继承和实现的关系,所有的实现都是通过自己和上面的父类的构造方法来实现的,我们来看一下
主要看看DefaultBHttpClientConnection,里面唯一的两个元素,看到名称就可以猜到应该是request的写和response的解析
其实http请求,最终是通过socket来实现tcp协议,最终还是通过IO的输入输出流来完成数据的传输的,其实后面的源码基本上都是围绕上面的SessionOutPutBuffer和SessionInputBuffer来实现的,后面的代码感觉就是流的操作,只不过再解析头信息的时候早了一些处理(不过不重要);其实可以去看看浏览器时怎么发请求的,我httpClient就是模仿;看看总体的流程图:
后面再说说,生成的连接时如何于socket关联起来的
分界线--------------------------------------------------------------------------
解释一下上图:ConnectionRequest是一个只有返回HttpClientConnection的get()方法的接口,继承了Cancellable接口。
BasicHttpClientConnectionManager实现Cancellable接口
接着点进去看看具体再哪里执行,最终调用的收是BasicHttpClientConnectionManager#connection()。
由上面可以看出来最核心的连接过程最终还是通过ConnectionOperator(连接执行器)来完成的,也就是连接管理器HttpClientConnectionManager来控制ConnectionOperator(连接执行器),最终生成连接的,我们接着下面来看HttpClientConnecitonOperator,这个接口就只有一个实现,即DefaultHttpClientConnectionOperator,所以上面ConnectionOperator#connect()执行到了DefaultHttpClientConnectionOperator#connect(),下面我们来分析一下:
public void connect(
final ManagedHttpClientConnection conn,
final HttpHost host,
final InetSocketAddress localAddress,
final int connectTimeout,
final SocketConfig socketConfig,
final HttpContext context) throws IOException {
// 从上下文中获取注册的字段SOCKET_FACTORY_REGISTRY,默认http.socket-factory-registry
final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);
// 根据Scheme(Http,Https等)默认的http,生成不一样的socket的连接工厂
final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
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);
// 生成socket用于通信传输
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);
sock.setSoTimeout(socketConfig.getSoTimeout());
sock.setReuseAddress(socketConfig.isSoReuseAddress());
sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
sock.setKeepAlive(socketConfig.isSoKeepAlive());
if (socketConfig.getRcvBufSize() > 0) {
sock.setReceiveBufferSize(socketConfig.getRcvBufSize());
}
if (socketConfig.getSndBufSize() > 0) {
sock.setSendBufferSize(socketConfig.getSndBufSize());
}
final int linger = socketConfig.getSoLinger();
if (linger >= 0) {
sock.setSoLinger(true, linger);
}
// 真正的连接过程,也就是新建Socket并绑定到HttpClientConnection上的过程
conn.bind(sock);
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);
...........
...........
}
上面代码中ConnectionSocketFactory也只有两个实现:PlainConnectionSocketFactory,SSLConnectionSocketFactory(实现了LayeredConnectionSocketFactory,继承自ConnectionSocketFactory)。
PlainConnectionSocketFactory是用于创建非HTTPS连接的,代码很简单,本质上就是new Socket(); (关于Socket编程,我自己都没研究,空了要研究一下,研究好了再说,总体来说其实就是基于io流的传输,里面源码也涉及到了,后面如果分析到了就简单说一下,socket研究深了感觉很伤脑子。。。。);而SSLConnectionSocketFactory就是在创建Socket前后有很多SSL相关的操作,总之就是各种安全加密相关的,这里不详细说明了。
其实DefaultHttpClientConnectionOperator#connect()本身只做一件事情,就是处理Connection和Socket的关系,将他们绑定起来,这也是Connection管理的一部分,我认为直接集成到Manager管理器中也是可以的,为什么没有这么做,可能是希望功能更加细化,更方便管理和拓展。
大概就这样吧,都是自己学习的分析,可能很多地方分析的有问题,欢迎指点;HttpClient其实现在用的比较少,因为其他的开源的通信软件很多,不过基本上原理都是模仿客户端来发送http请求的,貌似Dubbo也是在此基础上的,因为现在用的Spring的技术栈多点,后面也准备系统的学一下Dubbo。