okhttp 服务器性能,OkHttp网络请求分析

8f34c928cf13

http请求.gif

http请求如上所示:一个请求对应一个响应,一来一回。是一个上层协议,隐藏了许多细节。下面大概流程为:

地址解析 : 解析出协议,端口,主机IP

封装HTTP请求数据包

封装成TCP包,建立TCP连接(tcp三次握手)

客户机发送请求命令

服务器响应

服务器关闭TCP连接

头信息加入了这行代码 Connection:keep-alive TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

注意:http协议中基于tcp/ip

OkHttp对以上步骤的实现解析

8f34c928cf13

okhttp2.gif

RetryAndFollowUpInterceptor创建StreamAllocation对象,处理http的重定向及出错重试。对后续Interceptor的执行的影响为修改Request并创建StreamAllocation对象。

BridgeInterceptor补全缺失的一些http header。对后续Interceptor的执行的影响主要为修改了Request。Connection:keep-alive这里被默认添加

CacheInterceptor处理http缓存。对后续Interceptor的执行的影响为,若缓存中有所需请求的响应,则后续Interceptor不再执行。

ConnectInterceptor借助于前面分配的StreamAllocation对象建立与服务器之间的连接,并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响为,创建了HttpStream和connection。

CallServerInterceptor作为Interceptor链中的最后一个Interceptor,用于处理IO,与服务器进行数据交换。

https://www.jianshu.com/p/5c98999bc34f

使用方法

//1.初始化OKHttp

OkHttpClient okHttpClient = new OkHttpClient()

.newBuilder()

.build();

//2.构建request

final Request request = new Request.Builder()

.get()

.url(HttpUrl.parse("http://www.baidu.com/"))

.build();

//3.发送请求(这里并没有执行,只是构建了`RealCall`对象)

Call call = okHttpClient.newCall(request);

//4.接收响应(请求执行,并得到响应结果)

Response execute = call.execute();

//5.输出

System.out.println(execute.body().string());

分析

着重分析步骤3与4,步骤3是为了返回RealCall对象,步骤4则执行的是RealCall.execute()

RealCall.execute

@Override public Response execute() throws IOException {

synchronized (this) {

if (executed) throw new IllegalStateException("Already Executed");

executed = true;

}

captureCallStackTrace();

try {

client.dispatcher().executed(this);

Response result = getResponseWithInterceptorChain();

if (result == null) throw new IOException("Canceled");

return result;

} finally {

client.dispatcher().finished(this);

}

}

RealCall.getResponseWithInterceptorChain

Response getResponseWithInterceptorChain() throws IOException {

// Build a full stack of interceptors.

List interceptors = new ArrayList<>();

interceptors.addAll(client.interceptors());

interceptors.add(retryAndFollowUpInterceptor);

interceptors.add(new BridgeInterceptor(client.cookieJar()));

interceptors.add(new CacheInterceptor(client.internalCache()));

interceptors.add(new ConnectInterceptor(client));

if (!forWebSocket) {

interceptors.addAll(client.networkInterceptors());

}

interceptors.add(new CallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(

interceptors, null, null, null, 0, originalRequest);

return chain.proceed(originalRequest);

}

.okhttp 地址解析

//解析得到host --- BridgeInterceptor

requestBuilder.header("Host", hostHeader(userRequest.url(), false));

//解析得到主机ip --- StreamAllocation#findConnection

1. StreamAllocation#newStream()

RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,

writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);

2.StreamAllocation#findHealthyConnection()

RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,

pingIntervalMillis, connectionRetryEnabled);

3.StreamAllocation#findConnection()

if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {

newRouteSelection = true;

routeSelection = routeSelector.next();

}

4.RouteSelector#next()

public Selection next() throws IOException {

...

Proxy proxy = nextProxy();

...

return new Selection(routes);

}

5.RouteSelector#nextProxy()

private Proxy nextProxy() throws IOException {

if (!hasNextProxy()) {

throw new SocketException("No route to " + address.url().host()

+ "; exhausted proxy configurations: " + proxies);

}

Proxy result = proxies.get(nextProxyIndex++);

resetNextInetSocketAddress(result);

return result;

}

6.RouteSelector#resetNextInetSocketAddress()

socketHost = getHostString(proxySocketAddress);

与服务器tcp连接

ConnectInterceptor#intercept

HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);

findHealthyConnection -> findConnection -> RealConnection#connect -> connectSocket ->

private void connectSocket(int connectTimeout, int readTimeout, Call call,

EventListener eventListener) throws IOException {

Proxy proxy = route.proxy();

Address address = route.address();

rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP

? address.socketFactory().createSocket()

: new Socket(proxy);

eventListener.connectStart(call, route.socketAddress(), proxy);

rawSocket.setSoTimeout(readTimeout);

try {

Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);

} catch (ConnectException e) {

ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());

ce.initCause(e);

throw ce;

}

设置okhttp的超时,这里就可以看得很明白了,其实是设置socket的连接超时与读写超时

.connectTimeout(30*1000, TimeUnit.MILLISECONDS) .readTimeout(30*1000,TimeUnit.MILLISECONDS) .writeTimeout(30*1000,TimeUnit.MILLISECONDS)

发送请求命令

CallServerInterceptor#intercept 这是okhttp中的最后一个入拦截器,主要作用是 与服务器进行数据交换

httpCodec.writeRequestHeaders(request);

Http1Codec#writeRequestHeaders

public void writeRequest(Headers headers, String requestLine) throws IOException {

if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);

sink.writeUtf8(requestLine).writeUtf8("\r\n");

for (int i = 0, size = headers.size(); i < size; i++) {

sink.writeUtf8(headers.name(i))

.writeUtf8(": ")

.writeUtf8(headers.value(i))

.writeUtf8("\r\n");

}

sink.writeUtf8("\r\n");

state = STATE_OPEN_REQUEST_BODY;

}

这里的IO操作都是OKIO实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值