Spring Cloud Gateway是Spring Cloud的一个全新项目,基于Spring 5.0+Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。SpringCloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。Spring Cloud Gateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。Spring Cloud GateWay 使用的是WebFlux中的reactor-netty响应式编程组件,底层使用了Neety通讯框架。
路由Route
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
断言Predicate
开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
过滤Filter
指的是Spring框架中GateWayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改
工作流程
客户端向Spring Cloud GateWay发送请求。然后在GateWay Handler Mapping 中找到与请求相匹配的路由,将其发送到GateWay Web Handler。GateWay Web Handler再通过指定的过滤器链来将请求发送到实际的服务执行业务逻辑然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(pre)或之后(post)执行业务逻辑
Filter在pre类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等
Filter在post类型的过滤器可以做响应内容、修改响应头、日志的输出、流量监控等
引入依赖
注意不要导入web和actuator的依赖,否则会报错Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at thistime. Please remqve spring-boot-starter-web dependency.
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
dependencies>
配置文件
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes:
- id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
#匹配后提供服务的路由地址
uri: http://localhost:8001
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
#- After=2017-01-20T17:42:47.789-07:00[America/Denver]
#- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
#- Cookie=username,zzyy
#- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数
#- Host=**.atguigu.com
#- Method=GET
#- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由
# 过滤
#filters:
# - AddRequestHeader=X-Request-red, blue
- id: payment_route2
uri: http://localhost:8001
predicates:
Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
主启动类
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
配置文件方式
spring.cloud.gateway.routes[0].id=service-edu
spring.cloud.gateway.routes[0].uri=lb://service-edu
spring.cloud.gateway.routes[0].predicates=Path=/eduservice/**
spring.cloud.gateway.routes[1].id=service-oss
spring.cloud.gateway.routes[1].uri=lb://service-oss
spring.cloud.gateway.routes[1].predicates=Path=/eduoss/**
routes:
- id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
#匹配后提供服务的路由地址
uri: http://localhost:8001
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
JavaConfig方式
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
// 访问localhost:9527/guonei会转发到http://news.baidu.com/guonei
RouteLocator path_route = builder.routes()
.route("path_route", r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return path_route;
}
}
默认情况下GateWay会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由转发,从而实现动态路由动能
GateWay的uri的协议为lb,表示启用GateWay的负载均衡功能
第一步:开启从注册中心动态创建路由的功能,利用微服务名称进行路由
spring:
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
第二步:lb创建负载均衡uri
routes:
- id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
#匹配后提供服务的路由地址
uri: lb://CLOUD-PAYMENT-SERVICE
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
跨域:浏览器对于javascript的同源策略的限制 。
以下情况都属于跨域:
跨域原因说明 | 示例 |
---|---|
域名不同 | www.jd.com 与 www.taobao.com |
域名相同,端口不同 | www.jd.com:8080 与 www.jd.com:8081 |
二级域名不同 | item.jd.com 与 miaosha.jd.com |
如果域名和端口都相同,但是请求路径不同,不属于跨域,如:
www.jd.com/item
www.jd.com/goods
http和https也属于跨域
为什么有跨域问题?
跨域不一定都会有跨域问题。
因为跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。
因此:跨域问题 是针对ajax的一种限制。
但是这却给我们的开发带来了不便,而且在实际生产环境中,肯定会有很多台服务器之间交互,地址和端口都可能不同,怎么办?
全局配置类实现跨域问题
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。Spring Cloud Gateway包含许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合。Spring Cloud Gateway创建Route对象时,使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋给Route。Spring Cloud Gateway包含许多内置的Route Pred Factories。所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and
总的来说,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理
After Route Predicate:Before和Between使用一样
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
# 在这个时间段之后才能访问
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
Cookie Route Predicate
Cookie Route Predicate需要两个参数,一个是 Cookie name ,一个是正则表达式。路由规则会通过获取对应的Cookie name值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
# 请求中的cookie要有username属性并且为xiaoyixiao0628
- Cookie=username, xiaoyixiao0628
Header Route Predicate
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
# 请求头要有X-Request-Id属性并且值为整数的正则表达式
- Header=X-Request-Id, \d+
Host Route Predicate
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
# 请求头要有Host属性并且满足**.somehost.org,**.anotherhost.org
- Host=**.somehost.org,**.anotherhost.org
Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个ant分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。
Method Route Predicate
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
# 请求方法必须是GET或者POST
- Method=GET,POST
Path Route Predicate
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
# 访问地址为:ip地址:端口号/red/{segment}才能访问
- Path=/red/{segment}
Query Route Predicate
支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式。
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
RemoteAddr Route Predicate
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
# 如果请求的远端地址是192.168.1.10,则此路由匹配。
- RemoteAddr=192.168.1.1/24
Weight Route Predicate
这条路线将把80%的流量转发到weighthigh.org, 20%的流量转发到weighlow.org
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway内置了多种路由过滤器,都由GateWayFilter的工厂类来产生。
Filter的声明周期:Pre请求之前和POST请求之后
常用过滤器https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
全局过滤器https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters
自定义全局过滤器
实现发送请求时候没带uname参数网关拦截非法用户
第一步:实现两个接口org.springframework.cloud.gateway.filter.GlobalFilter
, org.springframework.core.Ordered
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("***** 进入全局过滤器 *****" + new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname == null) {
log.info("***** 用户名为null,非法用户 *****");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}