apollo+zuul实现动态路径、动态负载

1、动态路由实现的逻辑是对apollo的配置改变的事件进行监听,如果发现路由出现改变时重新设置zuul中的路由信息.zuul中通过前端请求访问时定位route时通过ZuulProperties中的路由配置通过路径匹配到对应的Route类
2、动态负载的实现逻辑是对pre route post error类型的过滤器的执行路径中自定义PreDecorationFilter的实现类从而替换掉内部的PreDecorationFilter类,自定义类该类中主要做的事情就是从apollo配置文件中读取到服务的权重比例存储到请求上下文中,在自定义IRule时获取到权重比例后定义自定义的权重算法获取目标Server. 权重的动态变化的感知通过自定义的WeightZuulProperties,apollo会自动更新动态权重的在WeightZuulProperties类的值

import com.ctrip.framework.apollo.enums.PropertyChangeType;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * @author 微服务底座平台
 * @version 2.0.0
 * @title: DynamicRouteConfig
 * @projectName: consul-apllo-zuul1
 * @description: 基于apollo的动态路由
 * @date: 2023-05-06 17:19
 * Zuul中的Filter类型: FilterConstants中存在对应过滤器常量
 * pre - 前置过滤器,在请求被路由前执行,通常用于处理身份认证,日志记录等;
 * <p>
 * route - 在路由执行后,服务调用前被调用;
 * <p>
 * error - 任意一个filter发生异常的时候执行或远程服务调用没有反馈的时候执行(超时),通常用于处理异常;
 * <p>
 * post - 在route或error执行后被调用,一般用于收集服务信息,统计服务性能指标等,也可以对response结果做特殊处理
 * <p>
 * pre:
 * 顺序-过滤器名称-功能
 * -3-ServletDetectionFilter-标记处理Servlet的类型
 * -2-Servlet30WrapperFilter-包装HttpServletRequest请求
 * -1-FormBodyWrapperFilter-包装请求体
 * route
 * 1-DebugFilter-标记调试标记
 * 5-PreDecorationFilter-根据配置的路由规则 ,判断请求的路径 需要路由到哪个服务的
 * 10-RibbonRoutingFilter-serviceId请求转发
 * 100-SimpleHostRoutingFilter-url请求转发
 * 500-SendForwardFilter-forward请求转发
 * 0-SendErrorFilter-处理错误请求的请求响应
 * 1000-SendResponseFilter-处理正常的请求响应
 * <p>
 * // 该中存储着不同类型的过滤器缓存,在该类的hashFiltersByType属性中,key-为route/pre/error/post
 * // value为该route类型的所有过滤器,并且根据order进行了排序
 * // 初始化时FilterLoader中的所有过滤器来自于类FilterRegistry
 * FilterLoader filterLoader = FilterLoader.getInstance();
 **/
@Component
@Configuration
public class DynamicRouteConfig implements ApplicationContextAware {

    private static final Log log = LogFactory.getLog(DynamicRouteConfig.class);

    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private RouteLocator routeLocator;
    @Autowired
    private ZuulProperties properties;
    @Autowired
    protected ServerProperties server;
    @Autowired
    private ProxyRequestHelper proxyRequestHelper;

    /**
     * Description @ApolloConfigChangeListener配置改变监听器,触发时将会指向configChange()方法
     * 获取到所有改变的配置中心的key
     * value = "zuul-config" 为apollo中namespace名称
     *
     * @date 2023/5/6 17:22
     * @param: changeEvent
     * @return: void
     */
    @ApolloConfigChangeListener(value = "zuul-config.yaml")
    private void configChange(ConfigChangeEvent changeEvent) {
        // 获取路由
        Map<String, ZuulProperties.ZuulRoute> routes = properties.getRoutes();
        boolean flat = false;
        for (String key : changeEvent.changedKeys()) {
            // 事件内容判断
            if (key.startsWith("zuul.") || key.contains("ribbon.listOfServers")) {
                // 路由是否需要刷新的标识
                flat = true;
                // 根据路由改变类型判断路由是否需要删除
                PropertyChangeType changeType = changeEvent.getChange(key).getChangeType();
                // 删除类的改变类型
                if (PropertyChangeType.DELETED.equals(changeType)) {
                    String[] split = key.split("\\.");
                    String deleteTarget = split[2];
                    Set<String> needDeleteKeys = routes.keySet();
                    if (needDeleteKeys.contains(deleteTarget)) {
                        routes.remove(deleteTarget);
                    }
                }
            }
        }
        // 重新设置路由
        properties.setRoutes(routes);
        // zuul路由刷新
        if (flat) {
            this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
            this.applicationContext.publishEvent(new RoutesRefreshedEvent(this.routeLocator));
        }
    }

