ezbake.discovery.stethoscope.server.StethoscopeServiceHandler.java Source code

Java tutorial

Introduction

Here is the source code for ezbake.discovery.stethoscope.server.StethoscopeServiceHandler.java

Source

/*   Copyright (C) 2013-2015 Computer Sciences Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 ezbake.discovery.stethoscope.server;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.net.HostAndPort;

import ezbake.base.thrift.EzBakeBaseThriftService;
import ezbake.common.properties.EzProperties;
import ezbake.discovery.stethoscope.thrift.StethoscopeService;
import ezbake.discovery.stethoscope.thrift.Endpoint;
import ezbake.ezdiscovery.ServiceDiscoveryClient;

import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.math.NumberUtils;
import org.apache.thrift.TProcessor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StethoscopeServiceHandler extends EzBakeBaseThriftService implements StethoscopeService.Iface {
    public final static String STETHOSCOPE_SERVICE_WRITE_EXPIRE_TIME = "stethoscope.service.write.expire.time.minutes";
    public final static String STETHOSCOPE_SERVICE_CLEANUP_TIME = "stethoscope.service.cleanup.time.minutes";
    public final static String STETHOSCOPE_ACTUALLY_REMOVE_FROM_ZOOKEEPER = "stethoscope.actually.remove.from.zookeeper";
    public final static String STETHOSCOPE_SERVICES_TO_IGNORE = "stethoscope.services.to.ignore";

    private final static Logger logger = LoggerFactory.getLogger(StethoscopeServiceHandler.class);

    private Cache<String, StethoscopeCacheEntry> serviceCache;

    private ServiceDiscoveryClient serviceDiscoveryClient;

    private EzProperties configuration;

    private ScheduledExecutorService scheduler;

    @Override
    public boolean checkin(String applicationName, String serviceName, Endpoint endpoint) {
        this.serviceCache.put(endpointToString(endpoint),
                new StethoscopeCacheEntry(applicationName, serviceName, System.nanoTime()));
        // Just make sure we access to make Guava happy
        this.serviceCache.getIfPresent(endpointToString(endpoint));
        logger.info("{} for {}{} just checked in!", endpointToString(endpoint), applicationName, serviceName);
        return true;
    }

    @Override
    public TProcessor getThriftProcessor() {
        this.configuration = new EzProperties(getConfigurationProperties(), true);
        this.serviceDiscoveryClient = new ServiceDiscoveryClient(configuration);

        int expireMinutes = configuration.getInteger(STETHOSCOPE_SERVICE_WRITE_EXPIRE_TIME, 15);
        boolean shouldRemoveEntriesFromZookeeper = configuration
                .getBoolean(STETHOSCOPE_ACTUALLY_REMOVE_FROM_ZOOKEEPER, false);
        logger.info("Stethoscope will wait {} minutes before timing something out after write", expireMinutes);

        Multimap<String, String> servicesToIgnore = getServicesToIgnore(
                configuration.getProperty(STETHOSCOPE_SERVICES_TO_IGNORE, ""));

        for (Map.Entry<String, String> entry : servicesToIgnore.entries()) {
            logger.info("Application: {}, Service: {} will NOT be removed from zookeeper", entry.getKey(),
                    entry.getValue());
        }

        if (shouldRemoveEntriesFromZookeeper) {
            logger.info("Stethoscope will remove entries from zookeeper");
        } else {
            logger.info("Stethoscope will NOT remove entries from zookeeper");
        }

        this.serviceCache = CacheBuilder.newBuilder().expireAfterWrite(expireMinutes, TimeUnit.MINUTES)
                .removalListener(new StethoscopeCacheRemovalListener(serviceDiscoveryClient,
                        shouldRemoveEntriesFromZookeeper, servicesToIgnore))
                .build();

        this.scheduler = Executors.newScheduledThreadPool(1);
        int cleanupMinutes = configuration.getInteger(STETHOSCOPE_SERVICE_CLEANUP_TIME, 10);
        logger.info("Stethoscope will wait {} minutes before running the clean up thread!", cleanupMinutes);
        scheduler.scheduleAtFixedRate(new CacheMaintenanceRunnable(), 0, cleanupMinutes, TimeUnit.MINUTES);
        populateCacheFromZookeeper();
        return new StethoscopeService.Processor(this);
    }

    @VisibleForTesting
    static String endpointToString(Endpoint endpoint) {
        return HostAndPort.fromParts(endpoint.getHostname(), endpoint.getPort()).toString();
    }

    /**
     * We use this to intially populate the cache
     */
    @VisibleForTesting
    void populateCacheFromZookeeper() {
        int count = 0;
        try {
            // So many loops :P
            for (String appName : serviceDiscoveryClient.getApplications()) {
                for (String serviceName : serviceDiscoveryClient.getServices(appName)) {
                    for (String endpoint : serviceDiscoveryClient.getEndpoints(appName, serviceName)) {
                        logger.info("Loaded application {}, service: {}, endpoint {}", appName, serviceName,
                                endpoint);
                        ++count;
                        serviceCache.put(endpoint,
                                new StethoscopeCacheEntry(appName, serviceName, System.nanoTime()));
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Unable to load the cache from zookeeper", e);
            throw new RuntimeException(e);
        }
        logger.info("Loaded {} entries from zookeeper", count);
    }

    /**
     * A private interal class which will force clean up at a given interval
     */
    private class CacheMaintenanceRunnable implements Runnable {
        @Override
        public void run() {
            logger.info("Cleaning up cache!");
            serviceCache.cleanUp();
            logger.info("The size of the case is {} ", serviceCache.size());
        }
    }

    private Multimap<String, String> getServicesToIgnore(final String serviceString) {
        Multimap<String, String> retVal = ArrayListMultimap.create();
        for (String entry : Splitter.on(',').split(serviceString)) {
            String[] tokens = entry.split("/");
            if (tokens.length < 2) {
                continue;
            }
            retVal.put(tokens[0], tokens[1]);
        }
        return retVal;
    }
}