网关是干啥用的就不用再说了。 sringcloud中的网关,第一代是zuul,但是性能比较差(1.x是阻塞式的,2.x是基于Netty的),然后有了第二代GateWay,基于Reactor模型 异步非阻塞。
springcloud网关就是一系列的filter,在请求到达真实服务的前后,进行拦截处理。
GateWay核⼼逻辑:路由转发+执⾏过滤器链
客户端向GateWay发出请求,然后在GateWay Handler Mapping中 找到与请求相匹配的路由,将其发送到GateWay Web Handler;Web Handler再通过指定的过滤器链来将请求发送到我们实际的服务执⾏业务逻辑,然后返回。
Filter 在 “pre” 类型过滤器中可以做参数校验、权限校验、流量监控、⽇志输出、协议转换等,在“post” 类型的过滤器中可以做响应内容、响应头的修改、⽇志的输出、流量监控等。
GateWay搭建使用:
引入依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-commons</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--GateWay 网关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--引入webflux--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <!--日志依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </dependency> <!--测试依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--lombok工具--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.4</version> <scope>provided</scope> </dependency> <!-- Actuator可以帮助你监控和管理Spring Boot应用--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
yml配置:
server: port: 9002 eureka: client: serviceUrl: # eureka server的路径 defaultZone: http://lagoucloudeurekaservera:8761/eureka/,http://lagoucloudeurekaserverb:8762/eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server 可以同步注册表 instance: #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip) prefer-ip-address: true #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@ spring: application: name: lagou-cloud-gateway cloud: gateway: routes: # 路由可以有多个 - id: service-autodeliver-router # 我们自定义的路由 ID,保持唯一 #uri: http://127.0.0.1:8096 # 目标服务地址 自动投递微服务(部署多实例) 动态路由:uri配置的应该是一个服务名称,而不应该是一个具体的服务实例的地址 #uri以 lb: //开头(lb代表从注册中⼼获取服务) uri: lb://lagou-service-autodeliver # gateway网关从服务注册中心获取实例信息然后负载后路由 predicates: # 断言:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默 认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。 - Path=/autodeliver/** - id: service-resume-router # 我们自定义的路由 ID,保持唯一 #uri: http://127.0.0.1:8081 # 目标服务地址 #http://localhost:9002/resume/openstate/1545132 #http://127.0.0.1:8081/openstate/1545132 uri: lb://lagou-service-resume predicates: # 断言:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默 认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。 - Path=/resume/** filters: - StripPrefix=1
注意:上面yml中有个配置:- StripPrefix=1,就是指定了一个StripPrefix过滤器,它的作用是除去url中的第1个前缀。 假如url为 /resume/test/xxx,真实访问到服务的url就没有/resume了。 另外,还有一个过滤器PrefixPath作用相反,如果配置: - PrefixPath=/user,那么真实访问的url会在最前面加上/user。
GateWay路由规则:
内置了很多 Predicates功能,实现了各种路由匹配规则(通过 Header、请求参数等作为条件)匹配到对应的路由。
下面来具体看看每一种如何配置:
- 时间点后匹配(请求时间在该时间点后的):
spring: cloud: gateway: routes: - id: after_route uri: https://example.org predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver]
- 时间点前匹配:
spring: cloud: gateway: routes: - id: before_route uri: https://example.org predicates: - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
- 时间区间匹配:
spring: cloud: gateway: routes: - id: between_route uri: https://example.org predicates: - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2018-01-20T17:42:47.789-07:00[America/Denver]
- 指定cookie匹配(cookie中有authToken的):
spring: cloud: gateway: routes: - id: cookie_route uri: https://example.org predicates: - Cookie=authToken
- 指定header匹配:
spring: cloud: gateway: routes: - id: header_route uri: https://example.org predicates: - Header=X-Request-Id
- 指定host匹配:
spring: cloud: gateway: routes: - id: host_route uri: https://example.org predicates: - Host=Host=**.somehost.org,**.anotherhost.org
- 指定method匹配:
spring: cloud: gateway: routes: - id: method_route uri: https://example.org predicates: - Method=GET,POST
- 请求路径匹配:
spring: cloud: gateway: routes: - id: path_route uri: https://example.org predicates: - Path=/red/{segment},/blue/{segment}
- 请求包含某参数匹配:
spring: cloud: gateway: routes: - id: query_route uri: https://example.org predicates: - Query=green
- 远程地址匹配:
spring: cloud: gateway: routes: - id: remoteaddr_route uri: https://example.org predicates: - RemoteAddr=192.168.1.1/24
- 动态路由-就是除了gateway自带的,我们可以自己定义,就像上面yml文件中那样
作用时机划分:pre:前置过滤器,请求到达服务之前作用。我们可利⽤这种过滤器实现身份验证、在集群中选择 请求的微服务、记录调试信息等。post:后置过滤器,请求从服务返回之后作用。这种过滤器可⽤来为响应添加标准的 HTTP Header 、收集统计信息和指标、将响应从微服务发送给客户端等。作用范围划分:GateWayFilter:应用到单个路由。GlobalFilter:应用到所有路由。GlobalFilter用的最多,下面自定义一个全局过滤器(实现GlobalFilter即可):当请求过来时,判断发送请求的客户端的 ip ,如果在⿊名单中,拒绝访问。package com.lagou.edu.filter; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.List; /** * 定义全局过滤器,会对所有路由生效 */ @Slf4j @Component // 让容器扫描到,等同于注册了 public class BlackListFilter implements GlobalFilter, Ordered { // 模拟黑名单(实际可以去数据库或者redis中查询) private static List<String> blackList = new ArrayList<>(); static { blackList.add("0:0:0:0:0:0:0:1"); // 模拟本机地址 } /** * 过滤器核心方法 * @param exchange 封装了request和response对象的上下文 * @param chain 网关过滤器链(包含全局过滤器和单路由过滤器) * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 思路:获取客户端ip,判断是否在黑名单中,在的话就拒绝访问,不在的话就放行 // 从上下文中取出request和response对象 ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); // 从request对象中获取客户端ip String clientIp = request.getRemoteAddress().getHostString(); // 拿着clientIp去黑名单中查询,存在的话就决绝访问 if(blackList.contains(clientIp)) { // 决绝访问,返回 response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码 log.debug("=====>IP:" + clientIp + " 在黑名单中,将被拒绝访问!"); String data = "Request be denied!"; DataBuffer wrap = response.bufferFactory().wrap(data.getBytes()); return response.writeWith(Mono.just(wrap)); } // 合法请求,放行,执行后续的过滤器 return chain.filter(exchange); } /** * 返回值表示当前过滤器的顺序(优先级),数值越小,优先级越高 * @return */ @Override public int getOrder() { return 0; } }
过滤器定义好后,无须其他配置。 只要被spring容器扫描到成为一个bean,即可生效。