Skip to content
bahirsch edited this page Sep 27, 2017 · 36 revisions

Intro

Foundation Communication supports a simple API that wraps standard HTTP client interactions. The idea behind the API is to supply an easy-to-use modern API that will enable working with multiple implementations. These can vary as long as they comply with the interfaces. Our current implementation are for latest Apache Client and Jetty Client.

Following is an explanation on how to use and configure the HTTP Client instance built by Foundation Infra.

API

The Main building blocks are:

  • HttpClient - The basic interface. This is a thread safe object that can and should be re-used across your application.
  • HttpClientFactory - The factory used to build the HTTP Client. Each implementation library will have it's own specific Factory class.
  • HttpRequest - An immutable object that holds all request related data. 
  • HttpResponse - An object holding all the relevant response data.

The factory API has many overloads that enable you to set default load-balancing on/off flag (this affects you if you call the execute method), inject configuration other than that used by configuration-lib library and change the strategy type (by default ROUND_ROBIN will be used).

The client can be used with full uri in case of external LB with known VIP or just access to a static resource on the WAN. For this, call the executeDirect method or execute if in the factory you instructed the library to build a non LB client. Calling executeWithLoadBalancer will ensure full usage of client side LB.

Note: **For verifying you are closing and releasing connections back to the pool, if autoCloseable is set to false, you must either consume the entity payload (getResponseAsString, or getResponse) OR call the 'close' API on the response object.

If autoCloseable is true - it will sliently consumer the response content and cache it in the wrapping response object. If you need streaming you must disab;e the auto-close behavior and call close on the response manually in a finally block. **

You should acquire an instance by using the following code. Notice the usage of request uri instead of url:

HttpClient clientTest = ApacheHttpClientFactory.createHttpClient("muku");

        HttpRequest request = HttpRequest.newBuilder()
                .httpMethod(HttpMethod.GET)
                .contentType("text/html")
                .uri("/my/path/to/resource")
                .header("H1","header 1 value 1")
                .header("H1","header 1 value 2")
                .header("H2","header 2 value")
                .queryParams("hhid","1223479")
                //.lbKey("") - optionally add a key to use with sticky servers
                .build();

        HttpResponse response = clientTest.executeWithLoadBalancer(request);

        LOGGER.info(response.getResponseAsString());

You can also run an async request:

private static class MyResponseHandler implements ResponseCallback<ApacheHttpResponse>{
        @Override
        public void completed(ApacheHttpResponse response) {
            String responseAsString = response.getResponseAsString();
            LOGGER.info(responseAsString );        }

        @Override
        public void failed(Throwable e) {
            LOGGER.error(e);        }

        @Override
        public void cancelled() {
            //not implemented        }
    }
        HttpClient clientTest = ApacheHttpClientFactory.createHttpClient("clientTest", LoadBalancerStrategy.STRATEGY_TYPE.FAIL_OVER);
        HttpRequest request = HttpRequest.newBuilder()
                .httpMethod(HttpMethod.GET)
                .uri("http://www.google.com")
                .build();