    /**
     * Description 路由更新操作
     *
     * @date 2023/5/8 14:06
     * @param: routeEntries
     * @return: void
     */
    private void addConfiguredRoutes(List<ZuulProperties.ZuulRoute> routeEntries) {
        // key为yaml中定义的信息
        Map<String, ZuulProperties.ZuulRoute> routes = this.properties.getRoutes();
        for (ZuulProperties.ZuulRoute routeEntry : routeEntries) {
            String route = routeEntry.getPath();
            if (routes.containsKey(route)) {
                log.warn("Overwriting route " + route + ": already defined by "
                        + routes.get(route));
            }
            routes.put(route, routeEntry);
        }
        //新路由集合重新设置
        this.properties.setRoutes(routes);
    }

    /**
     * Description 获取web上下文
     *
     * @date 2023/5/6 17:21
     * @param: applicationContext
     * @return: void
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * Description 从path中提取ZuulRoute的id
     *
     * @date 2023/5/8 14:30
     * @param: path
     * @return: java.lang.String
     */
    private String extractId(String path) {
        path = path.startsWith("/") ? path.substring(1) : path;
        path = path.replace("/*", "").replace("*", "");
        return path;
    }
}
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.consul.discovery.ConsulServer;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
public class WeightedBalancerRule extends AbstractLoadBalancerRule {

    private static final Log log = LogFactory.getLog(WeightedBalancerRule.class);

    private final AtomicInteger oldNextServerCyclicCounter = new AtomicInteger(0);
    private final AtomicInteger newNextServerCyclicCounter = new AtomicInteger(0);
    private final AtomicInteger nextServerCyclicCounter = new AtomicInteger(0);
    private final AtomicInteger newVersionServerCyclicCounter = new AtomicInteger(0);
    private final AtomicInteger oldVersionServerCyclicCounter = new AtomicInteger(0);

    public WeightedBalancerRule() {
    }

    @Override
    public Server choose(Object key) {
        Server targetServer = null;
        // 请求上下文获取
        RequestContext ctx = RequestContext.getCurrentContext();
        // 获取老服务比例
        Object newServerWeight = ctx.get("newServer");
        // 获取新服务比例
        Object oldServerWeight = ctx.get("oldServer");

        Object newVersion = ctx.get("newVersion");
        // 获取负载器
        ILoadBalancer lb = getLoadBalancer();
        if (lb == null) {
            return null;
        }
        // 获取所有可达不可达服务集合
        List<Server> allServers = lb.getAllServers();
        if (allServers.isEmpty()) {
            return null;
        }
        // 获取所有可达服务
        List<Server> reachableServers = lb.getReachableServers();
        // 存储新老服务实例集合
        List<Server> oldServers = new ArrayList<>();
        List<Server> newServers = new ArrayList<>();
        for (Server server : reachableServers) {
            // 获取对应的服务
            ConsulServer consulServer = (ConsulServer) server;
            List<String> tags = consulServer.getHealthService().getService().getTags();
            if (tags.contains("new-server")) {
                newServers.add(server);
            } else {
                oldServers.add(server);
            }
        }

        if (oldServerWeight == null && newServerWeight == null && newVersion == null) {
            int nextServerIndex = newOrOldIncrementAndGetModulo(oldServers.size(), true, true);
            return oldServers.get(nextServerIndex);
        }
        // 请求头中包含特定请求头时的转发
        if ((oldServerWeight == null || newServerWeight == null) && newVersion != null) {
            int nextServerIndex = newOrOldIncrementAndGetModulo(newServers.size(), false, true);
            return newServers.get(nextServerIndex);
        }

        // 如果不存在权重规则时使用轮询的方式访问
        if (oldServerWeight == null || newServerWeight == null) {
            return defaultRoundRobinRuleChoose(lb, key);
        }
        if (((Integer) oldServerWeight == 0 && (Integer) newServerWeight == 0) || oldServerWeight == newServerWeight) {
            return defaultRoundRobinRuleChoose(lb, key);
        }

        int newServerWeightValue = (Integer) newServerWeight;
        int oldServerWeightValue = (Integer) oldServerWeight;
        // 总权重
        int totalWeight = newServerWeightValue + oldServerWeightValue;
        // n in [0, totalWeight)
        int randomWeight = new Random().nextInt(totalWeight);

        int newServerCount = newServers.size();
        int oldServerCount = oldServers.size();
        if (oldServerWeightValue > newServerWeightValue) {
            // 从旧服务的Server集合中轮询获取一个服务
            if (randomWeight <= newServerWeightValue) {
                // 集合中存在多个服务时轮询获取
                int nextServerIndex = newOrOldIncrementAndGetModulo(newServerCount, true, false);
                targetServer = newServers.get(nextServerIndex);
            }
            // 从新服务的Server集合中轮询获取一个服务
            if (randomWeight >= oldServerWeightValue || randomWeight > newServerWeightValue) {
                int nextServerIndex = newOrOldIncrementAndGetModulo(oldServerCount, false, false);
                targetServer = oldServers.get(nextServerIndex);
            }
        } else {
            if (randomWeight <= oldServerWeightValue) {
                int nextServerIndex = newOrOldIncrementAndGetModulo(oldServerCount, false, false);
                targetServer = oldServers.get(nextServerIndex);
            }
            if (randomWeight >= newServerWeightValue || randomWeight > oldServerWeightValue) {
                int nextServerIndex = newOrOldIncrementAndGetModulo(newServerCount, true, false);
                targetServer = newServers.get(nextServerIndex);
            }
        }
        return targetServer;
    }

