spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
predicates:
- name: Path
args:
pattern: "'/services/'+serviceId.toLowerCase()+'/**'"
filters:
- name: RewritePath
args:
regexp: "'/services/' + serviceId.toLowerCase() + '/(?.*)'"
replacement: "'/${remaining}'"
@Component
注解里指定了名称value = "org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter"
,完全覆盖,生效了exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, mergedUrl)
package com.newatc.com.authorization;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.newatc.com.SpringContextHolderUtil;
import com.newatc.com.authorization.util.HttpClientUtil;
import com.newatc.com.authorization.vo.SignalVO;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* 重写Spring的RouteToRequestUrlFilter,自定义unit服务路由
*
* @author yanyulin
* @date 2023-12-6 19:44:52
*/
@Component(value = "org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter")
@Lazy
public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {
public static final int ROUTE_TO_URL_FILTER_ORDER = 10000;
private static final Log log = LogFactory.getLog(RouteToRequestUrlFilter.class);
private static final String SCHEME_REGEX = "[a-zA-Z]([a-zA-Z]|\\d|\\+|\\.|-)*:.*";
static final Pattern schemePattern = Pattern.compile(SCHEME_REGEX);
private static final String UNIT_API_PREFIX = "unit";
private static final String UNIT_ADAPTER = "UNIT_ADAPTER";
public static final String GET_UNIT_URL = "/services/core/api/signalcontrol/getSignalList";
public static final String GET_UNIT_HOSTS = "/services/core/api/signalcontrol/getUnitHostList";
private static final RedisTemplate<String, String> redisTemplate = SpringContextHolderUtil.getBean(StringRedisTemplate.class);
@Value("${server.port}")
private Integer SERVER_PORT;
public RouteToRequestUrlFilter() {}
static boolean hasAnotherScheme(URI uri) {
return schemePattern.matcher(uri.getSchemeSpecificPart()).matches() && uri.getHost() == null && uri.getRawPath() == null;
}
public int getOrder() {
return ROUTE_TO_URL_FILTER_ORDER;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
if (route == null) {
return chain.filter(exchange);
} else {
log.trace("RouteToRequestUrlFilter start");
URI uri = exchange.getRequest().getURI();
boolean encoded = ServerWebExchangeUtils.containsEncodedParts(uri);
URI routeUri = route.getUri();
if (hasAnotherScheme(routeUri)) {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());
routeUri = URI.create(routeUri.getSchemeSpecificPart());
}
log.debug("routeUri: " + JSON.toJSONString(routeUri));
if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {
throw new IllegalStateException("Invalid host: " + routeUri);
} else {
// 原先的Uri
URI mergedUrl = UriComponentsBuilder
.fromUri(uri)
.scheme(routeUri.getScheme())
.host(routeUri.getHost())
.port(routeUri.getPort())
.build(encoded)
.toUri();
// 如果是需要自定义指定路由的服务,根据业务重写
if (UNIT_API_PREFIX.equalsIgnoreCase(routeUri.getHost())) {
try {
log.info("mergedUrl前: " + JSON.toJSONString(mergedUrl));
String[] pathArr = uri.getPath().split("/");
String unitId = pathArr[pathArr.length - 1];
String host = "";
if (StringUtils.hasText(unitId)) {
host = getUnitHost(unitId);
}
if (StringUtils.hasText(host)) {
String newurl = host + mergedUrl.getPath();
if (StringUtils.hasText(exchange.getRequest().getURI().getQuery())) {
newurl = newurl + "?" + exchange.getRequest().getURI().getQuery();
}
URI newURI = new URI(newurl);
mergedUrl =
UriComponentsBuilder
.fromUri(uri)
.scheme(newURI.getScheme())
.host(newURI.getHost())
.port(newURI.getPort())
.build(encoded)
.toUri();
log.debug("mergedUrl后: " + JSON.toJSONString(mergedUrl));
}
} catch (Exception e) {
log.error("uri error", e);
}
}
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, mergedUrl);
return chain.filter(exchange);
}
}
}
/**
* 根据信号机编号获取对应的服务适配器
*
* @param unitId
* @return
*/
private String getUnitHost(String unitId) {
Map<String, String> unitIdHostMap = new HashMap<>();
List<SignalVO> signalList;
if (Boolean.TRUE.equals(redisTemplate.hasKey(UNIT_ADAPTER))) {
signalList = JSONArray.parseArray(redisTemplate.opsForValue().get(UNIT_ADAPTER), SignalVO.class);
} else {
String url = "http://localhost:" + SERVER_PORT + GET_UNIT_URL;
String info = HttpClientUtil.doGet(url, null);
signalList = JSONArray.parseArray(info, SignalVO.class);
if (null != signalList && !signalList.isEmpty()) {
redisTemplate.opsForValue().set(UNIT_ADAPTER, JSON.toJSONString(signalList), 1, TimeUnit.MINUTES);
}
}
if (null != signalList && !signalList.isEmpty()) {
signalList.forEach(e -> unitIdHostMap.put(e.getUnitId(), "http://" + e.getServerIp() + ":" + e.getServerPort()));
}
return unitIdHostMap.get(unitId);
}
}
String newurl = host + mergedUrl.getPath();
if (StringUtils.hasText(exchange.getRequest().getURI().getQuery())) {
newurl = newurl + "?" + exchange.getRequest().getURI().getQuery();
}
URI newURI = new URI(newurl);
mergedUrl =
UriComponentsBuilder
.fromUri(uri)
.scheme(newURI.getScheme())
.host(newURI.getHost())
.port(newURI.getPort())
.build(encoded)
.toUri();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, mergedUrl);
Spring Cloud Gateway是Spring Cloud生态系统中的一个组件,用于构建基于Spring Boot的API网关服务。Spring Cloud Gateway基于Reactive编程模型,使用WebFlux框架实现,可以快速、可靠地构建和部署高性能的微服务应用程序。
Spring Cloud Gateway具有以下特点:
基于WebFlux:Spring Cloud Gateway基于Reactive编程模型,利用WebFlux框架实现非阻塞、事件驱动的异步处理,可以提供更好的性能和并发处理能力。
灵活的路由规则:Spring Cloud Gateway支持基于URI、请求方法、请求头等多种维度的路由规则配置,使得对请求进行灵活的路由转发变得简单易用。
过滤器:Spring Cloud Gateway的过滤器功能可以实现对请求和响应的预处理和后处理,包括请求日志记录、鉴权、路由转发、重定向等功能,可以满足各种需求的定制化处理。
集成性:Spring Cloud Gateway可以与其他Spring Cloud组件和微服务框架无缝集成,例如Eureka、Consul、Ribbon等,能够灵活地进行微服务的注册、发现和负载均衡。
总之,Spring Cloud Gateway是一个灵活、高性能的API网关服务,能够帮助开发者快速构建和部署微服务应用,实现请求路由、负载均衡、安全验证等功能。