        clientTest.executeWithLoadBalancer(request, new MyResponseHandler());

Configuration

The full set of parameters that you can use in the config.properties is listed here:

Key Description
<apiName>.<index>.host The host name of the server being invoked.
<apiName>.<index>.port The port the server is listening on.
<apiName>.http.connectTimeout The time (in millis) The time to wait before timeouting an attempt to establish a new connection Default value is: 5000.
<apiName>.http.idleTimeout The timeout (in millis) for idle connections. Default value is: 180000.
<apiName>.http.maxConnectionsPerAddress The maximum connections kept open per address (address is a server host:port pair). Default value is: 1000.
<apiName>.http.maxQueueSizePerAddress The maximum queue size of pending requests when all connections in the pool are currently used. Default value is: 1000.
<apiName>.http.readTimeout The time (in millis) to wait before request is time out in the client. Default value is: 5000.
<apiName>.http.monitoringBaseUri A list of base Uri's. This list will be matched against the actual uri being served. If a match is found the base Uri will be used as the ApiName in monitoring of the server connections. It is recommended to not use any RegEx in this context and provide uri prefixes only - you should define the array like this:
<apiName>.http.monitoringBaseUri.1=/households
<apiName>.http.monitoringBaseUri.2=/devices
<apiName>.http.monitoringBaseUri.3=/whatever
.
.
.

See the below section for more info

<apiName>.http.numberOfAttempts The number of times to try and call a server before it will be passivated. Each server will be called several times in case of a failure to be sure that it failed for something that was not random. Default value is: 3.
<apiName>.http.retryDelay The time (in millis) to wait between retries. In order to make sure that the server is not temporarily unstable or maybe failed due to a random reason we wait for X milliseconds and try again. It is advised to set a small number here (no more than 1000-5000 milliseconds). Default value is: 500.
<apiName>.http.waitingTime The time (in millis) to wait after a server was passivated before trying to activate it again. This parameter is very important. It is advised to set it for a long period of time. If a server passivated it means that it timed out for X times. This means it is something fatal. It is advised to set this value for at least 60000 milliseconds (1 minute). Default value is: 60000.
<apiName>.http.exposeStatisticsToMonitor When set to true client requests will be monitored. This key is used as is without the config prefix prefix. Default value is: true.
<apiName>.http.autoCloseable Set to true if you want the client to close automatically and release the connection even before consuming the content. Note: you may need to disable this and call close directly if you want to work with the inputstream using streaming style.Default value is: true.
<apiName>.http.autoEncodeUri Set to false if you want the client to not encode URI's automatically. It is the calling API responsability to pass in a fully encoded URI - including query params if such exist.Default value is: true.
<apiName>.http.serviceDirectory.isEnabled Set to true if you want the client to work with a Service Directory. If you have a server that meets the API published by the sd-api lib, you can enable it here. You can set host and port using Configuration, see service.sd.host and service.sd.port below. More info on sd-api.Default value is: false.
<apiName>.http.serviceDirectory.serviceName Set the service name for the service lookup. This value is set by the applicative server you want to access and should have the same value.
<apiName>.http.staleConnectionCheckEnabled Set to true if you want Client connections to be tested before re-used from the pool. Currently supported only in Apache client.Note: can have a performance hit of up to 30 milliseconds per request.Default value is: false.
<apiName>.http.followRedirects Set to true if you want the client to follow redirects automatically.Default value is: false.
<apiName>.http.keyStorePath For SSL support. Path to keystore file.
<apiName>.http.keyStorePassword For SSL support. Keystore file password.
<apiName>.http.trustStorePath For SSL support. Path to trust-store file.
<apiName>.http.trustStorePassword For SSL support. Trust-store file password.
<apiName>.http.enforceTLS For SSL support. Set to true if an exception should be thrown if the client fails to support SSL.
Default value is: false
service.sd.host Set the host for a service directory server that implements the sd-api
service.sd.port Set the port for a service directory server that implements the sd-api
Usage example for HTTP Client:
<Parameter name="service.myHttpClient" base="service.http.client.base" description="my client description" type="STRUCTURE">
    <DefaultValue>
        <StructureValue>
            <StructureMemberValue name="server">
                <StructureValue index="1">
                    <StructureMemberValue name="host" value="<your http server host>" />
                    <StructureMemberValue name="port" value="<your http server port>" />
                </StructureValue>
            </StructureMemberValue>
        </StructureValue>
    </DefaultValue>
</Parameter>

This will take the structure member definition that the http server supports from the "service.http.base" base and add it to your specific http server definition. Now you only need to override any values you want (usually, you should at least override the port).

HTTPS Support

You can pass in the configuration a keystore path and password and truststore path and password. Doing so will enable client side ssl support. To enforce ssl support, set enforceTLS to true. Doing so will cause an exception to be thrown if ssl configuration fails.

Monitoring

We support automatic monitoring of ServerConnections.

Appendix A - Apache extensions

In some cases you may want to use some specific Apache client features. One of these features could be a custom Apache client HttpEntity Object.

Regular Httprequest only support string or byte array payloads. If you need any other support (e.g. MultiPart), you can set a custom ApacheHttpRequest:

ApacheHttpRequest requestBuilder = ApacheHttpRequest.newBuilder();
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create()
HttpEntity httpEntity = multipartEntityBuilder.build()

ApacheHttpRequest httpRequest= requestBuilder
      .uri("<your uri>")
      .httpMethod(HttpMethod.POST)
      .contentType("multipart/form-data")
      .apacheEntity(httpEntity)
      .build()
Clone this wiki locally