io.galeb.health.services.HealthCheckerService.java Source code

Java tutorial

Introduction

Here is the source code for io.galeb.health.services.HealthCheckerService.java

Source

/*
 * Copyright (c) 2014-2017 Globo.com - ATeam
 * All rights reserved.
 *
 * This source is subject to the Apache License, Version 2.0.
 * Please see the LICENSE file for more information.
 *
 * Authors: See AUTHORS file
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.galeb.health.services;

import com.google.gson.Gson;
import io.galeb.core.enums.SystemEnv;
import io.galeb.core.entity.Target;
import io.galeb.core.enums.EnumHealthState;
import io.galeb.health.util.CallBackQueue;
import org.asynchttpclient.AsyncCompletionHandler;
import org.asynchttpclient.AsyncHttpClient;
import org.asynchttpclient.RequestBuilder;
import org.asynchttpclient.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;

import java.net.URI;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

import static io.galeb.core.enums.EnumHealthState.*;
import static io.galeb.core.enums.EnumPropHealth.*;
import static org.asynchttpclient.Dsl.asyncHttpClient;
import static org.asynchttpclient.Dsl.config;

@SuppressWarnings("FieldCanBeLocal")
@Service
public class HealthCheckerService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final boolean keepAlive = Boolean.parseBoolean(SystemEnv.TEST_KEEPALIVE.getValue());
    private final int connectionTimeout = Integer.parseInt(SystemEnv.TEST_CONN_TIMEOUT.getValue());
    private final int pooledConnectionIdleTimeout = Integer.parseInt(SystemEnv.TEST_POOLED_IDLE_TIMEOUT.getValue());
    private final int maxConnectionsPerHost = Integer.parseInt(SystemEnv.TEST_MAXCONN_PER_HOST.getValue());

    private final CallBackQueue callBackQueue;
    private final AsyncHttpClient asyncHttpClient;

    private static final String HEALTHCHECKER_USERAGENT = "Galeb_HealthChecker/1.0";

    @Autowired
    public HealthCheckerService(final CallBackQueue callBackQueue) {
        this.callBackQueue = callBackQueue;
        this.asyncHttpClient = asyncHttpClient(config().setFollowRedirect(false).setSoReuseAddress(true)
                .setKeepAlive(keepAlive).setConnectTimeout(connectionTimeout)
                .setPooledConnectionIdleTimeout(pooledConnectionIdleTimeout)
                .setMaxConnectionsPerHost(maxConnectionsPerHost).setUserAgent(HEALTHCHECKER_USERAGENT).build());
    }

    @SuppressWarnings({ "FutureReturnValueIgnored", "unused" })
    @JmsListener(destination = "galeb-health", concurrency = "5-5")
    public void check(String targetStr) throws ExecutionException, InterruptedException {
        final Target target = new Gson().fromJson(targetStr, Target.class);
        final String poolName = target.getParent().getName();
        final Map<String, String> properties = target.getParent().getProperties();
        final String hcPath = Optional.ofNullable(properties.get(PROP_HEALTHCHECK_PATH.toString())).orElse("/");
        final String hcStatusCode = Optional.ofNullable(properties.get(PROP_HEALTHCHECK_CODE.toString()))
                .orElse("");
        final String hcBody = Optional.ofNullable(properties.get(PROP_HEALTHCHECK_RETURN.toString())).orElse("");
        final String hcHost = Optional.ofNullable(properties.get(PROP_HEALTHCHECK_HOST.toString()))
                .orElse(buildHcHostFromTarget(target));
        final String lastReason = target.getProperties().get(PROP_STATUS_DETAILED.toString());
        long start = System.currentTimeMillis();

        RequestBuilder requestBuilder = new RequestBuilder("GET").setUrl(target.getName() + hcPath)
                .setVirtualHost(hcHost);
        asyncHttpClient.executeRequest(requestBuilder, new AsyncCompletionHandler<Response>() {
            @Override
            public Response onCompleted(Response response) throws Exception {
                if (checkFailStatusCode(response) || checkFailBody(response))
                    return response;
                definePropertiesAndUpdate(OK, OK.toString());
                return response;
            }

            @Override
            public void onThrowable(Throwable t) {
                definePropertiesAndUpdate(UNKNOWN, t.getMessage());
            }

            private boolean checkFailBody(Response response) {
                if (!"".equals(hcBody)) {
                    String body = response.getResponseBody();
                    if (body != null && !body.isEmpty() && !body.contains(hcBody)) {
                        definePropertiesAndUpdate(FAIL, "Body check FAIL");
                        return true;
                    }
                }
                return false;
            }

            private boolean checkFailStatusCode(Response response) {
                if (!"".equals(hcStatusCode)) {
                    String statusCodeStr = String.valueOf(response.getStatusCode());
                    if (!hcStatusCode.equals(statusCodeStr)) {
                        definePropertiesAndUpdate(FAIL, "HTTP Status Code check FAIL");
                        return true;
                    }
                }
                return false;
            }

            private void definePropertiesAndUpdate(EnumHealthState state, String reason) {
                String newHealthyState = state.toString();

                target.getProperties().put(PROP_HEALTHY.toString(), newHealthyState);
                target.getProperties().put(PROP_STATUS_DETAILED.toString(), reason);
                String logMessage = buildLogMessage(reason);
                if (state.equals(OK)) {
                    logger.info(logMessage);
                } else {
                    logger.warn(logMessage);
                }
                if (lastReason == null || !reason.equals(lastReason)) {
                    callBackQueue.update(target);
                }
            }

            private String buildLogMessage(String reason) {
                return "Pool " + poolName + " -> " + "Test Params: { " + "ExpectedBody:\"" + hcBody + "\", "
                        + "ExpectedStatusCode:" + hcStatusCode + ", " + "Host:\"" + hcHost + "\", " + "FullUrl:\""
                        + target.getName() + hcPath + "\", " + "ConnectionTimeout:" + connectionTimeout + "ms }, "
                        + "Result: [ " + reason + " (request time: " + (System.currentTimeMillis() - start)
                        + " ms) ]";
            }

        });
    }

    private String buildHcHostFromTarget(Target target) {
        String hcHost;
        URI targetURI = URI.create(target.getName());
        hcHost = targetURI.getHost() + ":" + targetURI.getPort();
        return hcHost;
    }

}