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

org.mydotey.rpc.client.http.RandomLoadBalancer Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
package org.mydotey.rpc.client.http;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import org.mydotey.java.ObjectExtension;
import org.mydotey.rpc.error.ServiceUnavailableException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author koqizhao
 *
 * Jan 16, 2019
 */
public class RandomLoadBalancer implements HttpLoadBalancer {

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

    private long _ttl;
    private long _updateInterval;

    private List _serviceUrls;
    private volatile int _current;
    private volatile long _lastUpdateTime;
    private volatile long _lastForceUpdateTime;

    private Random _random;

    public RandomLoadBalancer(List serviceUrls, long ttl, long updateInterval) {
        ObjectExtension.requireNonEmpty(serviceUrls, "serviceUrls");
        if (ttl <= 0)
            throw new IllegalArgumentException("ttl <= 0: " + ttl);
        if (updateInterval < 0)
            throw new IllegalArgumentException("updateInterval < 0: " + updateInterval);

        _ttl = ttl;
        _updateInterval = updateInterval;

        _serviceUrls = new ArrayList<>(serviceUrls);
        Collections.shuffle(_serviceUrls);
        _lastUpdateTime = System.currentTimeMillis();
        _logger.info("init with serviceUrls: {}", _serviceUrls);

        _random = new Random();
    }

    @Override
    public HttpExecutionContext newExecutionContext() {
        String serviceUrl = getServiceUrl();
        return new ExecutionContext(serviceUrl);
    }

    protected void complete(HttpExecutionContext executionContext) {
        ObjectExtension.requireNonNull(executionContext, "executionContext");

        Throwable ex = executionContext.getExecutionError();
        if (ex == null)
            return;

        if (ex instanceof HttpRuntimeException || ex instanceof ServiceUnavailableException)
            forceUpdate();
    }

    protected String getServiceUrl() {
        if (_serviceUrls.size() > 1) {
            if (System.currentTimeMillis() - _lastUpdateTime >= _ttl) {
                synchronized (this) {
                    if (System.currentTimeMillis() - _lastUpdateTime >= _ttl) {
                        update();
                    }
                }
            }
        }

        return _serviceUrls.get(_current);
    }

    protected void forceUpdate() {
        if (_serviceUrls.size() <= 1)
            return;

        if (System.currentTimeMillis() - _lastForceUpdateTime < _updateInterval)
            return;

        synchronized (this) {
            if (System.currentTimeMillis() - _lastForceUpdateTime < _updateInterval)
                return;

            update();
            _lastForceUpdateTime = _lastUpdateTime;
        }
    }

    private void update() {
        int old = _current;
        do {
            _current = _random.nextInt(_serviceUrls.size());
        } while (_current == old);
        _lastUpdateTime = System.currentTimeMillis();
        _logger.info("serviceUrl changed from {} to {}", _serviceUrls.get(old), _serviceUrls.get(_current));
    }

    protected class ExecutionContext implements HttpExecutionContext {

        private String _serviceUrl;
        private volatile long _startTime;
        private volatile long _endTime;
        private volatile Throwable _executionError;

        public ExecutionContext(String serviceUrl) {
            ObjectExtension.requireNonBlank(serviceUrl, "serviceUrl");

            _serviceUrl = serviceUrl;
            _startTime = System.currentTimeMillis();
        }

        @Override
        public String getServiceUrl() {
            return _serviceUrl;
        }

        @Override
        public long getStartTime() {
            return _startTime;
        }

        @Override
        public long getEndTime() {
            return _endTime;
        }

        @Override
        public Throwable getExecutionError() {
            return _executionError;
        }

        @Override
        public void setExecutionError(Throwable executionError) {
            ObjectExtension.requireNonNull(executionError, "executionError");
            _executionError = executionError;
        }

        @Override
        public void complete() {
            if (_endTime != 0)
                return;

            synchronized (this) {
                if (_endTime != 0)
                    return;

                _endTime = System.currentTimeMillis();
                RandomLoadBalancer.this.complete(this);
            }
        }

        @Override
        public String toString() {
            return "ExecutionContext [_serviceUrl=" + _serviceUrl + ", _startTime=" + _startTime + ", _endTime="
                    + _endTime + ", _executionError=" + _executionError + "]";
        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy