简介
Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
特征
基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
动态路由
Predicates 和 Filters 作用于特定路由
集成 Hystrix 断路器
集成 Spring Cloud DiscoveryClient
易于编写的 Predicates 和 Filters
限流
路径重写
1.demo编写,依赖于springboot添加依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
</dependencies>
编写启动类:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
//path可以采用表达式
.path("/api/{segment}", "/api1/{segment}")
.filters(f -> f.addRequestHeader("user", "dandan"))
.uri("http://localhost:8088")
)
.build();
}
}
这里采用bean方式将需要代理的router配置进去,访问gateway接口 http://localhost:8080/api/hello
,则转向目标服务:http://localhost:8088/api/hello
2.深入测试predicate
(1)before, after, between 配置
before: 在设定的时间之前能够访问router
after: 在设定的时间之后能够访问router
between: 在时间区域之间能够访问router
PS: 时间设定值需要包含时区
spring:
cloud:
gateway:
routes:
- id: hello_route0
uri: http://localhost:8088/api/hello
predicates:
- Path=/api/{segment},/api1/{segment}
- After=2021-03-21T17:42:47.789-07:00[Asia/Shanghai]
- Before=2021-03-26T17:42:47.789-07:00[Asia/Shanghai]
- Between=2021-03-21T17:42:47.789-07:00[Asia/Shanghai], 2021-03-26T17:42:47.789-07:00[Asia/Shanghai]
(2)添加cookie:
spring:
cloud:
gateway:
routes:
- id: hello_route0
uri: http://localhost:8088/api/hello
predicates:
- Path=/api/{segment},/api1/{segment}
- Cookie=chocolate, ch.p
(3) 添加header,并且header支持正则表达式
spring:
cloud:
gateway:
routes:
- id: hello_route0
uri: http://localhost:8088/api/hello
predicates:
- Path=/api/{segment},/api1/{segment}
- Header=X-Request-Id, \d+
(4)配置method, 但是method值只能是post或get,不能两个都写上
spring:
cloud:
gateway:
routes:
- id: hello_route0
uri: http://localhost:8088/api/hello
predicates:
- Path=/api/{segment},/api1/{segment}
- Method=GET
(5)path匹配: - Path=/red/{segment},/blue/{segment},path的值可以配置多个路径
spring:
cloud:
gateway:
routes:
- id: hello_route0
uri: http://localhost:8088/api/hello
predicates:
- Path=/api/{segment},/api1/{segment}
(6)参数匹配,当配置上query时,请求必须带上参数才能访问该route: - Query=red
spring:
cloud:
gateway:
routes:
- id: hello_route0
uri: http://localhost:8088/api/hello
predicates:
- Path=/api/{segment},/api1/{segment}
- Query=red
(7)权重设置:
spring:
cloud:
gateway:
routes:
- id: hello_route0
uri: http://localhost:8088/api/hello
predicates:
- Path=/api/{segment},/api1/{segment}
- Weight=group1,8
3.spring-cloud-gateway-mvc模块代码解析主要功能,依托于spring-MVC,
将请求信息封装到proxyExchange中,通过proxyExchange。
forward方法跳转到真是方法上比较重要的类
1.配置类ProxyResponseAutoConfiguration:
/**
*在web项目项目启动时,加载该类
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@ConditionalOnClass({ HandlerMethodReturnValueHandler.class })
@EnableConfigurationProperties(ProxyProperties.class)
public class ProxyResponseAutoConfiguration implements WebMvcConfigurer {
@Autowired
private ApplicationContext context;
//将配置信息分装到ProxyProperties类型proxy的对象中
//properties = { "spring.cloud.gateway.proxy.auto-forward=Baz,Baz1" }
@Bean
@ConditionalOnMissingBean
public ProxyExchangeArgumentResolver proxyExchangeArgumentResolver(Optional<RestTemplateBuilder> optional,
ProxyProperties proxy) {
RestTemplateBuilder builder = optional.orElse(new RestTemplateBuilder());
RestTemplate template = builder.build();
template.setErrorHandler(new NoOpResponseErrorHandler());
template.getMessageConverters().add(new ByteArrayHttpMessageConverter() {
@Override
public boolean supports(Class<?> clazz) {
return true;
}
});
//初始化resolver
ProxyExchangeArgumentResolver resolver = new ProxyExchangeArgumentResolver(template);
resolver.setHeaders(proxy.convertHeaders());
resolver.setAutoForwardedHeaders(proxy.getAutoForward());
resolver.setSensitive(proxy.getSensitive()); // can be null
return resolver;
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(context.getBean(ProxyExchangeArgumentResolver.class));
}
private static class NoOpResponseErrorHandler extends DefaultResponseErrorHandler {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
}
}
(2)ProxyExchangeArgumentResolver中有一个非常重要的方法resolveArgument
通过实现HandlerMethodArgumentResolver接口,来实现resolveArgument方法,这个方法主要是返回ProxyExchange对象
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
ProxyExchange<?> proxy = new ProxyExchange<>(rest, webRequest, mavContainer, binderFactory, type(parameter));
proxy.headers(headers);
if (this.autoForwardedHeaders.size() > 0) {
proxy.headers(extractAutoForwardedHeaders(webRequest));
}
if (sensitive != null) {
proxy.sensitive(sensitive.toArray(new String[0]));
}
return proxy;
该方法在InvocableHandlerMethod类中的invokeForRequest方法中会用到,需要用到resolve返回的proxy, 进行方法匹配,跳转到目标路径:
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//该方法获取到的就是proxyExchange对象
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//将proxyExchange对象传入到doInvoke中,进行真实跳转
return doInvoke(args);
}
跳转到测试方法之后,通过proxy去路由到真实的服务:
@GetMapping("/forward/**")
public void forward(ProxyExchange<?> proxy) throws Exception {
String path = proxy.path("/forward");
if (path.startsWith("/special")) {
proxy.header("X-Custom", "FOO");
path = proxy.path("/forward/special");
}
proxy.forward(path);
}
真实的路由跳转目标方法是依赖于spring的mapping:
public void forward(String path) {
HttpServletRequest request = this.webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = this.webRequest.getNativeResponse(HttpServletResponse.class);
try {
request.getRequestDispatcher(path).forward(new BodyForwardingHttpServletRequest(request, response),
response);
}
catch (Exception e) {
throw new IllegalStateException("Cannot forward request", e);
}
「人生在世,留句话给我吧」