    //参考RoundRobinRule中的轮询
    public Server defaultRoundRobinRuleChoose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }

        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            List<Server> allServers = lb.getAllServers();
            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }
            int nextServerIndex = incrementAndGetModulo(serverCount);
            server = allServers.get(nextServerIndex);

            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }

            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }
            // Next.
            server = null;
        }
        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }

    private int incrementAndGetModulo(int modulo) {
        for (; ; ) {
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next)) {
                return next;
            }
        }
    }

    // 轮询获取服务
    private int newOrOldIncrementAndGetModulo(int modulo, boolean newOrOld, boolean newVersion) {
        if (newVersion) {
            if (!newOrOld) {
                for (; ; ) {
                    int current = newVersionServerCyclicCounter.get();
                    int next = (current + 1) % modulo;
                    if (newVersionServerCyclicCounter.compareAndSet(current, next)) {
                        return next;
                    }
                }
            } else {
                for (; ; ) {
                    int current = oldVersionServerCyclicCounter.get();
                    int next = (current + 1) % modulo;
                    if (oldVersionServerCyclicCounter.compareAndSet(current, next)) {
                        return next;
                    }
                }
            }
        }

        if (newOrOld) {
            for (; ; ) {
                int current = newNextServerCyclicCounter.get();
                int next = (current + 1) % modulo;
                if (newNextServerCyclicCounter.compareAndSet(current, next)) {
                    return next;
                }
            }
        } else {
            for (; ; ) {
                int current = oldNextServerCyclicCounter.get();
                int next = (current + 1) % modulo;
                if (oldNextServerCyclicCounter.compareAndSet(current, next)) {
                    return next;
                }
            }
        }
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

import com.netflix.zuul.context.RequestContext;
import com.utils.ApplicationContextUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.pre.InsecureRequestPathException;
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Map;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.*;

/**
 * @author 微服务底座平台
 * @version 2.0.0
 * @title: WeightPreDecorationFilter
 * @projectName: consul-apllo-zuul1
 * @description: 适配权重IRule定义的过滤器
 * @date: 2023-05-10 10:47
 **/
public class WeightPreDecorationFilter extends PreDecorationFilter {

    private static final Log log = LogFactory.getLog(WeightPreDecorationFilter.class);

    private RouteLocator routeLocator;

    private UrlPathHelper urlPathHelper = new UrlPathHelper();

    private ZuulProperties properties;

    private ProxyRequestHelper proxyRequestHelper;

    private String dispatcherServletPath;

    public WeightPreDecorationFilter(RouteLocator routeLocator, String dispatcherServletPath, ZuulProperties properties, ProxyRequestHelper proxyRequestHelper) {
        super(routeLocator, dispatcherServletPath, properties, proxyRequestHelper);
        this.routeLocator = routeLocator;
        this.properties = properties;
        this.urlPathHelper
                .setRemoveSemicolonContent(properties.isRemoveSemicolonContent());
        this.urlPathHelper.setUrlDecode(properties.isDecodeUrl());
        this.dispatcherServletPath = dispatcherServletPath;
        this.proxyRequestHelper = proxyRequestHelper;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        final String requestURI = this.urlPathHelper
                .getPathWithinApplication(ctx.getRequest());
        if (insecurePath(requestURI)) {
            throw new InsecureRequestPathException(requestURI);
        }
        Route route = this.routeLocator.getMatchingRoute(requestURI);
        if (route != null) {

            WeightZuulProperties weightZuulProperties = ApplicationContextUtil.getBean(WeightZuulProperties.class);
            Map<String, WeightZuulProperties.WeightRoute> routes = weightZuulProperties.getRoutes();
            WeightZuulProperties.WeightRoute weightRoute = routes.get(route.getId());
            // 将权重比例存储到上下文中
            ctx.set("newServer", weightRoute.getNewServer());
            ctx.set("oldServer", weightRoute.getOldServer());
            // 从请求头中获取到指定请求头
            String newVersion = ctx.getRequest().getHeader("newVersion");
            ctx.set("newVersion", newVersion);

            String location = route.getLocation();
            if (location != null) {
                ctx.put(REQUEST_URI_KEY, route.getPath());
                ctx.put(PROXY_KEY, route.getId());
                if (!route.isCustomSensitiveHeaders()) {
                    this.proxyRequestHelper.addIgnoredHeaders(
                            this.properties.getSensitiveHeaders().toArray(new String[0]));
                } else {
                    this.proxyRequestHelper.addIgnoredHeaders(
                            route.getSensitiveHeaders().toArray(new String[0]));
                }

                if (route.getRetryable() != null) {
                    ctx.put(RETRYABLE_KEY, route.getRetryable());
                }

                if (location.startsWith(HTTP_SCHEME + ":")
                        || location.startsWith(HTTPS_SCHEME + ":")) {
                    ctx.setRouteHost(getUrl(location));
                    ctx.addOriginResponseHeader(SERVICE_HEADER, location);
                } else if (location.startsWith(FORWARD_LOCATION_PREFIX)) {
                    ctx.set(FORWARD_TO_KEY,
                            StringUtils.cleanPath(
                                    location.substring(FORWARD_LOCATION_PREFIX.length())
                                            + route.getPath()));
                    ctx.setRouteHost(null);
                    return null;
                } else {
                    // set serviceId for use in filters.route.RibbonRequest
                    ctx.set(SERVICE_ID_KEY, location);
                    ctx.setRouteHost(null);
                    ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location);
                }
                if (this.properties.isAddProxyHeaders()) {
                    addProxyHeaders(ctx, route);
                    String xforwardedfor = ctx.getRequest()
                            .getHeader(X_FORWARDED_FOR_HEADER);
                    String remoteAddr = ctx.getRequest().getRemoteAddr();
                    if (xforwardedfor == null) {
                        xforwardedfor = remoteAddr;
                    } else if (!xforwardedfor.contains(remoteAddr)) { // Prevent duplicates
                        xforwardedfor += ", " + remoteAddr;
                    }
                    ctx.addZuulRequestHeader(X_FORWARDED_FOR_HEADER, xforwardedfor);
                }
                if (this.properties.isAddHostHeader()) {
                    ctx.addZuulRequestHeader(HttpHeaders.HOST,
                            toHostHeader(ctx.getRequest()));
                }
            }
        } else {
            log.warn("No route found for uri: " + requestURI);
            String forwardURI = getForwardUri(requestURI);

            ctx.set(FORWARD_TO_KEY, forwardURI);
        }
        return null;
    }
    /*public Object run() {
        // 自定义Route中获取所有路由信息
        WeightZuulProperties weightZuulProperties = ApplicationContextUtil.getBean(WeightZuulProperties.class);
        Map<String, WeightZuulProperties.WeightRoute> routes = weightZuulProperties.getRoutes();
        RequestContext ctx = RequestContext.getCurrentContext();
        final String requestUri = this.urlPathHelper
                .getPathWithinApplication(ctx.getRequest());
        if (insecurePath(requestUri)) {
            throw new InsecureRequestPathException(requestUri);
        }
        Route route = this.routeLocator.getMatchingRoute(requestUri);
        WeightZuulProperties.WeightRoute weightRoute = routes.get(route.getId());
        // 将权重比例存储到上下文中
        ctx.set("newServer", weightRoute.getNewServer());
        ctx.set("oldServer", weightRoute.getOldServer());
        // 执行父类逻辑
        return super.run();
    }*/

    String getForwardUri(String requestURI) {
        // default fallback servlet is DispatcherServlet
        String fallbackPrefix = this.dispatcherServletPath;

        String fallBackUri = requestURI;
        if (RequestUtils.isZuulServletRequest()) {
            // remove the Zuul servletPath from the requestUri
            log.debug("zuulServletPath=" + this.properties.getServletPath());
            fallBackUri = fallBackUri.replaceFirst(this.properties.getServletPath(), "");
            log.debug("Replaced Zuul servlet path:" + fallBackUri);
        } else if (this.dispatcherServletPath != null) {
            // remove the DispatcherServlet servletPath from the requestUri
            log.debug("dispatcherServletPath=" + this.dispatcherServletPath);
            fallBackUri = fallBackUri.replaceFirst(this.dispatcherServletPath, "");
            log.debug("Replaced DispatcherServlet servlet path:" + fallBackUri);
        }
        if (!fallBackUri.startsWith("/")) {
            fallBackUri = "/" + fallBackUri;
        }

        String forwardURI = (fallbackPrefix == null) ? fallBackUri
                : fallbackPrefix + fallBackUri;
        forwardURI = DOUBLE_SLASH.matcher(forwardURI).replaceAll("/");
        return forwardURI;
    }

    private boolean insecurePath(String path) {
        if (StringUtils.isEmpty(path)) {
            return false;
        }
        if (path.contains("%")) {
            try {
                path = URLDecoder.decode(path, "UTF-8");
            } catch (UnsupportedEncodingException ignored) {
                // Should never happen...
            }
        }
        if (isInsecurePath(path)) {
            return true;
        }
        return isInsecurePath(urlPathHelper.removeSemicolonContent(path));
    }

    private boolean isInsecurePath(String path) {
        if (path.contains(":/")) {
            String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
            if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
                if (log.isWarnEnabled()) {
                    log.warn(
                            "Path represents URL or has \"url:\" prefix: [" + path + "]");
                }
                return true;
            }
        }
        if (path.contains("../")) {
            if (log.isWarnEnabled()) {
                log.warn("Path contains \"../\"");
            }
            return true;
        }
        if (path.contains("..\\")) {
            if (log.isWarnEnabled()) {
                log.warn("Path contains \"..\\\"");
            }
            return true;
        }
        return false;
    }

    private void addProxyHeaders(RequestContext ctx, Route route) {
        HttpServletRequest request = ctx.getRequest();
        String host = toHostHeader(request);
        String port = String.valueOf(request.getServerPort());
        String proto = request.getScheme();
        if (hasHeader(request, X_FORWARDED_HOST_HEADER)) {
            host = request.getHeader(X_FORWARDED_HOST_HEADER) + "," + host;
        }
        if (!hasHeader(request, X_FORWARDED_PORT_HEADER)) {
            if (hasHeader(request, X_FORWARDED_PROTO_HEADER)) {
                StringBuilder builder = new StringBuilder();
                for (String previous : StringUtils.commaDelimitedListToStringArray(
                        request.getHeader(X_FORWARDED_PROTO_HEADER))) {
                    if (builder.length() > 0) {
                        builder.append(",");
                    }
                    builder.append(
                            HTTPS_SCHEME.equals(previous) ? HTTPS_PORT : HTTP_PORT);
                }
                builder.append(",").append(port);
                port = builder.toString();
            }
        } else {
            port = request.getHeader(X_FORWARDED_PORT_HEADER) + "," + port;
        }
        if (hasHeader(request, X_FORWARDED_PROTO_HEADER)) {
            proto = request.getHeader(X_FORWARDED_PROTO_HEADER) + "," + proto;
        }
        ctx.addZuulRequestHeader(X_FORWARDED_HOST_HEADER, host);
        ctx.addZuulRequestHeader(X_FORWARDED_PORT_HEADER, port);
        ctx.addZuulRequestHeader(X_FORWARDED_PROTO_HEADER, proto);
        addProxyPrefix(ctx, route);
    }

    private boolean hasHeader(HttpServletRequest request, String name) {
        return StringUtils.hasLength(request.getHeader(name));
    }

    private void addProxyPrefix(RequestContext ctx, Route route) {
        String forwardedPrefix = ctx.getRequest().getHeader(X_FORWARDED_PREFIX_HEADER);
        String contextPath = ctx.getRequest().getContextPath();
        String prefix = StringUtils.hasLength(forwardedPrefix) ? forwardedPrefix
                : (StringUtils.hasLength(contextPath) ? contextPath : null);
        if (StringUtils.hasText(route.getPrefix())) {
            StringBuilder newPrefixBuilder = new StringBuilder();
            if (prefix != null) {
                if (prefix.endsWith("/") && route.getPrefix().startsWith("/")) {
                    newPrefixBuilder.append(prefix, 0, prefix.length() - 1);
                } else {
                    newPrefixBuilder.append(prefix);
                }
            }
            newPrefixBuilder.append(route.getPrefix());
            prefix = newPrefixBuilder.toString();
        }
        if (prefix != null) {
            ctx.addZuulRequestHeader(X_FORWARDED_PREFIX_HEADER, prefix);
        }
    }

    private String toHostHeader(HttpServletRequest request) {
        int port = request.getServerPort();
        if ((port == HTTP_PORT && HTTP_SCHEME.equals(request.getScheme()))
                || (port == HTTPS_PORT && HTTPS_SCHEME.equals(request.getScheme()))) {
            return request.getServerName();
        } else {
            return request.getServerName() + ":" + port;
        }
    }

    private URL getUrl(String target) {
        try {
            return new URL(target);
        } catch (MalformedURLException ex) {
            throw new IllegalStateException("Target URL is malformed", ex);
        }
    }
}


