Skip to content

事件

事件允许你捕获应用程序的HTTP调用的度量信息。使用事件监视:

  • 你的应用程序进行的HTTP调用的大小和频率。 如果你调用太多,或者你调用太大,你应该知道这一点!
  • 这些调用在基础网络上的性能。 如果网络的性能不够好,你要么需要改进网络,要么减少网络使用量。

事件监听器(EventListener)

你可能感兴趣的事件的子类EventListener和重写方法。 在没有重定向或重试的成功HTTP调用中,此流程描述了事件序列。

Events Diagram

这里有一个示例事件listener,它打印每个带有时间戳的事件。

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");
  }

  ...
}

我们进行几次调用:

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();
}

监听程序打印相应的事件:

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

请注意,第二次调用没有触发connect事件。 它重用了第一个请求中的连接,从而显著提高了性能。

EventListener.Factory

在前面的示例中,我们使用了一个字段 “callStartNanos” 来跟踪每个事件的经过时间。 这很方便,但如果同时执行多个调用,则不会起作用。 要适应这一点,请使用 “工厂” 为每个 “调用” 创建一个新的 “eventlistener” 实例。 这允许每个监听器保持特定于呼叫的状态。

sample factory 为每个调用创建一个唯一的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");
  }

  ...
}

我们可以使用此监听程序来竞用一对并发的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() {
  ...
});

在家庭WiFi上运行这场竞用显示,《Times》 (“0002”) 比《邮报》 (“Post”) 稍早完成:

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

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;
      }
    }
  };

  ...
}

失败事件

当操作失败时,将调用failure方法。 如果与服务器建立连接失败,则为connectFailed(),如果HTTP调用永久失败,则为callFailed()。 当发生故障时,start 事件可能不会有相应的 end 事件。

Events Diagram

重试和Follow-Ups事件

OkHttp具有弹性,可以从某些连接故障中自动恢复。 在这种情况下,connectFailed()事件不是终端,并且后面不跟allFailed()。 尝试重试时,事件侦听器将接收多个相同类型的事件。

单个HTTP调用可能需要发出后续Follow-up请求来处理身份验证挑战、重定向和HTTP层超时。 在这种情况下,可能尝试进行多次连接、请求和响应。 Follow-ups是单个呼叫可能触发相同类型的多个事件的另一个原因。

Events Diagram

可用性

在OkHttp 3.11中,Events作为公共API提供。 将来的版本可能会引入新的事件类型; 你将需要覆盖相应的方法来处理它们。