All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mydotey.artemis.client.registry.InstanceRegistry Maven / Gradle / Ivy

The newest version!
package org.mydotey.artemis.client.registry;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.mydotey.artemis.ErrorCodes;
import org.mydotey.artemis.Instance;
import org.mydotey.artemis.ResponseStatus;
import org.mydotey.artemis.client.common.ArtemisClientConfig;
import org.mydotey.artemis.client.websocket.WebSocketSessionContext;
import org.mydotey.scf.filter.RangeValueConfig;
import org.mydotey.scf.filter.RangeValueFilter;
import org.mydotey.caravan.util.metric.AuditMetric;
import org.mydotey.caravan.util.metric.EventMetric;
import org.mydotey.caravan.util.metric.MetricConfig;
import org.mydotey.artemis.registry.FailedInstance;
import org.mydotey.artemis.registry.HeartbeatResponse;
import org.mydotey.caravan.util.concurrent.DynamicScheduledThread;
import org.mydotey.caravan.util.concurrent.DynamicScheduledThreadConfig;
import org.mydotey.artemis.util.ResponseStatusUtil;
import org.mydotey.codec.json.JacksonJsonCodec;
import org.mydotey.scf.Property;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;

/**
 * Created by fang_j on 10/07/2016.
 */
public class InstanceRegistry {

    private static final Logger _logger = LoggerFactory.getLogger(InstanceRegistry.class);

    private final InstanceRepository _instanceRepository;
    private final Property _ttl;
    private final Property _interval;
    private volatile long _lastHeartbeatTime;
    private volatile long _heartbeatAcceptStartTime = System.currentTimeMillis();
    private final WebSocketSessionContext _sessionContext;
    private final EventMetric _heartbeatStatus;
    private final AuditMetric _heartbeatPrepareLatency;
    private final AuditMetric _heartbeatSendLatency;
    private final AuditMetric _heartbeatAcceptLatency;
    private final DynamicScheduledThread _heartbeatChecker;

    public InstanceRegistry(final InstanceRepository instanceRepository, final ArtemisClientConfig config) {
        Preconditions.checkArgument(instanceRepository != null, "instance repository");
        Preconditions.checkArgument(config != null, "config");
        _instanceRepository = instanceRepository;
        _ttl = config.properties().getIntProperty(config.key("instance-registry.instance-ttl"), 20 * 1000,
            new RangeValueFilter<>(5 * 1000, 24 * 60 * 60 * 1000));
        _interval = config.properties().getIntProperty(config.key("instance-registry.heartbeat-interval"), 5 * 1000,
            new RangeValueFilter<>(500, 5 * 60 * 1000));
        _sessionContext = new WebSocketSessionContext(config) {
            @Override
            protected void afterConnectionEstablished(final WebSocketSession session) {

            }

            @Override
            protected void handleMessage(final WebSocketSession session, final WebSocketMessage message) {
                acceptHeartbeat(message);
            }
        };
        _sessionContext.start();

        _heartbeatStatus = config.eventMetricManager().getMetric(config.key("heartbeat.event"),
            new MetricConfig(ImmutableMap.of("metric_name_distribution", config.key("heartbeat.event.distribution"))));
        _heartbeatPrepareLatency = config.valueMetricManager().getMetric(config.key("heartbeat.prepare-latency"),
            new MetricConfig(ImmutableMap.of("metric_name_distribution",
                config.key("heartbeat.prepare-latency.distribution"), "metric_name_audit",
                config.key("heartbeat.prepare-latency"))));
        _heartbeatSendLatency = config.valueMetricManager().getMetric(config.key("heartbeat.send-latency"),
            new MetricConfig(ImmutableMap
                .of("metric_name_distribution", config.key("heartbeat.send-latency.distribution"), "metric_name_audit",
                    config.key("heartbeat.send-latency"))));
        _heartbeatAcceptLatency = config.valueMetricManager().getMetric(config.key("heartbeat.accept-latency"),
            new MetricConfig(ImmutableMap.of("metric_name_distribution",
                config.key("heartbeat.accept-latency.distribution"), "metric_name_audit",
                config.key("heartbeat.accept-latency"))));
        final DynamicScheduledThreadConfig dynamicScheduledThreadConfig = new DynamicScheduledThreadConfig(
            config.properties(),
            new RangeValueConfig(1, 1, 1 * 60 * 60 * 1000),
            new RangeValueConfig(1000, 500, 90 * 1000));
        _heartbeatChecker = new DynamicScheduledThread(config.key("instance-registry.heartbeat-checker"),
            new Runnable() {
                @Override
                public void run() {
                    checkHeartbeat();
                }
            }, dynamicScheduledThreadConfig);
        _heartbeatChecker.setDaemon(true);
        _heartbeatChecker.start();
    }

