官网地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
说到路由,想必各位一定最先想到的就是家里的路由器了,那么我们家里的路由器充当的是一个什么角色呢?
我们知道,如果我们需要连接互联网,那么就需要将手机或是电脑连接到家里的路由器才可以,而路由器则连接光猫,光猫再通过光纤连接到互联网,也就是说,互联网方向发送过来的数据,需要经过路由器才能到达我们的设备。而路由器充当的就是数据包中转站,所有的局域网设备都无法直接与互联网连接,而是需要经过路由器进行中转,我们一般说路由器下的网络是内网,而互联网那一端是外网。
我们的局域网设备,无法被互联网上的其他设备直接访问,肯定是能够保证到安全性的。并且互联网发送过来的数据,需要经过路由器进行解析,识别到底是哪一个设备的数据包,然后再发送给对应的设备。
而我们的微服务也是这样,一般情况下,可能并不是所有的微服务都需要直接暴露给外部调用,这时我们就可以使用路由机制,添加一层防护,让所有的请求全部通过路由来转发到各个微服务,并且转发给多个相同微服务实例也可以实现负载均衡。
在之前,路由的实现一般使用 Zuul,但是已经停更,而现在新出现了由 SpringCloud 官方开发的Gateway 路由,它相比 Zuul 不仅性能上得到了一定的提升,并且是官方推出,契合性也会更好,所以我们这里就主要讲解 Gateway。
回到目录…
现在我们来创建一个新的项目 gateway-server,作为我们的网关,这里需要添加两个依赖:
<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>
dependencies>
第一个依赖就是网关的依赖,而第二个则跟其他微服务一样,需要注册到 Eureka 才能生效,注意不要添加 Web 依赖,使用的是 WebFlux
框架。
我们来完善一下配置文件:设置端口、注册到 Eureka 中
server:
port: 8500
eureka:
client:
service-url:
defaultZone: http://localhost:8801/eureka, http://localhost:8802/eureka
spring:
application:
name: gateway
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
现在还没有配置任何的路由功能,我们接着将路由功能进行配置,给刚刚的配置文件添加配置项:
spring:
application:
name: gateway
cloud:
gateway:
routes: # 配置路由,注意这里是个列表,每一项都包含了很多信息
- id: borrow-service # 路由名称
uri: lb://borrowservice # 路由的地址,lb表示使用负载均衡到微服务,也可以使用 http 正常转发
predicates: # 路由规则,断言什么请求会被路由
- Path=/borrow/** # 只要是访问的这个路径,一律都被路由到上面指定的服务
路由规则的详细列表(断言工厂列表)在这里:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories,可以指定多种类型,包括指定时间段、Cookie 携带情况、Header 携带情况、访问的域名地址、访问的方法、路径、参数、访问者IP 等。也可以使用配置类进行配置,但是还是推荐直接配置文件,省事。
此时依然可以通过原有的服务地址进行访问:http://localhost:8082/borrow/2
我们现在也可以直接通过路由来访问我们的服务了:http://localhost:8500/borrow/2
这样我们就可以将不需要外网直接访问的微服务全部放到内网环境下,而只依靠网关来对外进行交涉。
回到目录…
路由过滤器支持以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应,路由过滤器的范围是某一个路由,跟之前的断言一样,Spring Cloud Gateway 也包含许多内置的路由过滤器工厂。
详细列表:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
比如,我们现在希望在请求到达时,在请求头中添加一些信息再转发给我们的服务,那么这个时候就可以使用路由过滤器来完成,我们只需要对配置文件进行修改:
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: borrow-service
uri: lb://borrowservice
predicates:
- Path=/borrow/**
# 继续添加新的路由配置,这里就以书籍管理服务为例
- id: book-service
uri: lb://bookservice
predicates:
- Path=/book/**
filters: # 添加过滤器
- AddRequestHeader=Test, HelloBook!
# AddRequestHeader 就是添加请求头信息,其他工厂请查阅官网
接着我们在 BookController 中打印一下请求头的日志,看看是不是成功添加了:
@Slf4j
@RestController
public class BookController {
@Resource
private BookService bookService;
@GetMapping("/book/{bid}")
public Book findBookById(@PathVariable("bid") int bid, HttpServletRequest req) {
log.info("book-service 的请求头: " + req.getHeader("Test"));
return bookService.getBookById(bid);
}
}
当我们不通过 Gateway 访问我们的图书管理服务时:就没有请求头。
现在我们通过 Gateway 访问我们的图书管理服务时:就成功获取到网关添加的请求头了。
回到目录…
除了针对于某一个路由配置过滤器之外,我们也可以自定义全局过滤器,它能够作用于全局。
比如我们要实现拦截没有携带指定请求参数的请求:
①自定义全局过滤器类,实现 GlobalFilter
接口,重写 filter()
方法,实现具体过滤策略。
@Slf4j
@Component // 需要注册为Bean
public class TestFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 先获取 ServerHttpRequest 对象,注意不是 HttpServletRequest
ServerHttpRequest request = exchange.getRequest();
// 打印一下所有的请求参数
log.info("请求参数: " + request.getQueryParams());
// 判断是否包含 val 参数,且参数值为 1
List<String> value = request.getQueryParams().get("val");
if(value != null && value.contains("1")) {
// 将 ServerWebExchange 向过滤链的下一级传递(跟 JavaWeb 中介绍的过滤器其实是差不多的)
return chain.filter(exchange);
}else {
// 直接在这里拦截,然后返回响应
return exchange.getResponse().setComplete();
}
}
}
②当然,过滤器肯定是可以存在很多个的,所以我们可以手动指定过滤器之间的顺序:
优于
单独的路由过滤器执行。配置文件中的单个路由过滤器 Order 值:
filters: # 过滤器
- AddRequestHeader=Test, HelloBook! # Order = 1
- AddRequestHeadersIfNotPresent=X-Request-Color-1:blue # Order = 2
- AddRequestParameter=red, blue # Order = 3
全局路由过滤器指定 Order 值: 实现 Ordered
接口,默认为 0
@Component
public class TestFilter implements GlobalFilter, Ordered { // 实现 Ordered 接口
@Override
public int getOrder() {
return 0;
}
}
回到目录…
总结:
提示:这里对文章进行总结:
本文是对SpringCloud的学习,学习了部署 Gateway路由网关,并且在配置文件中设置单个路由过滤器,以及通过自定义类实现全局过滤器的方法。之后的学习内容将持续更新!!!