深入剖析OkHttp系列(五) 来自官方的事件机制
Event
Events allow you to capture metrics on your application’s HTTP calls. Use events to monitor: The size and frequency of the HTTP calls your application makes. If you’re making too many calls, or your calls are too large, you should know about it! The performance of these calls on the underlying network. If the network’s performance isn’t sufficient, you need to either improve the network or use less of it.
Events允許你捕獲你的應用Http請求的調用監(jiān)控(也就是生命周期)。 使用Events可以展示:.應用Http請求發(fā)出的大小和頻率, 如果你發(fā)出了太多請求, 或者你的請求太大, 你應該了解它。
.底層網絡調用的性能。如果性能不足, 你應該提高網絡或減少網絡使用。
EventListener
Subclass EventListener and override methods for the events you are interested in. In a successful HTTP call with no redirects or retries the sequence of events is described by this flow.
繼承EventListener, 重寫你感興趣的events方法。 在一次沒有重定向 或 重試的Http成功請求中, 事件序列執(zhí)行如下。Here’s a sample event listener that prints each event with a timestamp.
這是一個打印每個事件的簡單的eventListener。 class PrintingEventListener extends EventListener {private long callStartNanos;private void printEvent(String name) {long nowNanos = System.nanoTime();if (name.equals("callStart")) {callStartNanos = nowNanos;}long elapsedNanos = nowNanos - callStartNanos;System.out.printf("%.3f %s%n", elapsedNanos / 1000000000d, name);}@Override public void callStart(Call call) {printEvent("callStart");}@Override public void callEnd(Call call) {printEvent("callEnd");}@Override public void dnsStart(Call call, String domainName) {printEvent("dnsStart");}@Override public void dnsEnd(Call call, String domainName, List<InetAddress> inetAddressList) {printEvent("dnsEnd");}... } 復制代碼 我們發(fā)幾個請求: Request request = new Request.Builder().url("https://publicobject.com/helloworld.txt").build();System.out.println("REQUEST 1 (new connection)"); try (Response response = client.newCall(request).execute()) {// Consume and discard the response body.response.body().source().readByteString(); }System.out.println("REQUEST 2 (pooled connection)"); try (Response response = client.newCall(request).execute()) {// Consume and discard the response body.response.body().source().readByteString(); } 復制代碼 EventListener會打印相應的事件: REQUEST 1 (new connection) 0.000 callStart 0.010 dnsStart 0.017 dnsEnd 0.025 connectStart 0.117 secureConnectStart 0.586 secureConnectEnd 0.586 connectEnd 0.587 connectionAcquired 0.588 requestHeadersStart 0.590 requestHeadersEnd 0.591 responseHeadersStart 0.675 responseHeadersEnd 0.676 responseBodyStart 0.679 responseBodyEnd 0.679 connectionReleased 0.680 callEnd REQUEST 2 (pooled connection) 0.000 callStart 0.001 connectionAcquired 0.001 requestHeadersStart 0.001 requestHeadersEnd 0.002 responseHeadersStart 0.082 responseHeadersEnd 0.082 responseBodyStart 0.082 responseBodyEnd 0.083 connectionReleased 0.083 callEnd 復制代碼Notice how no connect events are fired for the second call. It reused the connection from the first request for dramatically better performance.
注意: 第二次調用沒有觸發(fā)連接事件。 它重用了第一次連接, 從而顯著提高了性能。EventListener。Factory
In the preceding example we used a field, callStartNanos, to track the elapsed time of each event. This is handy, but it won’t work if multiple calls are executing concurrently. To accommodate this, use a Factory to create a new EventListener instance for each Call. This allows each listener to keep call-specific state. This sample factory creates a unique ID for each call and uses that ID to differentiate calls in log messages.
在前面的示例中,我們使用了一個字段callStartNanos來跟蹤每個事件的已用時間。這很方便,但如果多個調用同時執(zhí)行,它將無法工作。 為了適應這種情況,請使用Factory為每個Call創(chuàng)建一個新的EventListener實例。 這允許每個監(jiān)聽器保持特定于呼叫的狀態(tài)。 此示例工廠為每個調用創(chuàng)建唯一ID,并使用該ID區(qū)分日志消息中的調用。 class PrintingEventListener extends EventListener {public static final Factory FACTORY = new Factory() {final AtomicLong nextCallId = new AtomicLong(1L);@Override public EventListener create(Call call) {long callId = nextCallId.getAndIncrement();System.out.printf("%04d %s%n", callId, call.request().url());return new PrintingEventListener(callId, System.nanoTime());}};final long callId;final long callStartNanos;public PrintingEventListener(long callId, long callStartNanos) {this.callId = callId;this.callStartNanos = callStartNanos;}private void printEvent(String name) {long elapsedNanos = System.nanoTime() - callStartNanos;System.out.printf("%04d %.3f %s%n", callId, elapsedNanos / 1000000000d, name);}@Override public void callStart(Call call) {printEvent("callStart");}@Override public void callEnd(Call call) {printEvent("callEnd");}... } 復制代碼We can use this listener to race a pair of concurrent HTTP requests
我們可以使用這個listener來同時追蹤一對HTTP請求: Request washingtonPostRequest = new Request.Builder().url("https://www.washingtonpost.com/").build(); client.newCall(washingtonPostRequest).enqueue(new Callback() {... });Request newYorkTimesRequest = new Request.Builder().url("https://www.nytimes.com/").build(); client.newCall(newYorkTimesRequest).enqueue(new Callback() {... }); 復制代碼Running this race over home WiFi shows the Times (0002) completes just slightly sooner than the Post (0001):
在WiFi上運行這次追蹤顯示, 0002的完成時間只比0001快一點。 0001 https://www.washingtonpost.com/ 0001 0.000 callStart 0002 https://www.nytimes.com/ 0002 0.000 callStart 0002 0.010 dnsStart 0001 0.013 dnsStart 0001 0.022 dnsEnd 0002 0.019 dnsEnd 0001 0.028 connectStart 0002 0.025 connectStart 0002 0.072 secureConnectStart 0001 0.075 secureConnectStart 0001 0.386 secureConnectEnd 0002 0.390 secureConnectEnd 0002 0.400 connectEnd 0001 0.403 connectEnd 0002 0.401 connectionAcquired 0001 0.404 connectionAcquired 0001 0.406 requestHeadersStart 0002 0.403 requestHeadersStart 0001 0.414 requestHeadersEnd 0002 0.411 requestHeadersEnd 0002 0.412 responseHeadersStart 0001 0.415 responseHeadersStart 0002 0.474 responseHeadersEnd 0002 0.475 responseBodyStart 0001 0.554 responseHeadersEnd 0001 0.555 responseBodyStart 0002 0.554 responseBodyEnd 0002 0.554 connectionReleased 0002 0.554 callEnd 0001 0.624 responseBodyEnd 0001 0.624 connectionReleased 0001 0.624 callEnd 復制代碼The EventListener.Factory also makes it possible to limit metrics to a subset of calls. This one captures metrics on a random 10%:
EventListener.Factory還可以限制一部分請求的指標。 這個隨機捕獲10%的指標: class MetricsEventListener extends EventListener {private static final Factory FACTORY = new Factory() {@Override public EventListener create(Call call) {if (Math.random() < 0.10) {return new MetricsEventListener(call);} else {return EventListener.NONE;}}};... } 復制代碼Events with Failures(有失敗的事件)
When an operation fails, a failure method is called. This is connectFailed() for failures while building a connection to the server, and callFailed() when the HTTP call fails permanently. When a failure happens it is possible that a start event won’t have a corresponding end event.
當一個操作失敗了, 失敗的方法會被調用。 這是一個在創(chuàng)建服務器連接時的連接錯誤。 當失敗發(fā)生時, 可能一個開始事件就不會有對應的結束事件了。Events with Retries and Follow-Ups(帶重試的事件和跟進)
OkHttp is resilient and can automatically recover from some connectivity failures. In this case, the connectFailed() event is not terminal and not followed by callFailed(). Event listeners will receive multiple events of the same type when retries are attempted. A single HTTP call may require follow-up requests to be made to handle authentication challenges, redirects, and HTTP-layer timeouts. In such cases multiple connections, requests, and responses may be attempted. Follow-ups are another reason a single call may trigger multiple events of the same type.
OkHttp具有彈性, 可以自動從連接故障中恢復。 這種情況下, connectFailed()不是終點, 且后面沒有callFailed()。 在嘗試重試時, EventListener將會接收多個同類型的事件。單個請求調用可能需要后序的請求來詢問身份驗證, 重定向, 和Http層面的超時。 在這種情況下, 多個連接請求和響應會發(fā)生。后序請求是可能會收到多個同類型事件的原因。
適用性
事件機制可以在OkHttp 3.11版本公開使用。 未來可能會引入新的事件類型, 你需要重寫響應的方法來處理。總結
以上是生活随笔為你收集整理的深入剖析OkHttp系列(五) 来自官方的事件机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TypeScript基础入门 - 函数
- 下一篇: [雪峰磁针石博客]2018最佳12个开源