    protected void acceptHeartbeat(final WebSocketMessage message) {
        try {
            final HeartbeatResponse response = JacksonJsonCodec.DEFAULT.decode(
                ((String) message.getPayload()).getBytes(),
                HeartbeatResponse.class);
            final ResponseStatus status = response.getResponseStatus();
            if (status == null) {
                _heartbeatStatus.addEvent("null");
            } else {
                _heartbeatStatus.addEvent(status.getStatus());
            }

            long heartbeatTime = System.currentTimeMillis() - _heartbeatAcceptStartTime;
            _heartbeatAcceptLatency.addValue(heartbeatTime);

            if (ResponseStatusUtil.isServiceDown(status)) {
                _sessionContext.markdown();
            }
            if (ResponseStatusUtil.isFail(status)) {
                _logger.warn("heartbeat failed: " + status.getMessage());
            } else if (ResponseStatusUtil.isPartialFail(status)) {
                _logger.info("heartbeat partial failed: " + status.getMessage());
            }

            registerToServicesRegistry(response.getFailedInstances());
        } catch (final Throwable e) {
            _logger.error("handle heartbeat message failed", e);
        }
    }

    protected void sendHeartbeat() {
        try {
            if (_sessionContext.get() == null) {
                return;
            }

            long heartbeatPrepareStartTime = System.currentTimeMillis();
            final TextMessage message = _instanceRepository.getHeartbeatMessage();
            _heartbeatPrepareLatency.addValue(System.currentTimeMillis() - heartbeatPrepareStartTime);
            if (message == null) {
                _logger.info("heartbeat message is null");
                _lastHeartbeatTime = System.currentTimeMillis();
                return;
            }

            long heartbeatSendStartTime = System.currentTimeMillis();
            _sessionContext.get().sendMessage(message);
            _heartbeatSendLatency.addValue(System.currentTimeMillis() - heartbeatSendStartTime);
            _lastHeartbeatTime = System.currentTimeMillis();
            _heartbeatAcceptStartTime = System.currentTimeMillis();
        } catch (Throwable e) {
            _logger.warn("send heartbeat failed.", e);
        }
    }

    protected void checkHeartbeat() {
        long heartbeatInterval = System.currentTimeMillis() - _lastHeartbeatTime;
        if (heartbeatInterval >= _ttl.getValue()) {
            _logger.warn(String.format("heartbeat interval time is more than %d", _ttl.getValue()));
            _sessionContext.markdown();
        }

        if (heartbeatInterval >= _interval.getValue()) {
            sendHeartbeat();
        }
    }

    protected void registerToServicesRegistry(final List failedInstances) {
        try {
            if (CollectionUtils.isEmpty(failedInstances)) {
                return;
            }

            final Set instances = new HashSet();
            for (final FailedInstance fs : failedInstances) {
                if (ErrorCodes.DATA_NOT_FOUND.equals(fs.getErrorCode())
                    || ErrorCodes.UNKNOWN.equals(fs.getErrorCode())) {
                    instances.add(fs.getInstance());
                }
            }
            _instanceRepository.registerToRemote(instances);
        } catch (final Throwable e) {
            _logger.warn("register failed instances failed", e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy