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请求的调用监控(也就是生命周期)。 使用Events可以展示: .应用Http请求发出的大小和频率, 如果你发出了太多请求, 或者你的请求太大, 你应该了解它。 .底层网络调用的性能。如果性能不足, 你应该提高网络或减少网络使用。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成功请求中, 事件序列执行如下。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我们发几个请求:inetAddressList) { printEvent("dnsEnd"); } ...}复制代码
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 callStart0.010 dnsStart0.017 dnsEnd0.025 connectStart0.117 secureConnectStart0.586 secureConnectEnd0.586 connectEnd0.587 connectionAcquired0.588 requestHeadersStart0.590 requestHeadersEnd0.591 responseHeadersStart0.675 responseHeadersEnd0.676 responseBodyStart0.679 responseBodyEnd0.679 connectionReleased0.680 callEndREQUEST 2 (pooled connection)0.000 callStart0.001 connectionAcquired0.001 requestHeadersStart0.001 requestHeadersEnd0.002 responseHeadersStart0.082 responseHeadersEnd0.082 responseBodyStart0.082 responseBodyEnd0.083 connectionReleased0.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.
注意: 第二次调用没有触发连接事件。 它重用了第一次连接, 从而显著提高了性能。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来跟踪每个事件的已用时间。这很方便,但如果多个调用同时执行,它将无法工作。 为了适应这种情况,请使用Factory为每个Call创建一个新的EventListener实例。 这允许每个监听器保持特定于呼叫的状态。 此示例工厂为每个调用创建唯一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
我们可以使用这个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 callStart0002 https://www.nytimes.com/0002 0.000 callStart0002 0.010 dnsStart0001 0.013 dnsStart0001 0.022 dnsEnd0002 0.019 dnsEnd0001 0.028 connectStart0002 0.025 connectStart0002 0.072 secureConnectStart0001 0.075 secureConnectStart0001 0.386 secureConnectEnd0002 0.390 secureConnectEnd0002 0.400 connectEnd0001 0.403 connectEnd0002 0.401 connectionAcquired0001 0.404 connectionAcquired0001 0.406 requestHeadersStart0002 0.403 requestHeadersStart0001 0.414 requestHeadersEnd0002 0.411 requestHeadersEnd0002 0.412 responseHeadersStart0001 0.415 responseHeadersStart0002 0.474 responseHeadersEnd0002 0.475 responseBodyStart0001 0.554 responseHeadersEnd0001 0.555 responseBodyStart0002 0.554 responseBodyEnd0002 0.554 connectionReleased0002 0.554 callEnd0001 0.624 responseBodyEnd0001 0.624 connectionReleased0001 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.
当一个操作失败了, 失败的方法会被调用。 这是一个在创建服务器连接时的连接错误。 当失败发生时, 可能一个开始事件就不会有对应的结束事件了。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层面的超时。 在这种情况下, 多个连接请求和响应会发生。后序请求是可能会收到多个同类型事件的原因。适用性
事件机制可以在OkHttp 3.11版本公开使用。 未来可能会引入新的事件类型, 你需要重写响应的方法来处理。