Spring Cloud Gateway入门学习
Spring Cloud Gateway是Spring Cloud的一个项目,它是基于Spring,Webflux,Spring boot等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式,目标为替换Netflix Zuul项目,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标和限流。
Spring Cloud Gateway的主要特征:
- 基于Spring Framework 5,SpringBoot2.0
- 动态路由
- Predicates和Filters作用于特定路由
- 集成Hystrix断路器
- 集成SpringCloud DiscoveryClient
- 易于编写的Predicates和Filters
- 限流
- 路径重写
基本概念:
Route(路由):这是网关的基本构建块。它由一个ID,一个目标uri,一组断言和一组过滤器定义。如果断言成功,则路由匹配。
Predicate(断言):这是一个java8的Predicate。输入类型是一个ServerWebExchange。我们可以使用它来匹配来自HTTP请求的任何内容,例如headers或参数。
Filter(过滤器):我们可以使用 它来修改请求和参数。
一,基本使用
1.1 引用依赖
springboot版本2.4.5,springcloud版本2020.0
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.0.1</version>
</dependency>
1.2 启动类
package com.lmc.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication5010 {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication5010.class, args);
}
}
1.3 配置文件
server:
port: 5010
spring:
application:
name: tools-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: tools-task
uri: lb://tools-task
predicates:
- Path=/tltk
filters:
- StripPrefix=1
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
spring.cloud.gateway.routes.id:自定义的路由ID,保持唯一
spring.cloud.gateway.routes.id.uri:目标服务地址
spring.cloud.gateway.routes.id.uri: lb//XXXX(服务名):配置服务
spring.cloud.gateway.routes.id.predicates:路由条件,predicates接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方式来将predicate组合成其他复杂的逻辑
spring.cloud.gateway.routes.id.filters StripPrefix=n:去掉n个路径
spring.cloud.gateway.discovery.locator.enabled:是否根据注册中心路由,随服务的改变而改变路由
spring.cloud.gateway.discovery.locator.lower-case-service-id:是否强制将服务名转为小写
1.4 测试
现服务tools-task接口: http://localhost:8083/test/api
先启动服务注册中心,再启动gateway,再启动tools-task,访问: http://localhost:5010/tools-task/test/api,成功访问数据
二, 过滤器
? 在Spring Cloud Gateway中,过滤器的作用主要是为对请求与返回进行处理,可以简单理解为一个方法的前置事件与后置事件,所以一般过滤器被分为两种,分别为“pre”和“post”。在“pre”类型的过滤器可以做参数校验,权限校验,流量监控,日志输出,协议转换等,在“post”类型的过滤器中可以做响应内容,响应头的修改,日志输出,流量监控等。
过滤器执行过程中,order越大,优先级越低
2.1 常用过滤器
- AddRequestHeader:为请求增加一个header
- AddRequestParameter:为请求新增请求参数
- AddResponseHeader:为响应新增返回header
- DedupeResponseHeader:为去除返回信息中重复的header信息
- ……
2.2 自定义过滤器
自定义过滤器,需要实现GatewayFilter和Ordered接口
举例: 请求完成时间过滤器
package com.lmc.filter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class ElapsedFilter implements GatewayFilter, Ordered {
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
exchange.getAttributes().put("beginTime", System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute("beginTime");
if (startTime != null) {
System.out.println(exchange.getRequest().getURI().getRawPath() + " : " + (System.currentTimeMillis() - startTime) + "ms");
}
})
);
}
}
然后将该过滤器绑定到路由中:
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.path("/lmchh/**")
.filters(f -> f.stripPrefix(1)
.filter(new ElapsedFilter()))
.uri("http://localhost:5020")
.order(0))
.build();
}
2.3 全局过滤器
当需要所有请求都要经过某些操作时,可以使用全局过滤器。全局过滤器需要实现GlobalFilter接口
package com.lmc.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class GlobalFilter implements org.springframework.cloud.gateway.filter.GlobalFilter, Ordered {
@Override
public int getOrder() {
return -10;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("this is global filter ... start ...");
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
System.out.println("this is global filter ... end ...");
})
);
}
}
三, Gateway功能
3.1 认证
举例,认证过滤器:
package com.lmc.filter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
@Component
public class TokenAuthenticationFilter extends AbstractGatewayFilterFactory {
private static final String Bearer_ = "Bearer ";
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder mutate = request.mutate();
ServerHttpResponse response = exchange.getResponse();
try {
String header = request.getHeaders().getFirst("Authorization");
if (header == null || !header.startsWith(Bearer_)) {
throw new RuntimeException("请求头中Authorization的信息为空");
}
String token = header.substring(7);
if(!StringUtils.isEmpty(token)) {
System.out.println("校验通过");
mutate.header("userDetails", token).build();
}else {
System.out.println("token无效");
return null;
}
} catch (Exception e) {
e.printStackTrace();
}
ServerHttpRequest build = mutate.build();
return chain.filter((ServerWebExchange) exchange.mutate().request(build));
};
}
}
配置:
filters:
- StripPrefix=1
- TokenAuthenticationFilter
3.2 熔断
熔断降级:在分布式系统中,网关作为流量的入口,大量请求进入网关,向后端远程系统或服务发起调用,后端服务不可避免的会产生调用失败(超时或者异常),失败时不能让请求堆积在网关上,需要快速失败并返回回去,这就需要在网关上做熔断,降级操作。
所需依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
配置:
filters:
- StripPrefix=1
- name: Hystrix
args:
name: defaultfallback
fallbackUri: 'forward:/defaultfallback'
举例,熔断回调:
package com.lmc.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
@RestController
public class DefaultHystrixController {
@RequestMapping("/defaultfallback")
@HystrixCommand(commandProperties= {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public Map<String, Object> defaultfallback() {
System.out.println("默认降级操作...");
Map<String, Object> map = new HashMap<String, Object>();
map.put("resultCode", "fail");
map.put("resultMessage", "服务异常...");
map.put("returnObj", null);
return map;
}
}
创建接口测试:
设置3秒内没响应就进行熔断降级
@RequestMapping("/timeout")
public String timeout() {
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
return "timeout";
}
设置/timeout映射睡眠5秒,引发熔断机制。
访问 /timeout ,结果调用了/defaultfallback。
3.3 限流
限流:网关上有大量请求,对指定服务进行限流,可以很大程度上提高服务的可用性与稳定性,限流的目的是通过对并发访问/请求进行限速,或对一个时间窗口内的请求进行限速来保护系统。一旦达到限制速率则可以拒绝服务,排队或等待,降级。
集成限流:Spring Cloud Gateway默认集成了Redis限流,可以对不同服务做不同维度的限流,如:IP限流,用户限流,接口限流。(需提前开启Redis)
举例,新建限流配置类,通过KeyResolver来指定限流的key
package com.lmc.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
@Configuration
public class RateLimiterConfig {
@Bean(value = "remoteAddrKeyResolver")
public KeyResolver remoteAddrKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
限流方式:
配置类
spring:
application:
name: api-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: provider
uri: lb://api-eureka-provider
predicates:
- Path=/provider/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: defaultfallback
fallbackUri: 'forward:/defaultfallback'
- name: RequestRateLimiter
args:
key-resolver: '#{@remoteAddrKeyResolver}'
redis-rate-limiter.replenishRate: 2
redis-rate-limiter.burstCapacity: 2
注意:
filter名称必须是RequestRateLimiter,
redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求
redis-rete-limiter.burstCapacity:令牌桶的容量,允许在一秒内完成的最大请求数。
key-resolver:使用SpEL按名称引用bean
3.4 动态路由
动态路由的内容较多,今天做笔记已经做到十一点半,下篇再讲好了。
…
|