SpringCloudGateway-1
大约 15 分钟
SpringCloudGateway 自动装配类
1、
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-core</artifactId>
<version>2.2.2.RELEASE</version>
<scope>compile</scope>
</dependency>
2、
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayHystrixCircuitBreakerAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayResilience4JCircuitBreakerAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayRedisAutoConfiguration,\
org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration,\
org.springframework.cloud.gateway.config.SimpleUrlHandlerMappingGlobalCorsAutoConfiguration,\
org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.gateway.config.GatewayEnvironmentPostProcessor
GatewayAutoConfiguration
1、
package org.springframework.cloud.gateway.config;
@Configuration(proxyBeanMethods = false)
// 如果要想让此配置类生效,应该在配置文件之中提供有“spring.cloud.gateway.enabled”配置项,若没有为true
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties // 注入一些相关的属性内容
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, // HTTP处理类
WebFluxAutoConfiguration.class }) // 配置WebFlux处理类
// 处理完成之后还需要提供有相关的Bean对象
@AutoConfigureAfter({ GatewayLoadBalancerClientAutoConfiguration.class,
GatewayClassPathWarningAutoConfiguration.class })
@ConditionalOnClass(DispatcherHandler.class) // 分发的处理
public class GatewayAutoConfiguration {
@Bean
public StringToZonedDateTimeConverter stringToZonedDateTimeConverter() {
return new StringToZonedDateTimeConverter();// 字符串与日期时间转换处理
}
@Bean
public RouteLocatorBuilder routeLocatorBuilder(// 路由定位器构建器
ConfigurableApplicationContext context) {
return new RouteLocatorBuilder(context);
}
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(
GatewayProperties properties) { // 提供有相关的路由的配置属性
return new PropertiesRouteDefinitionLocator(properties);
}
// 面试题:SpringCloudGateway之中所有的路由信息保存在什么位置?
// 回答:所有的路由信息保存在网关的内存之中,这样可以快速读取配置;
@Bean
@ConditionalOnMissingBean(RouteDefinitionRepository.class)
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();// 在内存中保存路由的定义存储
}
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(
List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(
Flux.fromIterable(routeDefinitionLocators));
}
@Bean
public ConfigurationService gatewayConfigurationService(BeanFactory beanFactory,
@Qualifier("webFluxConversionService") ObjectProvider<ConversionService> conversionService,
ObjectProvider<Validator> validator) {
return new ConfigurationService(beanFactory, conversionService, validator);
}
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator,
ConfigurationService configurationService) { // 根据构造器创建路由定位器
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
gatewayFilters, properties, configurationService);
}
@Bean
@Primary
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(// 缓存路由定位器
new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
@Bean
public RouteRefreshListener routeRefreshListener(// 路由刷新监听
ApplicationEventPublisher publisher) {
return new RouteRefreshListener(publisher); // 允许动态修改路由配置
}
@Bean
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
return new FilteringWebHandler(globalFilters);
}
@Bean
public GlobalCorsProperties globalCorsProperties() {
return new GlobalCorsProperties();
}
@Bean
public RoutePredicateHandlerMapping routePredicateHandlerMapping(
FilteringWebHandler webHandler, RouteLocator routeLocator,
GlobalCorsProperties globalCorsProperties, Environment environment) {
return new RoutePredicateHandlerMapping(webHandler, routeLocator,
globalCorsProperties, environment);
}
@Bean
public GatewayProperties gatewayProperties() { // 网关配置的属性内容
return new GatewayProperties();
}
@Bean
public SecureHeadersProperties secureHeadersProperties() {
return new SecureHeadersProperties();
}
@Bean
@ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled",
matchIfMissing = true)
public ForwardedHeadersFilter forwardedHeadersFilter() {
return new ForwardedHeadersFilter();// 过滤器
}
@Bean
public RemoveHopByHopHeadersFilter removeHopByHopHeadersFilter() {
return new RemoveHopByHopHeadersFilter();
}
@Bean
@ConditionalOnProperty(name = "spring.cloud.gateway.x-forwarded.enabled",
matchIfMissing = true)
public XForwardedHeadersFilter xForwardedHeadersFilter() {
return new XForwardedHeadersFilter();
}
@Bean
public AdaptCachedBodyGlobalFilter adaptCachedBodyGlobalFilter() {
return new AdaptCachedBodyGlobalFilter();
}
@Bean
public RemoveCachedBodyFilter removeCachedBodyFilter() {
return new RemoveCachedBodyFilter();
}
@Bean
public RouteToRequestUrlFilter routeToRequestUrlFilter() {
return new RouteToRequestUrlFilter();
}
@Bean
public ForwardRoutingFilter forwardRoutingFilter(
ObjectProvider<DispatcherHandler> dispatcherHandler) {
return new ForwardRoutingFilter(dispatcherHandler);
}
@Bean
public ForwardPathFilter forwardPathFilter() {
return new ForwardPathFilter();
}
@Bean
public WebSocketService webSocketService() {
return new HandshakeWebSocketService();
}
@Bean
public WebsocketRoutingFilter websocketRoutingFilter(WebSocketClient webSocketClient,
WebSocketService webSocketService,
ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
return new WebsocketRoutingFilter(webSocketClient, webSocketService,
headersFilters);
}
@Bean
public WeightCalculatorWebFilter weightCalculatorWebFilter(
ConfigurationService configurationService,
ObjectProvider<RouteLocator> routeLocator) {
return new WeightCalculatorWebFilter(routeLocator, configurationService);
}
@Bean
public AfterRoutePredicateFactory afterRoutePredicateFactory() {
return new AfterRoutePredicateFactory();
}
@Bean
public BeforeRoutePredicateFactory beforeRoutePredicateFactory() {
return new BeforeRoutePredicateFactory();
}
@Bean
public BetweenRoutePredicateFactory betweenRoutePredicateFactory() {
return new BetweenRoutePredicateFactory();
}
@Bean
public CookieRoutePredicateFactory cookieRoutePredicateFactory() {
return new CookieRoutePredicateFactory();
}
@Bean
public HeaderRoutePredicateFactory headerRoutePredicateFactory() {
return new HeaderRoutePredicateFactory();
}
@Bean
public HostRoutePredicateFactory hostRoutePredicateFactory() {
return new HostRoutePredicateFactory();
}
@Bean
public MethodRoutePredicateFactory methodRoutePredicateFactory() {
return new MethodRoutePredicateFactory();
}
@Bean
public PathRoutePredicateFactory pathRoutePredicateFactory() {
return new PathRoutePredicateFactory();
}
@Bean
public QueryRoutePredicateFactory queryRoutePredicateFactory() {
return new QueryRoutePredicateFactory();
}
@Bean
public ReadBodyPredicateFactory readBodyPredicateFactory() {
return new ReadBodyPredicateFactory();
}
@Bean
public RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory() {
return new RemoteAddrRoutePredicateFactory();
}
@Bean
@DependsOn("weightCalculatorWebFilter")
public WeightRoutePredicateFactory weightRoutePredicateFactory() {
return new WeightRoutePredicateFactory();
}
@Bean
public CloudFoundryRouteServiceRoutePredicateFactory cloudFoundryRouteServiceRoutePredicateFactory() {
return new CloudFoundryRouteServiceRoutePredicateFactory();
}
@Bean
public AddRequestHeaderGatewayFilterFactory addRequestHeaderGatewayFilterFactory() {
return new AddRequestHeaderGatewayFilterFactory();
}
@Bean
public MapRequestHeaderGatewayFilterFactory mapRequestHeaderGatewayFilterFactory() {
return new MapRequestHeaderGatewayFilterFactory();
}
@Bean
public AddRequestParameterGatewayFilterFactory addRequestParameterGatewayFilterFactory() {
return new AddRequestParameterGatewayFilterFactory();
}
@Bean
public AddResponseHeaderGatewayFilterFactory addResponseHeaderGatewayFilterFactory() {
return new AddResponseHeaderGatewayFilterFactory();
}
@Bean
public ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory(
ServerCodecConfigurer codecConfigurer) {
return new ModifyRequestBodyGatewayFilterFactory(codecConfigurer.getReaders());
}
@Bean
public DedupeResponseHeaderGatewayFilterFactory dedupeResponseHeaderGatewayFilterFactory() {
return new DedupeResponseHeaderGatewayFilterFactory();
}
@Bean
public ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory(
ServerCodecConfigurer codecConfigurer, Set<MessageBodyDecoder> bodyDecoders,
Set<MessageBodyEncoder> bodyEncoders) {
return new ModifyResponseBodyGatewayFilterFactory(codecConfigurer.getReaders(),
bodyDecoders, bodyEncoders);
}
@Bean
public PrefixPathGatewayFilterFactory prefixPathGatewayFilterFactory() {
return new PrefixPathGatewayFilterFactory();
}
@Bean
public PreserveHostHeaderGatewayFilterFactory preserveHostHeaderGatewayFilterFactory() {
return new PreserveHostHeaderGatewayFilterFactory();
}
@Bean
public RedirectToGatewayFilterFactory redirectToGatewayFilterFactory() {
return new RedirectToGatewayFilterFactory();
}
@Bean
public RemoveRequestHeaderGatewayFilterFactory removeRequestHeaderGatewayFilterFactory() {
return new RemoveRequestHeaderGatewayFilterFactory();
}
@Bean
public RemoveRequestParameterGatewayFilterFactory removeRequestParameterGatewayFilterFactory() {
return new RemoveRequestParameterGatewayFilterFactory();
}
@Bean
public RemoveResponseHeaderGatewayFilterFactory removeResponseHeaderGatewayFilterFactory() {
return new RemoveResponseHeaderGatewayFilterFactory();
}
@Bean(name = PrincipalNameKeyResolver.BEAN_NAME)
@ConditionalOnBean(RateLimiter.class)
@ConditionalOnMissingBean(KeyResolver.class)
public PrincipalNameKeyResolver principalNameKeyResolver() {
return new PrincipalNameKeyResolver();
}
@Bean
@ConditionalOnBean({ RateLimiter.class, KeyResolver.class })
public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory(
RateLimiter rateLimiter, KeyResolver resolver) {
return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver);
}
@Bean
public RewritePathGatewayFilterFactory rewritePathGatewayFilterFactory() {
return new RewritePathGatewayFilterFactory();
}
@Bean
public RetryGatewayFilterFactory retryGatewayFilterFactory() {
return new RetryGatewayFilterFactory();
}
@Bean
public SetPathGatewayFilterFactory setPathGatewayFilterFactory() {
return new SetPathGatewayFilterFactory();
}
@Bean
public SecureHeadersGatewayFilterFactory secureHeadersGatewayFilterFactory(
SecureHeadersProperties properties) {
return new SecureHeadersGatewayFilterFactory(properties);
}
@Bean
public SetRequestHeaderGatewayFilterFactory setRequestHeaderGatewayFilterFactory() {
return new SetRequestHeaderGatewayFilterFactory();
}
@Bean
public SetResponseHeaderGatewayFilterFactory setResponseHeaderGatewayFilterFactory() {
return new SetResponseHeaderGatewayFilterFactory();
}
@Bean
public RewriteResponseHeaderGatewayFilterFactory rewriteResponseHeaderGatewayFilterFactory() {
return new RewriteResponseHeaderGatewayFilterFactory();
}
@Bean
public RewriteLocationResponseHeaderGatewayFilterFactory rewriteLocationResponseHeaderGatewayFilterFactory() {
return new RewriteLocationResponseHeaderGatewayFilterFactory();
}
@Bean
public SetStatusGatewayFilterFactory setStatusGatewayFilterFactory() {
return new SetStatusGatewayFilterFactory();
}
@Bean
public SaveSessionGatewayFilterFactory saveSessionGatewayFilterFactory() {
return new SaveSessionGatewayFilterFactory();
}
@Bean
public StripPrefixGatewayFilterFactory stripPrefixGatewayFilterFactory() {
return new StripPrefixGatewayFilterFactory();
}
@Bean
public RequestHeaderToRequestUriGatewayFilterFactory requestHeaderToRequestUriGatewayFilterFactory() {
return new RequestHeaderToRequestUriGatewayFilterFactory();
}
@Bean
public RequestSizeGatewayFilterFactory requestSizeGatewayFilterFactory() {
return new RequestSizeGatewayFilterFactory();
}
@Bean
public RequestHeaderSizeGatewayFilterFactory requestHeaderSizeGatewayFilterFactory() {
return new RequestHeaderSizeGatewayFilterFactory();
}
@Bean
public GzipMessageBodyResolver gzipMessageBodyResolver() {
return new GzipMessageBodyResolver();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpClient.class)
protected static class NettyConfiguration { // 进行Netty相关配置
protected final Log logger = LogFactory.getLog(getClass());
@Bean
@ConditionalOnProperty(name = "spring.cloud.gateway.httpserver.wiretap")
public NettyWebServerFactoryCustomizer nettyServerWiretapCustomizer(
Environment environment, ServerProperties serverProperties) {
return new NettyWebServerFactoryCustomizer(environment, serverProperties) {
@Override
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers(httpServer -> httpServer.wiretap(true));
super.customize(factory);
}
};
}
@Bean
@ConditionalOnMissingBean
public HttpClient gatewayHttpClient(HttpClientProperties properties,
List<HttpClientCustomizer> customizers) {
HttpClientProperties.Pool pool = properties.getPool();
ConnectionProvider connectionProvider;
if (pool.getType() == DISABLED) {
connectionProvider = ConnectionProvider.newConnection();
}
else if (pool.getType() == FIXED) {
connectionProvider = ConnectionProvider.fixed(pool.getName(),
pool.getMaxConnections(), pool.getAcquireTimeout(),
pool.getMaxIdleTime(), pool.getMaxLifeTime());
}
else {
connectionProvider = ConnectionProvider.elastic(pool.getName(),
pool.getMaxIdleTime(), pool.getMaxLifeTime());
}
HttpClient httpClient = HttpClient.create(connectionProvider)
// TODO: move customizations to HttpClientCustomizers
.httpResponseDecoder(spec -> {
if (properties.getMaxHeaderSize() != null) {
// cast to int is ok, since @Max is Integer.MAX_VALUE
spec.maxHeaderSize(
(int) properties.getMaxHeaderSize().toBytes());
}
if (properties.getMaxInitialLineLength() != null) {
// cast to int is ok, since @Max is Integer.MAX_VALUE
spec.maxInitialLineLength(
(int) properties.getMaxInitialLineLength().toBytes());
}
return spec;
}).tcpConfiguration(tcpClient -> {
if (properties.getConnectTimeout() != null) {
tcpClient = tcpClient.option(
ChannelOption.CONNECT_TIMEOUT_MILLIS,
properties.getConnectTimeout());
}
// configure proxy if proxy host is set.
HttpClientProperties.Proxy proxy = properties.getProxy();
if (StringUtils.hasText(proxy.getHost())) {
tcpClient = tcpClient.proxy(proxySpec -> {
ProxyProvider.Builder builder = proxySpec
.type(ProxyProvider.Proxy.HTTP)
.host(proxy.getHost());
PropertyMapper map = PropertyMapper.get();
map.from(proxy::getPort).whenNonNull().to(builder::port);
map.from(proxy::getUsername).whenHasText()
.to(builder::username);
map.from(proxy::getPassword).whenHasText()
.to(password -> builder.password(s -> password));
map.from(proxy::getNonProxyHostsPattern).whenHasText()
.to(builder::nonProxyHosts);
});
}
return tcpClient;
});
HttpClientProperties.Ssl ssl = properties.getSsl();
if ((ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
|| ssl.getTrustedX509CertificatesForTrustManager().length > 0
|| ssl.isUseInsecureTrustManager()) {
httpClient = httpClient.secure(sslContextSpec -> {
// configure ssl
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
X509Certificate[] trustedX509Certificates = ssl
.getTrustedX509CertificatesForTrustManager();
if (trustedX509Certificates.length > 0) {
sslContextBuilder = sslContextBuilder
.trustManager(trustedX509Certificates);
}
else if (ssl.isUseInsecureTrustManager()) {
sslContextBuilder = sslContextBuilder
.trustManager(InsecureTrustManagerFactory.INSTANCE);
}
try {
sslContextBuilder = sslContextBuilder
.keyManager(ssl.getKeyManagerFactory());
}
catch (Exception e) {
logger.error(e);
}
sslContextSpec.sslContext(sslContextBuilder)
.defaultConfiguration(ssl.getDefaultConfigurationType())
.handshakeTimeout(ssl.getHandshakeTimeout())
.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
});
}
if (properties.isWiretap()) {
httpClient = httpClient.wiretap(true);
}
if (!CollectionUtils.isEmpty(customizers)) {
customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
for (HttpClientCustomizer customizer : customizers) {
httpClient = customizer.customize(httpClient);
}
}
return httpClient;
}
@Bean
public HttpClientProperties httpClientProperties() {
return new HttpClientProperties();
}
@Bean
public NettyRoutingFilter routingFilter(HttpClient httpClient,
ObjectProvider<List<HttpHeadersFilter>> headersFilters,
HttpClientProperties properties) {
return new NettyRoutingFilter(httpClient, headersFilters, properties);
}
@Bean
public NettyWriteResponseFilter nettyWriteResponseFilter(
GatewayProperties properties) {
return new NettyWriteResponseFilter(properties.getStreamingMediaTypes());
}
@Bean
public ReactorNettyWebSocketClient reactorNettyWebSocketClient(
HttpClientProperties properties, HttpClient httpClient) {
ReactorNettyWebSocketClient webSocketClient = new ReactorNettyWebSocketClient(
httpClient);
if (properties.getWebsocket().getMaxFramePayloadLength() != null) {
webSocketClient.setMaxFramePayloadLength(
properties.getWebsocket().getMaxFramePayloadLength());
}
webSocketClient.setHandlePing(properties.getWebsocket().isProxyPing());
return webSocketClient;
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ HystrixObservableCommand.class, RxReactiveStreams.class })
protected static class HystrixConfiguration { // Hystrix相关配置
@Bean
public HystrixGatewayFilterFactory hystrixGatewayFilterFactory(
ObjectProvider<DispatcherHandler> dispatcherHandler) {
return new HystrixGatewayFilterFactory(dispatcherHandler);
}
@Bean
@ConditionalOnMissingBean(FallbackHeadersGatewayFilterFactory.class)
public FallbackHeadersGatewayFilterFactory fallbackHeadersGatewayFilterFactory() {
return new FallbackHeadersGatewayFilterFactory();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Health.class)
protected static class GatewayActuatorConfiguration { // Actuator监控服务配置
@Bean
@ConditionalOnProperty(name = "spring.cloud.gateway.actuator.verbose.enabled",
matchIfMissing = true)
@ConditionalOnAvailableEndpoint
public GatewayControllerEndpoint gatewayControllerEndpoint(
List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
return new GatewayControllerEndpoint(globalFilters, gatewayFilters,
routePredicates, routeDefinitionWriter, routeLocator);
}
@Bean
@Conditional(OnVerboseDisabledCondition.class)
@ConditionalOnAvailableEndpoint
public GatewayLegacyControllerEndpoint gatewayLegacyControllerEndpoint(
RouteDefinitionLocator routeDefinitionLocator,
List<GlobalFilter> globalFilters,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> routePredicates,
RouteDefinitionWriter routeDefinitionWriter, RouteLocator routeLocator) {
return new GatewayLegacyControllerEndpoint(routeDefinitionLocator,
globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter,
routeLocator);
}
}
private static class OnVerboseDisabledCondition extends NoneNestedConditions {
OnVerboseDisabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(name = "spring.cloud.gateway.actuator.verbose.enabled",
matchIfMissing = true)
static class VerboseDisabled {
}
}
}
RouteLocator
1、
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator,
ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
gatewayFilters, properties, configurationService);
}
@Bean
@Primary
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(
new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
2、
package org.springframework.cloud.gateway.route;
import reactor.core.publisher.Flux;
public interface RouteLocator {
Flux<Route> getRoutes();// 此时数据将采用异步返回的形式处理,同时可以获取多个路由信息
}
3、
package org.springframework.cloud.gateway.route;
public class Route implements Ordered {
private final String id; // 路由的标记
private final URI uri; // 路由的访问地址
private final int order; // 顺序,与Ordered有关
private final AsyncPredicate<ServerWebExchange> predicate; // 断言
private final List<GatewayFilter> gatewayFilters; // 网关过滤
private final Map<String, Object> metadata; // 元数据信息
}
4、
package org.springframework.cloud.gateway.route;
@Validated // 【JSR303验证标准】该配置Bean必须验证(后面会有动态路由配置)
public class RouteDefinition {
private String id;
@NotEmpty // 非空验证
@Valid
private List<PredicateDefinition> predicates = new ArrayList<>();
@Valid // 验证
private List<FilterDefinition> filters = new ArrayList<>();
@NotNull
private URI uri;
private Map<String, Object> metadata = new HashMap<>();
private int order = 0;
}
FilteringWebHandler
1、
@Bean
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
return new FilteringWebHandler(globalFilters); // 需要保存全部的全局过滤器
}
2、
package org.springframework.cloud.gateway.handler;
public class FilteringWebHandler implements WebHandler {
protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class);
private final List<GatewayFilter> globalFilters; // 网关过滤器
public FilteringWebHandler(List<GlobalFilter> globalFilters) { // 全局过滤器
this.globalFilters = loadFilters(globalFilters); // 加载全局过滤器
}
private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
// 将GlobalFilter接口的全部子类实例,转为了GatewayFilter实例
return filters.stream().map(filter -> { // Stream处理,此时的filter为GlobalFilter实例
// 创建GatewayFilter的实现子类对象实例(适配器的处理类)
GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
if (filter instanceof Ordered) { // 是否是Ordered接口子类
int order = ((Ordered) filter).getOrder();// 获取执行顺序,数字越小越先执行
return new OrderedGatewayFilter(gatewayFilter, order); // GatewayFilter子类
}
return gatewayFilter;
}).collect(Collectors.toList());// 将最终的处理结果转为List集合返回
}
@Override // ServerWebExchange可以获取到Request/Response对象实例
public Mono<Void> handle(ServerWebExchange exchange) { // WebHandler接口的方法
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR); // 获取路由
List<GatewayFilter> gatewayFilters = route.getFilters();// 获取网关过滤
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters); // 组合过滤器
combined.addAll(gatewayFilters); // 添加网关过滤
AnnotationAwareOrderComparator.sort(combined); // 进行过滤排序
if (logger.isDebugEnabled()) {
logger.debug("Sorted gatewayFilterFactories: " + combined);
}
return new DefaultGatewayFilterChain(combined).filter(exchange); // 过滤链
}
private static class DefaultGatewayFilterChain implements GatewayFilterChain {
private final int index;
private final List<GatewayFilter> filters;
DefaultGatewayFilterChain(List<GatewayFilter> filters) {
this.filters = filters;
this.index = 0;
}
private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
this.filters = parent.getFilters();
this.index = index;
}
public List<GatewayFilter> getFilters() {
return filters;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.defer(() -> {
if (this.index < filters.size()) {
GatewayFilter filter = filters.get(this.index);
DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this,
this.index + 1);
return filter.filter(exchange, chain);
}
else {
return Mono.empty(); // complete
}
});
}
}
private static class GatewayFilterAdapter implements GatewayFilter {
private final GlobalFilter delegate;
GatewayFilterAdapter(GlobalFilter delegate) {
this.delegate = delegate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
sb.append("delegate=").append(delegate);
sb.append('}');
return sb.toString();
}
}
}
RoutePredicateHandlerMapping
1、
@Bean
public RoutePredicateHandlerMapping routePredicateHandlerMapping(
FilteringWebHandler webHandler, RouteLocator routeLocator,
GlobalCorsProperties globalCorsProperties, Environment environment) {
return new RoutePredicateHandlerMapping(webHandler, routeLocator,
globalCorsProperties, environment);
}
2、
package org.springframework.cloud.gateway.handler;
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private final FilteringWebHandler webHandler; // 网关过滤器
private final RouteLocator routeLocator; // 网关路由定位
private final Integer managementPort; // 管理端口
private final ManagementPortType managementPortType; // 端口类型
public RoutePredicateHandlerMapping(FilteringWebHandler webHandler,
RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties,
Environment environment) {
this.webHandler = webHandler;
this.routeLocator = routeLocator;
this.managementPort = getPortProperty(environment, "management.server.");
this.managementPortType = getManagementPortType(environment);
setOrder(1); // 设置执行顺序
setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
}
private ManagementPortType getManagementPortType(Environment environment) {
Integer serverPort = getPortProperty(environment, "server."); // 获取端口内容
if (this.managementPort != null && this.managementPort < 0) {
return DISABLED; // 端口类型的处理
}
return ((this.managementPort == null
|| (serverPort == null && this.managementPort.equals(8080))
|| (this.managementPort != 0 && this.managementPort.equals(serverPort)))
? SAME : DIFFERENT);
}
private static Integer getPortProperty(Environment environment, String prefix) {
return environment.getProperty(prefix + "port", Integer.class); // 处理属性
}
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
// don't handle requests on management port if set and different than server port
if (this.managementPortType == DIFFERENT && this.managementPort != null
&& exchange.getRequest().getURI().getPort() == this.managementPort) {
return Mono.empty();// 端口处理类型是否符合预期,直接返回空
}
exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());
return lookupRoute(exchange) // 查找路由
.flatMap((Function<Route, Mono<?>>) r -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isDebugEnabled()) {
logger.debug(
"Mapping [" + getExchangeDesc(exchange) + "] to " + r);
}
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
return Mono.just(webHandler); // 进行WEB处理
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isTraceEnabled()) {
logger.trace("No RouteDefinition found for ["
+ getExchangeDesc(exchange) + "]");
}
})));
}
@Override
protected CorsConfiguration getCorsConfiguration(Object handler,
ServerWebExchange exchange) {
return super.getCorsConfiguration(handler, exchange);
}
private String getExchangeDesc(ServerWebExchange exchange) {
StringBuilder out = new StringBuilder();
out.append("Exchange: ");
out.append(exchange.getRequest().getMethod());
out.append(" ");
out.append(exchange.getRequest().getURI());
return out.toString();
}
protected Mono<Route> lookupRoute(ServerWebExchange exchange) { // 路由查找
return this.routeLocator.getRoutes() // 通过路由定位器获取全部的路由
.concatMap(route -> Mono.just(route).filterWhen(r -> {
// 对当前的路由进行一些测试处理
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return r.getPredicate().apply(exchange); // 路由的处理
})
.doOnError(e -> logger.error(// 路由错误
"Error applying predicate for route: " + route.getId(),
e))
.onErrorResume(e -> Mono.empty()))
.next()
.map(route -> { // 路由处理
if (logger.isDebugEnabled()) {
logger.debug("Route matched: " + route.getId());
}
validateRoute(route, exchange); // 路由校验
return route; // 返回路由对象
});
}
@SuppressWarnings("UnusedParameters")
protected void validateRoute(Route route, ServerWebExchange exchange) {
}
protected String getSimpleName() {
return "RoutePredicateHandlerMapping";
}
public enum ManagementPortType { // 端口的类型定义
DISABLED, // 服务端口关闭
SAME, // 与应用的端口相同
DIFFERENT; // 与应用的端口不同
}
}
动态路由简介
微服务核心架构
- 微服务架构中引入了网关技术之后,就可以得到如图的完整核心架构,在整个的架构之中网关将成为某一个资源能否被正常调用的关键核心。
动态网关技术
- 在传统的网关开发过程中,所有需要网关代理的资源都必须通过 application.yml 文件进行定义,而这些配置全部都是静态方式实现的,即:当有某一个新的微服务资源上线否则将时,除了要考虑到新资源的服务部署之外,还需要进行微服务网关的重新启动,无法加载到新的资源,而网关的重启过程中势必将影响其他微服务的正常运行而要想解决此类问题就必须引入动态网关技术进行资源管理
动态路由配置实现结构
- 在进行路由设置时需要明确的传递路由的 ID、Predicate、Filter 等配置项,所以一般可以通过一个 JSON 的结构来进行此配置项的定义,而在 SpringCloudGateway 中路由的信息是由 RouteDefinition 类对象定义的,这样就可以直接将 JSON 数据转为 RouteDefinition 实例,随后利用 RouteDefinitionWriter 接口进行配置写入,而要想让动态路由生效,则还必须通过 Spring 事件处理机制发送一“RefreshRoutesEvent”事件,这样才可以将写入的路由信息进行保存。
动态路由模型
动态路由操作
- 动态路由的操作需要通过 RouteDefinitionWriter 接口完成,而该接口主要是通过路由 ID 以及 RouteDefinition 对象实例实现操作。所以为了便于操作的统一性,最佳的做法是创建一个专属的动态路由服务类,并在该类中提供路由数据的增加、修改与删除操作同时为了便于网关数据的管理需要,可以通过 REST 进行操作接口发布这样只需要传,下面通过具体的步骤进行实现。入正确的数据即可进行网关维护,
1、
spring:
application:
name: microcloud.gateway # 网关名称
cloud: # Cloud配置
loadbalancer:
ribbon:
enabled: false # 关闭默认配置
nacos: # Nacos注册中心配置
discovery: # 发现服务
server-addr: nacos-server:8848 # Nacos服务地址
namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
group: MICROCLOUD_GROUP # 一般建议大写
cluster-name: MuyanGateway # 配置集群名称
username: muyan # 用户名
password: yootk # 密码
gateway: # 网关配置
metrics:
enabled: true # 启用服务监控
discovery: # 服务发现
locator: # 资源定位
enabled: false # 取消默认路由配置,默认值就是false
routes: # 定义静态路由
- id: yootk_example # 配置路由ID
uri: https://www.yootk.com/resources # 设置访问路径的匹配
predicates:
- Path=/muyan-yootk # 配置访问路径
- id: forward_example # 配置路由ID
uri: forward:///globalforward # 配置本地转发
predicates:
- Path=/globalforward # 配置访问路径
filters:
- PrefixPath=/gateway/action # 路径前缀
- id: dept # 路由标记
uri: lb://dept.provider # 负载均衡调用
predicates: # 路由谓词工厂
- Path=/** # 匹配全部的路径
filters: # 配置过滤器
- RemoveRequestHeader=Request-Token-Muyan # 删除指定的头信息
- Log=muyan, yootk # 过滤器=NameValueConfig(name属性, value属性)
2、
spring:
application:
name: microcloud.gateway # 网关名称
cloud: # Cloud配置
loadbalancer:
ribbon:
enabled: false # 关闭默认配置
nacos: # Nacos注册中心配置
discovery: # 发现服务
server-addr: nacos-server:8848 # Nacos服务地址
namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
group: MICROCLOUD_GROUP # 一般建议大写
cluster-name: MuyanGateway # 配置集群名称
username: muyan # 用户名
password: yootk # 密码
gateway: # 网关配置
metrics:
enabled: true # 启用服务监控
discovery: # 服务发现
locator: # 资源定位
enabled: false # 取消默认路由配置,默认值就是false
3、
package com.yootk.gateway.service;
import javassist.NotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@Service
@Slf4j
public class DynamicRouteService implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter; // 路由数据的写入
private ApplicationEventPublisher publisher; // 事件发布器
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher; // 保存事件发布器
}
public boolean add(RouteDefinition definition) { // 追加新的路由配置
log.info("增加路由配置项,新的路由ID为:{}", definition.getId()); // 日志输出
try {
this.routeDefinitionWriter.save(Mono.just(definition)).subscribe(); // 配置写入
this.publisher.publishEvent(new RefreshRoutesEvent(this)); // 发布路由事件
} catch (Exception e) {
e.printStackTrace();
log.error("路由增加失败,增加的路由ID为:{}", definition.getId());
return false;
}
return true;
}
public Mono<ResponseEntity<Object>> delete(String id) { // 根据id删除数据
log.info("删除路由配置项,删除的路由ID为:{}", id); // 日志输出
return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> {
return Mono.just(ResponseEntity.ok().build());
})).onErrorResume((t) -> {
return t instanceof NotFoundException;
}, (r) -> {
return Mono.just(ResponseEntity.notFound().build());
});
}
public boolean update(RouteDefinition definition) { // 修改已有的路由配置
log.info("更新路由配置项,新的路由ID为:{}", definition.getId()); // 日志输出
try {
this.delete(definition.getId()); // 根据ID删除已有路由
this.routeDefinitionWriter.save(Mono.just(definition)).subscribe(); // 配置写入
this.publisher.publishEvent(new RefreshRoutesEvent(this)); // 发布路由事件
} catch (Exception e) {
log.error("路由更新失败,增加的路由ID为:{}", definition.getId());
return false;
}
return true;
}
}
4、
package com.yootk.gateway.action;
import com.yootk.gateway.service.DynamicRouteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/routes/*") // 访问父路径
public class DynamicRouteAction { // 动态路由
@Autowired
private DynamicRouteService dynamicRouteService; // 路由业务对象
@PostMapping("add")
public Boolean add(@RequestBody RouteDefinition definition) {
return this.dynamicRouteService.add(definition);
}
@DeleteMapping("delete/{id}")
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
return this.dynamicRouteService.delete(id);
}
@PostMapping("update")
public Boolean update(@RequestBody RouteDefinition definition) {
return this.dynamicRouteService.update(definition);
}
}
5、
gateway-9501:9501/routes/add
6、
{
"id": "dept",
"uri": "lb://dept.provider",
"order": 1,
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/**"
}
}
],
"filters": [
{
"name": "AddRequestHeader",
"args": {
"_genkey_0": "Request-Token-Muyan",
"_genkey_1": "www.yootk.com"
}
}
]
}
7、
gateway-9501:9501/routes/update
{
"id": "dept",
"uri": "lb://dept.provider",
"order": 1,
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/provider/dept/list"
}
}
],
"filters": [
{
"name": "AddRequestHeader",
"args": {
"_genkey_0": "Request-Token-Muyan",
"_genkey_1": "www.yootk.com"
}
}
]
}
8、
gateway-9501:9501/routes/delete/dept
9、
gateway-9501:9090/actuator/gateway/routes
动态路由配置持久化
Nacos 网关数据持久化
- 网关数据除了可以动态配置之外,还需要进行有效的持久化管理,这样才可以保证在每次服务启动之后网关配置数据不会丢失,同时在进行数据持久化配置时,还需要充分的考虑持久化配置数据发生改变时,网关也要及时进行更新处理,所以最佳的做法就是通过 Nacos 进行数据存储
1、
[
{
"id": "dept",
"uri": "lb://dept.provider",
"order": 1,
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/**"
}
}
],
"filters": [
{
"name": "AddRequestHeader",
"args": {
"_genkey_0": "Request-Token-Muyan",
"_genkey_1": "www.yootk.com"
}
}
]
}
]
2、
project(":gateway-9501") { // 网关模块
dependencies {
implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery') {
exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖
}
implementation(libraries.'nacos-client') // 引入与当前的Nacos匹配的依赖库
implementation('org.springframework.cloud:spring-cloud-starter-gateway') // 网关依赖
implementation('org.springframework.boot:spring-boot-starter-actuator') // Actuator依赖库
implementation('org.springframework.cloud:spring-cloud-starter-loadbalancer')
implementation(libraries.'caffeine')
implementation(libraries.'micrometer-registry-prometheus')
implementation(libraries.'micrometer-core')
}
}
3、
package com.yootk.gateway.config;
import com.alibaba.nacos.api.PropertyKeyConst;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Properties;
@Component
@Data
@ConfigurationProperties(prefix = "spring.cloud.nacos.discovery")
public class GatewayNacosConfig { // 自定义配置存储类
private String serverAddr;
private String namespace;
private String group;
private String username;
private String password;
private String dataId = "gateway.config";
private long timeout = 2000;
public Properties getNacosProperties() {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, this.serverAddr);
properties.put(PropertyKeyConst.NAMESPACE, this.namespace);
properties.put(PropertyKeyConst.USERNAME, this.username);
properties.put(PropertyKeyConst.PASSWORD, this.password);
return properties;
}
}
4、
package com.yootk.gateway.listener;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yootk.gateway.config.GatewayNacosConfig;
import com.yootk.gateway.service.DynamicRouteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
@Component
@Slf4j
public class GatewayNacosRouteListener implements CommandLineRunner {
@Autowired
private DynamicRouteService dynamicRouteService; // 设置业务层处理
@Autowired
private GatewayNacosConfig nacosConfig; // Nacos服务配置
// 因为Nacos里面保存的数据类型是JSON数据,所以需要对JSON进行解析,直接使用Jackson组件了
@Autowired
private ObjectMapper mapper; // 获取Jackson组件
@Override
public void run(String... args) throws Exception {
this.nacosDynmaicRouteListener();// 启动时加载配置
}
public void nacosDynmaicRouteListener() { // 动态路由监听
try {
ConfigService configService = NacosFactory.createConfigService(this.nacosConfig.getNacosProperties());
String content = configService.getConfig(this.nacosConfig.getDataId(), this.nacosConfig.getGroup(), this.nacosConfig.getTimeout()); // 获取指定的配置项
log.info("【网关启动】读取Nacos网关配置项:{}", content); // 日志输出
GatewayNacosRouteListener.this.setRoute(content); // 路由配置
configService.addListener(this.nacosConfig.getDataId(), this.nacosConfig.getGroup(), new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
log.info("【网关更新】读取Nacos网关配置项:{}", configInfo); // 日志输出
GatewayNacosRouteListener.this.setRoute(configInfo);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void setRoute(String configInfo) { // 定义路由处理
try { // 将读取到的数据内容转为路由的配置定义,本操作是由Jackson组件完成的
RouteDefinition[] routes = this.mapper.readValue(configInfo, RouteDefinition[].class);
for (RouteDefinition route : routes) {
this.dynamicRouteService.update(route); // 业务更新
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5、
[
{
"id": "dept",
"uri": "lb://dept.provider",
"order": 1,
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/provider/dept/list"
}
}
],
"filters": [
{
"name": "AddRequestHeader",
"args": {
"_genkey_0": "Request-Token-Muyan",
"_genkey_1": "www.yootk.com"
}
}
]
}
]
demo