Events
Events allow you to capture metrics on your application’s HTTP calls. Use events to monitor:
事件允许您捕获应用程序HTTP调用的度量。使用事件来监控:
-
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!
应用程序进行HTTP call的大小和频率。如果你发起了很多call,或者你发起的call数据很大,你应该知道这一点!
-
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.
这些调用在底层网络上的性能。如果网络的性能不够,您需要改进网络或者减少网络的使用。
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并覆盖您感兴趣的事件的方法。在没有重定向或重试的成功HTTP调用中,事件序列由该流描述。
Events DiagramHere’s a sample event listener that prints each event with a timestamp.
下面是一个示例事件侦听器,它使用时间戳打印每个事件。
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");
}
...
}
We make a couple calls:
我们发起几次请求
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();
}
And the listener prints the corresponding events:
监听器输出相应的事件:
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.
注意,如何不为第二个call触发connect事件。它重用了第一个请求的连接,从而显著提高了性能。
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.
在前面的示例中,我们使用了一个字段callStartNanos来跟踪每个事件的运行时间。这很方便,但如果多个调用同时执行,则无法正常工作。要适应这种情况,请使用工厂为每个调用创建一个新的EventListener实例。这允许每个侦听器保持特定于调用的状态。
This sample factory creates a unique ID for each call and uses that ID to differentiate calls in log messages.
这个示例工厂为每个调用创建一个惟一的ID,并使用该ID来区分日志消息中的调用。
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:
我们可以使用这个监听器来竞争一对并发的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)比Post(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.
当一个操作失败时,将调用一个失败方法。这是connectFailed(),用于在构建到服务器的连接时发生故障,以及callFailed(),用于HTTP调用永久失败。当失败发生时,startevent可能没有相应的结束事件。
Events DiagramEvents 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.
OkHttp具有弹性,可以从某些连接失败中自动恢复。在这种情况下,connectFailed()事件不是终端事件,后面没有callFailed()。当尝试重试时,事件侦听器将接收相同类型的多个事件。
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.
单个HTTP调用可能需要后续请求来处理身份验证挑战、重定向和HTTP层超时。在这种情况下,可以尝试多个连接、请求和响应。跟踪是单个调用可能触发相同类型的多个事件的另一个原因。
Events DiagramAvailability
Events is available as a public API in OkHttp 3.11. Future releases may introduce new event types; you will need to override the corresponding methods to handle them.
事件可以作为OkHttp 3.11中的公共API使用。未来的版本可能会引入新的事件类型;您需要覆盖相应的方法来处理它们。
网友评论