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();
}
}