Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

End-to-End Network Performance Monitoring with OkHttp EventListeners

Tech Sep 7 1

Implementation Strategy

To achieve granular visibility into network operations, developers can leverage the EventListener API provided by OkHttp. This mechanism allows for the interception of specific events during an HTTP request's lifecycle, enabling the collection of metrics such as DNS resolution time, TCP handshake duration, TLS negotiation time, and server processing latency.

Configuration

The OkHttpClient.Builder exposes methods to attach a listener. While a single listener instance can be reused, using a factory is recommended for advanced scenarios where call-specific data needs to be tracked.


OkHttpClient client = new OkHttpClient.Builder()
    .eventListenerFactory(new EventListener.Factory() {
        @Override public EventListener create(Call call) {
            return new NetworkTraceListener();
        }
    })
    .build();

Request Lifecycle Analysis

A standard network call traverses several distinct states. To accurately measure performance, timestamps must be captured at the following transition points:

  1. callStart: The moment the request is initiated.
  2. DNS Phase: The interval between dnsStart and dnsEnd, representing the time required to resolve the domain name.
  3. Connection Phase: Spanning from connectStart to connectEnd. This includes the TCP handshake and, if applicable, the TLS handshake.
  4. Request Submission: The duration taken to send headers and the request body.
  5. Response Phase: The time spent receiving response headers and the body.
  6. callEnd: The termination of the request.

Custom Listener Implementation

The following code demonstrates a custom listener that calculates the duration for each critical phase. It utilizes nanosecond precision to ensure high accuracy.


public final class NetworkTraceListener extends EventListener {
    private long startTimestamp;
    private long dnsStartTimestamp;
    private long connectStartTimestamp;
    private long secureConnectStartTimestamp;
    private long requestEndTimestamp;
    private long responseHeaderStartTimestamp;

    @Override
    public void callStart(Call call) {
        startTimestamp = System.nanoTime();
    }

    @Override
    public void dnsStart(Call call, String domainName) {
        dnsStartTimestamp = System.nanoTime();
    }

    @Override
    public void dnsEnd(Call call, String domainName, List<InetAddress> addressList) {
        long dnsDuration = System.nanoTime() - dnsStartTimestamp;
        // Log or store DNS duration
    }

    @Override
    public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {
        connectStartTimestamp = System.nanoTime();
    }

    @Override
    public void secureConnectStart(Call call) {
        secureConnectStartTimestamp = System.nanoTime();
    }

    @Override
    public void secureConnectEnd(Call call, Handshake handshake) {
        long tlsDuration = System.nanoTime() - secureConnectStartTimestamp;
        // Log TLS duration
    }

    @Override
    public void connectEnd(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) {
        long totalConnectTime = System.nanoTime() - connectStartTimestamp;
        // Log total connection duration
    }

    @Override
    public void requestHeadersEnd(Call call, Request request) {
        // Headers sent
    }

    @Override
    public void requestBodyEnd(Call call, long byteCount) {
        requestEndTimestamp = System.nanoTime();
    }

    @Override
    public void responseHeadersStart(Call call) {
        responseHeaderStartTimestamp = System.nanoTime();
        long serverProcessingTime = responseHeaderStartTimestamp - requestEndTimestamp;
        // Log server processing time
    }

    @Override
    public void responseBodyEnd(Call call, long byteCount) {
        long totalDuration = System.nanoTime() - startTimestamp;
        // Log total request time
    }
}

Calculated Metrics

Based on the captured timestamps, the following key performance indicators (KPIs) can be derived:

  • Total Latency: callEnd - callStart
  • Waiting Duration: dnsStart - callStart (Time spent in the queue before network activity begins).
  • DNS Lookup Time: dnsEnd - dnsStart
  • TCP Handshake: secureConnectStart - connectStart (if HTTPS is used).
  • TLS Handshake: secureConnectEnd - secureConnectStart
  • Network Download Time: responseBodyEnd - responseHeadersStart

Supplementary Telemetry

Beyond timing, effective monitoring requires contextual data to diagnose root causes. The following parameters should be aggregated alongside timing metrics:

  • Resource Identifiers: Request URL, HTTP Method (GET, POST), and HTTP Status Code.
  • Connection Details: Proxy usage, target IP address, and resolved DNS list.
  • Traffic Analysis: Total upstream and downstream bytes (including retries and redirects).
  • Environment: Network type (WiFi, 4G, 5G) and security protocol (TLS version).
  • Error Context: Stack traces for exceptions such as SocketTimeoutException or UnknownHostException.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.