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

com.github.ji4597056.server.DiscoveryForwardHandler Maven / Gradle / Ivy

The newest version!
package com.github.ji4597056.server;

import com.github.ji4597056.WsForwardProperties;
import com.github.ji4597056.utils.CommonUtils;
import com.github.ji4597056.utils.WebsocketConstant;
import io.netty.util.CharsetUtil;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.util.UriUtils;

/**
 * eureka forward handler
 *
 * @author Jeffrey
 * @since 2017/11/29 13:09
 */
public class DiscoveryForwardHandler extends AbstractWsServerHandler {

    /**
     * init flag
     */
    private volatile boolean dirty = true;

    /**
     * key:patternComparator,value:url path
     */
    private Map serviceMap = new ConcurrentHashMap<>();

    private PathMatcher matcher = new AntPathMatcher();

    private DiscoveryClient discoveryClient;

    private WsForwardProperties wsForwardProperties;

    public DiscoveryForwardHandler(DiscoveryClient discoveryClient,
        WsForwardProperties wsForwardProperties) {
        this.discoveryClient = discoveryClient;
        this.wsForwardProperties = wsForwardProperties;
    }

    @Override
    public String getForwardUrl(WebSocketSession session) {
        init();
        ServiceHandler handler = getServiceId(session.getUri().getPath());
        String address = getLoadBalanceInstance(handler);
        return getWsForwardUrl(address, handler, session.getUri());
    }

    /**
     * get forward url
     *
     * @param address address
     * @param handler ForwardHandler
     * @param uri uri
     * @return forward url
     */
    private String getWsForwardUrl(String address, ServiceHandler handler, URI uri) {
        String prefix = Optional.ofNullable(handler.getForwardPrefix()).orElse("/");
        try {
            String query = UriUtils.encodeQuery(uri.getQuery(), CharsetUtil.UTF_8.name());
            String url = UriUtils.encodePath(handler.getPrefix() == null ? uri.getPath()
                    : uri.getPath().substring(handler.getPrefix().length()),
                CharsetUtil.UTF_8.name());
            if (query == null) {
                return WebsocketConstant.WS_SCHEME + ":/" + prefix + address + url;
            }
            return WebsocketConstant.WS_SCHEME + ":/" + prefix + address + url + "?" + query;
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * get ForwardHandler
     *
     * @param uri uri
     * @return service id
     */
    private ServiceHandler getServiceId(String uri) {
        return serviceMap.entrySet().stream().filter(entry -> matcher.match(entry.getKey(), uri))
            .findFirst().map(Entry::getValue)
            .orElseThrow(
                () -> new IllegalStateException("Can't find matching patterns for " + uri));
    }

    /**
     * get loadbance ServiceInstance
     *
     * @param handler forward handler
     * @return address
     */
    private String getLoadBalanceInstance(ServiceHandler handler) {
        List addresses;
        if (discoveryClient != null && handler.getServiceId() != null) {
            // if service id isn't null, get address from discoveryClient
            List instances = discoveryClient.getInstances(handler.getServiceId());
            if (instances == null || instances.isEmpty()) {
                throw new IllegalStateException("Can't find service id for " + handler.getId());
            }
            addresses = instances.stream()
                .map(instance -> instance.getHost() + ":" + instance.getPort()).collect(
                    Collectors.toList());
        } else if (handler.getListOfServers() != null && handler.getListOfServers().length > 0) {
            // if service-id is null, get addresses from listOfServers
            addresses = Arrays.asList(handler.getListOfServers());
        } else {
            throw new IllegalStateException(
                "Can't find service id or listOfServers for " + handler.getId());
        }
        return addresses
            .get(CommonUtils.getLoadBlanceIndex(handler.getCounter(), addresses.size()));
    }

    /**
     * init serviceMap
     */
    private void init() {
        if (dirty) {
            synchronized (this) {
                if (this.dirty) {
                    wsForwardProperties.getHandlers().forEach((id, handler) -> {
                        if (handler.isEnabled()) {
                            serviceMap.put(CommonUtils.getWsPattern(handler),
                                new ServiceHandler(id, handler.getServiceId(),
                                    handler.getListOfServers(), handler.getPrefix(),
                                    handler.getForwardPrefix()));
                        }
                    });
                    this.dirty = false;
                }
            }
        }
    }

    static class ServiceHandler {

        private final String id;

        private final AtomicInteger counter;

        private final String serviceId;

        private final String[] listOfServers;

        private final String prefix;

        private final String forwardPrefix;

        public ServiceHandler(String id, String serviceId, String[] listOfServers, String prefix,
            String forwardPrefix) {
            this.id = id;
            this.serviceId = serviceId;
            this.listOfServers = listOfServers;
            this.prefix = prefix;
            this.forwardPrefix = forwardPrefix;
            this.counter = new AtomicInteger();
        }

        public String getId() {
            return id;
        }

        public String getServiceId() {
            return serviceId;
        }

        public String[] getListOfServers() {
            return listOfServers;
        }

        public String getPrefix() {
            return prefix;
        }

        public String getForwardPrefix() {
            return forwardPrefix;
        }

        public AtomicInteger getCounter() {
            return counter;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy