API网关
- 网关路由必须支持负载均衡,服务列表是从注册中心拉取的
- 客户端发出请求的URL指向的是网关,URL还必须要包含目标信息
- 网关收到URL,通过一定的规则,要能识别出交给哪个实例去处理
- 网关有能力对请求响应进行修改
引入依赖包
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
</dependencies>
配置启动类
@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApplication {
public static void main(String[] args) {
SpringApplication.run(GateWayApplication.class, args);
}
}
编写application.yaml的配置文件
server:
port: 8341
Spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: 192.168.200.130:8848
namespace: ce6fa22e-fc35-4fd3-9568-2cf609059088
group: JIAGOU_GROUP
file-extension: yaml
prefix: ${Spring.application.name}
heart-beat-interval: 5
gateway:
discovery:
locator:
enabled: true #启用DiscoveryClient的标志
routes:
- id: course-service-route # 路由的唯一标识 http://localhost:8341/course/api/courses/1
uri: lb://course-agg-service #网关收到请求后将请求转发到 lb://course-agg-service
predicates:
- Path=/course/** # 断言匹配请求路径
filters: # 过滤器,可以做认证、限流、日志等
- StripPrefix=1 # 针对URL剪切前缀,这里去掉了前缀/course
主要概念:
- routes:路由定义了匹配请求的条件,以及如何将请求转发到下游服务
- predicates:用于匹配请求的条件,比如请求路径、请求参数、头部信息等。
- filter:过滤器,进行模式匹配
路由谓词工厂
- After=2017-01-20T17:42:47.789-07:00[America/Denver] # 在某个时间之后
- Before=2017-01-20T17:42:47.789-07:00[America/Denver] #在某个时间之前
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] # 两者之间
- Cookie=chocolate, ch.p #cookie谓词工厂,此谓词匹配具有给定名称且其值与正则表达式匹配的 Cookie。
- Header=X-Request-Id, \d+
- Host=**.somehost.org,**.anotherhost.org
- Method=GET,POST
- Path=/red/{segment},/blue/{segment}
- Query=green
- RemoteAddr=192.168.1.1/24
Filter工厂
- AddRequestHeader:向请求添加一个 HTTP 头。
- AddResponseHeader:向响应添加一个HTTP 头。
- DedupeResponseHeader:去除响应头中的重复值。
- PrefixPath:为请求路径添加前缀。
- PreserveHostHeader:保留原始请求的 Host 头。
- RedirectTo:重定向请求到另一个 URL。章
- RemoveRequestHeader:移除请求中的一个 HTTP 头。
- RemoveResponseHeader:移除响应中的一个HTTP 头。
- RewritePath:重写请求路径。
- SetPath:设置请求路径。
- SetRequestHeader:设置请求头。
- SetResponseHeader:设置响应头。
- SetStatus:设置响应状态码。
- StripPrefix:去除请求路径中的前缀
1网关全局过滤器
@Component
@Order(1)
@Slf4j
public class LoggingGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
long startTime = System.currentTimeMillis();
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
//Mono.fromRunnable此时所有的请求都处理完成后才执行
ServerHttpResponse response = exchange.getResponse();
long endTime = System.currentTimeMillis();
log.info("请求地址:{},请求方法:{},响应状态码:{},响应时间:{}",request.getURI()
,request.getMethod()
,response.getStatusCode().value(),endTime-startTime);
}));
}
}
2实现认证过滤器
在微服务中,服务数量较多,不能再服务中进行认证,会进行统一的认证,认证成功后颁发Token,前端会将令牌放在RequestHeader中
创建一个对所有的请求进行认证的全局过滤器
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AuthenticationGlobalFilter implements GlobalFilter {
// 认证头
private static final String AUTHENTICATION_HEADER = "Authorization";
// 登录URL
private static final String LOGIN_URL = "/api/login";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if(exchange.getRequest().getPath().toString().contains(LOGIN_URL)){
return chain.filter(exchange);
}
String token = exchange.getRequest().getHeaders().getFirst(AUTHENTICATION_HEADER);
if(token == null || IsValid(token)){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// 请求结束
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
private boolean IsValid(String token) {
//此处通过工具类校验
return true;
}
}
3网关的限流
此处集成了redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
自定义一个Config类:但要注意此处的限流策略只能选择一种,也就是说@Bean只能生效一个方法
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 RateLimiterConfiguration {
/**
* 使用ip地址作为限流
* @return
*/
@Bean
public KeyResolver ipKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
/**
* 使用用户id作为限流,需要在请求中携带userId参数
* @return
*/
//@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
/**
* 返回一个KeyResolver对象,用于解析请求的路径
*
* @return KeyResolver路径解析器,通过请求对象获取路径值
*/
//@Bean
public KeyResolver pathResolver() {
// Lambda表达式用于创建KeyResolver的实例
// Mono.just确保返回一个包含请求路径的Mono Flux
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
}
在application.yaml文件中修改策略配置
server:
port: 8341
Spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: 192.168.200.130:8848
namespace: ce6fa22e-fc35-4fd3-9568-2cf609059088
group: JIAGOU_GROUP
file-extension: yaml
prefix: ${Spring.application.name}
heart-beat-interval: 5
gateway:
discovery:
locator:
enabled: true #启用DiscoveryClient的标志
routes:
- id: course-service-route # 路由的唯一标识 http://localhost:8341/course/api/courses/1
uri: lb://course-agg-service #网关收到请求后将请求转发到 lb://course-agg-service
predicates:
- Path=/course/** # 断言匹配请求路径
filters: # 过滤器,可以做认证、限流、日志等
- StripPrefix=1 # 针对URL剪切前缀,这里去掉了前缀/course
- name: RequestRateLimiter # 定义限流策略
args:
redis-rate-limiter.replenishRate: 1 # 每秒填充的令牌数
redis-rate-limiter.burstCapacity: 2 # 令牌桶的容量
key-resolver: "#{@ipKeyResolver}" # 限流key的生成策略
redis:
host: 43.143.110.136
port: 6379
database: 0
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 8 # 连接池中的最大空闲连接
password: 1234
当请求数量大于令牌个数时,触发限流策略,状态码显示429