import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author 微服务底座平台
 * @version 2.0.0
 * @title: WeightZuulProperties
 * @projectName: consul-apllo-zuul1
 * @description: 适配权重配置
 * @date: 2023-05-10 10:56
 **/
@Component
@Configuration
@ConfigurationProperties("zuul")
public class WeightZuulProperties {

    Map<String, WeightRoute> routes = new LinkedHashMap<>();

    public Map<String, WeightRoute> getRoutes() {
        return routes;
    }

    public void setRoutes(Map<String, WeightRoute> routes) {
        this.routes = routes;
    }

    public static class WeightRoute extends ZuulProperties.ZuulRoute {

        /*获取老服务比例*/
        private Integer newServer;
        /*获取新服务比例*/
        private Integer oldServer;

        public Integer getNewServer() {
            return newServer;
        }

        public void setNewServer(Integer newServer) {
            this.newServer = newServer;
        }

        public Integer getOldServer() {
            return oldServer;
        }

        public void setOldServer(Integer oldServer) {
            this.oldServer = oldServer;
        }
    }

}


import com.netflix.loadbalancer.IRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * @author 微服务底座平台
 * @version 2.0.0
 * @title: ZuulConfigure
 * @projectName: consul-apllo-zuul1
 * @description: ZuulConfigure
 * @date: 2023-05-10 14:00
 **/
@Component
@Configuration
public class ZuulConfigure {

    @Autowired
    protected ServerProperties server;

    @Autowired
    protected ZuulProperties zuulProperties;

    @Bean(value = "preDecorationFilter")
    public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
                                                   ProxyRequestHelper proxyRequestHelper) {
        return new WeightPreDecorationFilter(routeLocator,
                this.server.getServlet().getContextPath(), this.zuulProperties,
                proxyRequestHelper);
    }

    // 自定义IRule,该规则将会作用域所有需要负载的客户端
    @Bean
    public IRule rule(){
        return  new WeightedBalancerRule();
    }
}

apollo中对应的服务的权重比例配置及路由配置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值