使用的是阻塞io、进行了tcp复用(connection缓存)。
核心Interceptor工作
其核心是通过拦截器工作的。构建的链进行逐个调用。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> 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, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
缓存问题
响应结果的缓存
内部会缓存httpresponse允许缓存的结果,比如头部携带允许cache,则可以把结果缓存下来。
如果再次请求返回的是304,没有携带内容,则直接使用。
这一块和协议勾搭的比较深,没详细分析。
Connection的缓存
在内部做分发的时候会根据地址找到对应的connection,也会进行缓存,避免多次链接。
其缓冲器会默认允许有5个没有使用的连接,空闲300秒,则会回收。
只要连接都在使用,可以无限的缓存连接。获取不到缓存的连接,就会无限创建。
同步和异步
其进行http调用是通过getResponseWithInterceptorChain这个方法。
同步调用,则直接在提交之后到线程池之后,直接获取结果,进行原地阻塞。
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
// 原地阻塞等待
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
异步调用,则是提交到线程池之后,在线程运行的时候获取到结果,然后调用回调函数,在进行处理。
其使用了核心线程池大小是0,队列无限大的线程池。所以会一直有一个非核心线程在运行。
// 先enqueue 进行队列中。
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
// 在线程池执行的时候去获取结果
protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
返回结果和Http响应关联
通过CallServerInterceptor进行网络交互
正常的网络通信结果是不知道针对哪个请求进行响应的。如何在众多响应字节中找到对应的请求结果。
首先在连接复用的时候是会对connection进行引用计数,没有引用才可以获取。有引用不行,所以不会存在一个连接同时发送多个http请求的问题。
在http请求返回的时候根据http协议,先对header进行解析,以换行符结尾。然后对body进行解析,以header中的content-length进行解析,从而保证获取正确的http响应。
使用demo
同步get
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
public static void main(String[] args) throws IOException {
GetExample example = new GetExample();
String response = example.run("https://raw.github.com/square/okhttp/master/README.md");
System.out.println(response);
}
同步post
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
String bowlingJson(String player1, String player2) {
return "{'winCondition':'HIGH_SCORE',"
+ "'name':'Bowling',"
+ "'round':4,"
+ "'lastSaved':1367702411696,"
+ "'dateStarted':1367702378785,"
+ "'players':["
+ "{'name':'" + player1 + "','history':[10,8,6,7,8],'color':-13388315,'total':39},"
+ "{'name':'" + player2 + "','history':[6,10,5,10,10],'color':-48060,'total':41}"
+ "]}";
}
public static void main(String[] args) throws IOException {
PostExample example = new PostExample();
String json = example.bowlingJson("Jesse", "Jake");
String response = example.post("http://www.roundsapp.com/post", json);
System.out.println(response);
}
异步get
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).enqueue(() ->{
// 此处要重写callback的方法
) {
return response.body().string();
}
}
public static void main(String[] args) throws IOException {
GetExample example = new GetExample();
String response = example.run("https://raw.github.com/square/okhttp/master/README.md");
System.out.println(response);
}
注:
并发调用从连接池获取不到连接的话,会无限创建连接。
public ConnectionPool() {
// 5代表允许的最大空闲的连接,这个的意思是我会有一个数字关联connection是否在使用
// 如果没有使用的数字超过了5个,则会进行回收
// 6代表是对没有使用的连接 6分钟回收,和5没啥关系
this(5, 6, TimeUnit.MINUTES);
}
1351

被折叠的 条评论
为什么被折叠?



