文章目录
1. 前言
总的来说:该拦截器的作用是给当前的请求(Call
)分配一个Exchange
,在后面一个拦截器中,使用这个Exchange
进行I/O操作。(Exchange
可以看做一个I/O的窗口),该拦截器为请求准备好了连接到下一个服务器(可能是源服务器或代理)的I/O窗口
其会利用
ExchangeFinder
来获取一个运行在可用连接上的ExchangeCodec
。然后由此ExchangeCodec
来构建一个exchange
详细的说:该类涉及到了I/O,连接,路由,代理。
内容很复杂,先来看一下I/O(Exchange
),连接(Connection
)和路由(Route
)的关系
- I/O建立在连接上
- 连接建立在路由上
- 路由建立在代理上
2. I/O
I/O指的是从Socket
管道中读取或者写入数据。
I/O主要包括三个类,分别是Exchange
,ExchangeCodec
,ExchangeFinder
。他们的关系是:Exchange
建立在由ExchangeFinder
寻找的ExchangeCodec
上
2.1 Exchange
该类是对ExchangeCodec
的封装,除了ExchangeCodec
具有的功能外,还加入了:
- 事件回调
- 记录读写发生错误的次数
的功能。站在请求/响应的角度说,这个类的功能就是对请求和响应进行I/O读写。(但是读者需要知道,真正进行读写的不是他,而是ExchangeCodec
)
2.2 ExchangeCodec
此类的作用是负责请求和响应的I/O流读写操作。对于Http1
和Http2
两个不同的版本有不同的读写操作。其底层采用okio
在Socket
进行读写操作。
配置好Socket
源后,该类便可以从中读取数据或者写入数据
2.3 ExchangeFinder
该类的作用是:为Exchange
寻找一个运行在可用连接上的ExchangeCodec
3. 连接(Connection)
连接建立在路由上,一个连接包含了:
- 路由信息
- 一个
Socket
管道 - 包含HTTPS的TSL握手信息的
Handshake
- 使用的协议
3.1 可用连接
当需要为ExchangeCodec
寻找一个可用连接时,其首先会检查请求已有的连接是否可用,如果不可用,会尝试从ConnectionPool
中获取一个合适的连接,如果以上都不行,则会新建一个连接。
ConnectionPool
内部使用一个线程安全的队列来保存Connection
。每次新建连接时都会将新建的连接放入ConnectionPool
中,当连接不可以被任何ExchangeCodec
使用时,将其移出ConnectionPool
,所以ConnectionPool
中维护的是可再次使用的连接
-
检查当前请求(
Call
)是否已经有了连接,如果有,检查已有的连接是否可用,如果已有连接:- 存在且没有被回收
- 连接中路由的地址和端口没有被修改过
则已有连接可用,否则触发已有连接被回收事件
需要注意的是,一个全新的
Call
是没有连接的,那么为什么还要检查是否有连接呢?
因为这个Call
可能不是全新的,比如发生重定向的Call
-
从
ConnectionPool
中获取,如果获取的某个连接:- 满足(1)的要求
- 承载的请求没有达到最大值
- 其中的路由符合请求的要求
则连接可以用于这个请求,否则不可用。如果可用则会回调
connectionAcquired(Call, Connection)
事件 -
重新创建一个连接
- 首先会找到可用的路由,因为连接是创建在路由上的
- 然后配置这个连接的超时,事件回调等信息
- 将连接与请求建立联系
- 将新建的连接加入到
ConnectionPool
中 - 将连接返回
4. 路由
路由包含了:地址,代理和InetSocketAddress
4.1 可用路由
当需要新建一个连接时,会选择一个可用的路由来新建连接。选择策略如下:
- 首先会检查请求
Call
已有连接的路由是否可用 - 然后会使用
routeSelection
来获取一个可用路由 - 最后会使用
RouteSelector
来生成一个routeSelection
,使用routeSelection
来获取可用路由。
在RouteSelector
中创建了可以使用的路由,然后将这些路由交给routeSelection
,以后获取路由时直接访问RouteSelection
即可
5.代理
代理有3种类型,分别是:
Direct
:没有代理HTTP
:HTTP代理Socket
:Socket代理
代理可以在配置OKHTTP Client
时配置。