OkHttp的性能指标拦截(首包,剩余包时间,resultCode)
最近,部门接到新的任务就是做httpClient,URLConnection,OKhttpClient,webview的网络性能指标进行监控,当然这些指标最准确的方法还是使用jni在底层建链与dns解析,发包等函数进行hook,再进行指标采集。但领导要求我们在java层尝试一下,今天主要记录下,我对OkHttp网络指标采集的调研结果和尝试。
1 OkHttpClient client = new OkHttpClient(); 2 Request request = new Request.Builder().url(url).get().build(); 3 Response response = client.newCall(request).execute();
对于httpClient我们通尝都是替换execute()方法,然后对httpClient的对像进行更改配置自己的拦截功能,查看源码,发现OkHttpClient相对不同的是execute方法并不在OkHttpClient,而是newCall方法中new 了一个call实例。
1 public Call newCall(Request request) { 2 return new Call(this, request); 3 }
//Call构造器中,对OkHttpClient进行了深拷备,所以对newCall之后的OkHttpClient配置都将无法生效,所以我们必须在newCall之前将拦截代码进行塞入
Call(OkHttpClient client, Request originalRequest) {
// Copy the client. Otherwise changes (socket factory, redirect policy,
// etc.) may incorrectly be reflected in the request when it is
// executed.
this.client = client.copyWithDefaults();
this.originalRequest = originalRequest;
}
发现Call类并不是final的,所以决定对其继承,因为 Call的构造器是protected的,所以子类需要在相同的包名下,而上层的拦截于是这样实现的
1 public static Call newCall(OkHttpClient okHttpClient, Request paramRequest) 2 { 3 return new MyCall(okHttpClient, paramRequest, okHttpClient.newCall(paramRequest)); 4 }
MyCall类的中的public,protected方法进行重写(execute(),enqueue(Callback paramCallback),cancel(),isCanceled()),实现使用传入的call进行调用,重写getResponse方法
1 @Override 2 Response getResponse(Request request, boolean forWebSocket) throws IOException { 3 RequestBody body = request.body(); 4 if (body != null) { 5 Request.Builder requestBuilder = request.newBuilder(); 6 7 MediaType contentType = body.contentType(); 8 if (contentType != null) { 9 requestBuilder.header("Content-Type", contentType.toString()); 10 } 11 12 long contentLength = body.contentLength(); 13 if (contentLength != -1) { 14 requestBuilder.header("Content-Length", Long.toString(contentLength)); 15 requestBuilder.removeHeader("Transfer-Encoding"); 16 } else { 17 requestBuilder.header("Transfer-Encoding", "chunked"); 18 requestBuilder.removeHeader("Content-Length"); 19 } 20 21 request = requestBuilder.build(); 22 } 23 24 // Create the initial HTTP engine. Retries and redirects need new engine 25 // for each attempt. 26 engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null, null); 27 28 int followUpCount = 0; 29 while (true) { 30 if (canceled) { 31 engine.releaseConnection(); 32 throw new IOException("Canceled"); 33 } 34 35 try { 36 Timer requestTime = ThreadLocalMetricsRecorder.getInstance().getRequestTimer(); 37 Timer responseTime = ThreadLocalMetricsRecorder.getInstance().getResponseTimer(); 38 requestTime.start(); 39 engine.sendRequest(); 40 requestTime.stop(); 41 responseTime.start(); 42 engine.readResponse(); 43 responseTime.stop(); 44 } catch (RequestException e) { 45 // The attempt to interpret the request failed. Give up. 46 throw e.getCause(); 47 } catch (RouteException e) { 48 // The attempt to connect via a route failed. The request will 49 // not have been sent. 50 HttpEngine retryEngine = engine.recover(e); 51 if (retryEngine != null) { 52 engine = retryEngine; 53 continue; 54 } 55 // Give up; recovery is not possible. 56 throw e.getLastConnectException(); 57 } catch (IOException e) { 58 // An attempt to communicate with a server failed. The request 59 // may have been sent. 60 HttpEngine retryEngine = engine.recover(e, null); 61 if (retryEngine != null) { 62 engine = retryEngine; 63 continue; 64 } 65 66 // Give up; recovery is not possible. 67 throw e; 68 } 69 70 Response response = engine.getResponse(); 71 Request followUp = engine.followUpRequest(); 72 73 if (followUp == null) { 74 if (!forWebSocket) { 75 engine.releaseConnection(); 76 } 77 return response; 78 } 79 80 if (++followUpCount > MAX_FOLLOW_UPS) { 81 throw new ProtocolException("Too many follow-up requests: " + followUpCount); 82 } 83 84 if (!engine.sameConnection(followUp.url())) { 85 engine.releaseConnection(); 86 } 87 88 Connection connection = engine.close(); 89 request = followUp; 90 engine = new HttpEngine(client, request, false, false, forWebSocket, connection, null, null, response); 91 } 92 }
算出首包,剩余包时间和拦截重定向,通过重写enqueue方法,获取清求的错误与response
1 @Override 2 public void enqueue(Callback paramCallback) 3 { 4 // a(); 5 this.e.enqueue(new Callback() { 6 7 @Override 8 public void onResponse(Response response) throws IOException { 9 10 } 11 12 @Override 13 public void onFailure(Request request, IOException e) { 14 15 } 16 }); 17 }