Skip to content

Http Client

paulcleary edited this page Nov 10, 2015 · 2 revisions

Why would I use it?

  • You are writing a Scala or Java application that uses the Apache Http Components
  • You would like to seamlessly propagate the trace context to downstream systems

Many, many people use Apache Http Components in order to make http calls to remote servers. The money-http-client module extends the AspectJ module to make it stupid simple to support tracing for projects that use the Apache Http Components library.

BONUS! - by using the http client module, your application will seamlessly pass the current span to downstream systems! It does this by embedding the span id in a custom HTTP request header.

You can use the Http Client Module with or without AspectJ. If you use AspectJ, you can read up on how to use it in the AspectJ documentation. If you choose to not use AspectJ, you can still leverage the Http Client module and record key metrics, just not all of the metrics can be recorded (which is ok for many)

The Http Client module currently uses version 4.3.5 of the Apache Http Components and Client modules. If your version is different, your results...may...be...different

Adding the dependency

Add a dependency as follows for maven:

    <dependency>
        <groupId>com.comcast.money</groupId>
        <artifactId>money-http-client_2.10</artifactId>
        <version>${money.version}</version>
    </dependency>

And here is a dependency for SBT:

    libraryDependencies += "com.comcast.money" %% "money-http-client" % "${money.version}"

Weaving

Weaving with AspectJ is optional. Reading the information on weaving in the AspectJ for more information

Metrics

The http client module will automatically capture several metrics around the calls to http client. The following metrics are recorded:

httpCallDuration

This is how long it took for the http client to make the http request and get a response back. This is done by timing the httpClient.execute invocations.

httpCallWithBodyDuration


Note: This is not supported unless using AspectJ


This is how long it took for the http client to make the http request, get a response back, and fully retrieve the content of the request body. this metric has a lot more acrobatics to get its work done:

  1. Start a timer when the httpClient.execute method is invoked
  2. Stop the timer when the EntityUtils method is invoked to retrieve the contents of the body from the request.

processResponseDuration


Note: This is not supported unless using AspectJ


This is the time between when the body was retrieved using EntityUtils and the end of the trace.

statusCode

This is the status code returned on the Http Response; for example 200, 204, 304, 400, or 500

Configuration

Not everyone loves the names of our metrics, so we give you the ability to change the names that are emitted if you want something else.

The following shows the default settings. Feel free to change the names to something else if you want.

money {
  http-client {
    metric-names {
      http-call-duration = "httpCallDuration"
      http-call-with-body-duration = "httpCallWithBodyDuration"
      http-process-response-duration = "processResponseDuration"
      http-status-code = "statusCode"
    }
  }
}

Non-AspectJ Usage

To use the Http-Client module without AspectJ, you need to use the TraceFriendlyHttpClient class and wrap whatever HttpClient instance you are using. TraceFriendlyHttpClient extends HttpClient, so you should be able to use everything normally.

import com.comcast.money.http.client.TraceFriendlyHttpClient;

import static com.comcast.money.japi.JMoney.*;

public class MyService {
  
  private HttpClient httpClient;

  public MyService(HttpClient httpClient) {
    this.httpClient = new TraceFriendlyHttpClient(httpClient);
  }

  public String getSomething() {

    try (TraceSpan span = newSpan("get-stuff")) {
      HttpGet httpGet = new HttpGet(url);
      HttpResponse response = httpClient.execute(httpGet);
      return EntityUtils.toString(response.getEntity());
    }
  }
}

In the above example, we have a simple service class that wraps an HttpClient inside of TraceFriendlyHttpClient. Now, it this setup, we will send the X-MoneyTrace header on every request; we will also record the response code and http call duration.

We also start a new span. We strongly recommend when using money that all external service calls have their own span. If you do not make a call in its own span, there is no way to override the name of the Notes that are recorded.

AspectJ Usage

Yea, just use the Aspectj money module's @Traced annotation or traced functions in Scala money-core or money-concurrent; as long as you are using the http client like the examples below, you are good!

Well almost, you should follow these guidelines:

  • Use EntityUtils to retrieve the response body. This is the class that is instrumented by the trace aspect
  • Use either the httpClient.execute method that returns an HttpResponse, or, the variation that takes a ResponseHandler, both variations will be picked up
  • It is possible that you are following a usage pattern that is not currently picked up by the aspect. If so, let us know, easy enough to add!
  • Capture the entire call and processing of the response in a single trace.
  • Capture each external service call in its own method, and trace each one separately
    @Traced("ExternalMarketPlace.GetAccountInfo")
    private AccountInfo load(String accountId) {
    
        HttpGet httpGet = new HttpGet(url);
        
        // Execute the request, this will be picked up by the http trace aspect
        HttpResponse httpResponse = httpClient.execute(httpGet);
        HttpEntity responseEntity = httpResponse.getEntity();
        
        // Read the response body, this also will be picked up by the http trace aspect
        // EntityUtils.toString is one of the methods picked up by the aspect
        String body = EntityUtils.toString(responseEntity);
        
        // Parsing the body should also be in here, as it is part of the service call
        return json.parse(body); 
    }

You can also use the http execute method variation that takes a ResponseHandler as an argument. The http-client money module will still capture metrics accordingly. The following example is overly contrived and simple, but it illustrates the use case.

public class AccountInfoResponseHandler implements ResponseHandler<AccountInfo> {

    @Override
    public AccountInfo handleResponse(HttpResponse response) {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            // EntityUtils is picked up here
            return json.parse(EntityUtils.toString(response));
        } else {
            throw new ServiceCallFailedException(statusCode);
        }
    }
}

... then somewhere else ...

    @Traced("ExternalMarketPlace.GetAccountInfo")
    private AccountInfo load(String accountId) {
    
        HttpGet httpGet = new HttpGet(url);
        
        AccountInfoResponseHandler handler = new AccountInfoResponseHandler();
        
        // Execute the request, this will be picked up by the http trace aspect
        // calls into the handler are also picked up
        return httpClient.execute(httpGet, handler);        
    }