1.什么是SpringCloudGateway?
Spring Cloud Gateway 网关是SpringCloud官方提供的;
原来有一个Zuul网关,是Netflix公司提供的,现在已经不维护了,后面Netflix公司又出来了一个Zuul2.0网关,但由于一直没有发布稳定版本,所以springcloud等不及了就自己推出一个网关,已经不打算整合zuul2.0了;
Spring Cloud Gateway 项目提供了一个用于在Spring MVC之上构建API网关的库,Spring Cloud Gateway旨在提供一种简单而高效的方法来将请求路由到API,并为它们提供跨领域的关注,例如:安全性,监视/度量和弹性等;
2.SpringCloudGateway有什么功能特性?
建立在Spring Framework 5,Project Reactor和Spring Boot 2.0之上;
能够匹配任何请求属性上的路由;
谓词和过滤器特定于路由;
Hystrix断路器集成;
Spring Cloud DiscoveryClient的集成;
易于编写的谓词和过滤器;
请求速率限制;
路径改写
3.SpringCloudGateway核心概念
- 路由:网关的基本组成部分,由一组路由配置组成。单个路由配置包含id、uri、谓词集合、过滤器集合组成。如果维词校验结果为true则住转发,否则拦截请求。
- 谓词:这是Java 8函数谓词,输入类型是Spring Framework ServerWebExchange,可以匹配HTTP请求中的所有内容,例如请求头或参数;
- 过滤器:这些是使用特定工厂构造的Spring Framework GatewayFilter实例,可以在发送给下游请求之前或之后修改请求和响应;
4.简单的网关demo
- 新建网关服务并设置依赖
<artifactId>alan-gateway</artifactId>
<name>alan-gateway</name>
<description>测试网关服务,不需要spring-web服务</description>
<dependencies>
<!--spring-cloud-starter-gateway 网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
- 配置applicaytion.yml文件
server:
port: 9999
spring:
application:
name: alan-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.144.100:80
gateway:
discovery:
locator:
# 启用DiscoveryClient网关集成,实现服务发现功能
enabled: true
# 路由集合
routes:
# 路由id
- id: route0
# 资源地址 lb表示开启负载均衡
uri: lb://consumer
predicates:
- Path=consumer/**
- 启动网关及consumer服务,请求网关。网关将请求转发到consumer服务上。
5.SpringCloudGateway的工作原理
6.谓词工厂
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分,Spring Cloud Gateway包括许多内置的路由谓词工厂,所有这些谓词都与HTTP请求的不同属性匹配,可以将多个路由谓词工厂结合使用;
总共有11个路由谓词工厂:
- The After Route Predicate Factory
- The Before Route Predicate Factory
- The Between Route Predicate Factory
- The Cookie Route Predicate Factory
- The Header Route Predicate Factory
- The Host Route Predicate Factory
- The Method Route Predicate Factory
- The Path Route Predicate Factory
- The Query Route Predicate Factory
- The RemoteAddr Route Predicate Factory
- The Weight Route Predicate Factory
- After路由谓词工厂:After route谓词工厂采用一个参数,即datetime(这是一个Java ZonedDateTime),该谓词匹配在指定日期时间之后发生的请求。
- Before路由谓词工厂:Before路由谓词工厂采用一个参数,即datetime(这是一个Java ZonedDateTime),该谓词匹配在指定日期时间之前发生的请求
- Between路由谓词工厂:路由谓词之间的工厂使用两个参数datetime1和datetime2,它们是java ZonedDateTime对象,该谓词匹配在datetime1之后和datetime2之前发生的请求,datetime2参数必须在datetime1之后
- Cookie 路由谓词工厂:Cookie路由谓词工厂采用两个参数,即cookie名称和一个regexp(这是Java正则表达式),该谓词匹配具有给定名称且其值与正则表达式匹配的cookie
- Header 路由谓词工厂:header 路由谓词工厂使用两个参数,header 名称和一个regexp(这是Java正则表达式),该谓词与具有给定名称的header 匹配,该header 的值与正则表达式匹配
- Host 路由谓词工厂:host路由谓词工厂使用一个参数:主机名模式列表
- Method 路由谓词工厂:方法路由谓词工厂使用方法参数,该参数是一个或多个参数:要匹配的HTTP方法
- Path路由谓词工厂:路径路由谓词工厂使用两个参数:Spring PathMatcher模式列表和一个称为matchOptionalTrailingSeparator的可选标志
- Query路由谓词工厂:查询路由谓词工厂采用两个参数:必需的参数和可选的regexp(这是Java正则表达式)
- RemoteAddr 路由谓词工厂:RemoteAddr路由谓词工厂使用源列表(最小大小为1),这些源是标记(IPv4或IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是IP地址,而16是子网掩码))
- RemoteAddr 路由谓词工厂:权重路由谓词工厂采用两个参数:group和weight(一个int),权重是按组计算的。
7.GatewayFilter工厂
路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应,Spring Cloud Gateway包括许多内置的GatewayFilter工厂;
8.自定义谓词
TokenConfig
public class TokenConfig
{
private String token;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
TokenRoutePredicateFactory名字必须是XXXX+RoutePredicateFactory
@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenConfig> {
public TokenRoutePredicateFactory() {
super(TokenConfig.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("token");
}
@Override
public Predicate<ServerWebExchange> apply(TokenConfig tokenConfig) {
// (T t) -> true
return exchange -> {
MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();
boolean flag = false;
List<String> list = new ArrayList<>();
valueMap.forEach((k, v) -> {
list.addAll(v);
});
for (String s : list) {
if (StringUtils.equalsIgnoreCase(s, tokenConfig.getToken())) {
flag = true;
break;
}
}
return flag;
};
}
}
谓词配置:
spring:
application:
name: alan-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.144.100:80
gateway:
discovery:
locator:
# 启用DiscoveryClient网关集成,实现服务发现功能
enabled: true
# 路由集合
routes:
# 路由id
- id: route0
# 资源地址 lb表示开启负载均衡
uri: lb://consumer
predicates:
- Token=123456
9.自定义过滤器
过滤器分为全局过滤器和普通过滤器,全局过滤器不需要在路由中显式配置自动生效。普通过滤器需要在路由中配置后才能生效。
- 普通过滤器 继承AbstractNameValueGatewayFilterFactory
@Component
public class RequestLogGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return (ServerWebExchange exchange, GatewayFilterChain chain)->{
MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();
valueMap.forEach((k,v)->{
System.out.println(k);
System.out.println("==========================");
v.forEach(s->{
System.out.println("*****"+s+"******");
});
});
//继续下一个filter
return chain.filter(exchange);
};
}
}
- 全局过滤器 实现 GlobalFilter, Ordered
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
MultiValueMap<String, String> map = exchange.getRequest().getQueryParams();
System.out.println("进入全局过滤器,无需显式配置");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
10.集成Ribbon实现负载均衡
实现原理是在全局LoadBalancerClientFilter中进行拦截,然后再该过滤器中依赖LoadBalancerClient loadBalancer,而此负载均衡接口的具体实现是RibbonLoadBalancerClient,即spring cloud gateway已经整合好了ribbon,已经可以实现负载均衡,我们不需要做任何工作,网关对后端微服务的转发就已经具有负载均衡功能;uri: lb//consumer
11.集成Sentinel
- 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- sentinel-spring-cloud-gateway-adapter -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.7.2</version>
</dependency>
- 添加配置项dashboard
server:
port: 9999
spring:
application:
name: alan-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.144.100:80
gateway:
discovery:
locator:
# 启用DiscoveryClient网关集成,实现服务发现功能
enabled: true
# 路由集合
routes:
# 路由id
- id: route0
# 资源地址 lb表示开启负载均衡
uri: lb://consumer
predicates:
- Token=123456
filters:
- RequestLog=prefix,gateway
sentinel:
transport:
dashboard: 127.0.0.1:8080
eager: true
- 启用SentinelGatewayFilter,配置SentinelGatewayBlockHandler并注入自定义限流Handler
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(BlockRequestHandler myBlockRequestHandler) {
// Register the block exception handler for Spring Cloud Gateway.
//重定向bloack处理
// GatewayCallbackManager.setBlockHandler(new RedirectBlockRequestHandler("http://www.baidu.com"));
//自定义bloack处理
GatewayCallbackManager.setBlockHandler(myBlockRequestHandler);
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
/**
* 自定义的BlockRequestHandler
*
* @return
*/
@Bean(name = "myBlockRequestHandler")
public BlockRequestHandler myBlockRequestHandler() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
return ServerResponse.status(HttpStatus.BAD_GATEWAY)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject("服务器太热了,它罢工了~" + throwable.getClass()));
}
};
return blockRequestHandler;
}
12.处理跨域问题
什么叫跨域?
协议、ip(域名)、端口有一个不一样就是跨域请求。
添加配置类
/**
* 配置网关跨域cors请求支持
*
*/
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*"); //是什么请求方法,比如 GET POST PUT DELATE .....
config.addAllowedOrigin("*"); //来自哪个域名的请求,*号表示所有
config.addAllowedHeader("*"); //是什么请求头
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}