什么是网关?
????????网关实质上是一个网络通向其他网络的 IP 地址,是当前微服务项目的"统一入口"。
网关能做什么?
????????反向代理 、鉴权、 流量控制、 熔断、?日志监控等
图片原文:http://t.csdnimg.cn/SvUJh
核心概念
Router(路由):路由时构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
Predicate(断言):断言说简单点,就是请求匹配条件。断言是定义匹配条件,如果请求符合条件,则该请求匹配断言所属的路由。
Spring Cloud Gateway 网关的搭建:
<!--网关jar-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>${spring-cloud-alibaba-version}</version>
</dependency>
<!--引入nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-cloud-alibaba-version}</version>
</dependency>
<!--注意我这里没有引入父工程,而是springboot父启动依赖,也不要引入web依赖而是webflux-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
server:
port: 8070
spring:
application:
name: alibaba-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:3333 #注册服务到nacos,注意nacos的端口号配置
gateway:
discovery:
locator:
enabled: true #表明gateway开启服务注册和发现的功能,gateway为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务。
lower-case-service-id: true #请求路径的服务g名改为小写
自定义路由
server:
port: 8070
spring:
application:
name: alibaba-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:3333 #注册服务到nacos,注意nacos的端口号配置
gateway:
discovery:
locator:
enabled: false #表明gateway开启服务注册和发现的功能,gateway为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务。
lower-case-service-id: true #请求路径的服务g名改为小写
routes: l
- id: user-consumer #唯一 路由id
uri: http://127.0.0.1:7780 #使用http:IP+服务端口 或lb://alibaba-user-consumer-8090 也可以,lb为负载均衡
predicates: # 断言
- Path=/ts/** # 路径需携带ts
filters:
- StripPrefix=1 #路由去掉前缀信息 使用内置过滤器
路由过滤器
请求头过滤器配置示例(局部过滤器)
有时候SpringCloudGateWay提供的过滤器工厂不能满足自己的要求。?,可能有时候需要在过滤时做一些其它的逻辑操作。 那么这时候可以选择使用Java代码自定义全局过滤器。
yml配置如图:
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Author
* @Date 2023/12/25 11:11
* @Description:
*/
//@Component
//@Order(1)
public class GateWayFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token=exchange.getRequest().getHeaders().getFirst("token");
System.out.println("token1====="+token);
if ("123".equals(token)){
return chain.filter(exchange);
}
System.out.println("错误,不放行");
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
return exchange.getResponse().setComplete();
}
}
这里请求头携带的token为123时才允许放行。
当有多个过滤器时,Order的值决定了过滤器的执行顺序。
数值越大优先级越低, 负的越多, 优先级越高。
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Author
* @Date 2023/12/25 11:31
* @Description:
*/
//@Component
public class GateWayFilter1 implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return null;
}
@Override
public int getOrder() {
return 2;
}
}
spring:
cloud:
gateway:
globalcors: # 全局的跨域配置
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
# options请求 就是一种询问服务器是否浏览器可以跨域的请求
# 如果每次跨域都有询问服务器是否浏览器可以跨域对性能也是损耗
# 可以配置本次跨域检测的有效期maxAge
# 在maxAge设置的时间范围内,不去询问,统统允许跨域
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 允许在请求中携带cookie
maxAge: 360000 # 本次跨域检测的有效期(单位毫秒)
# 有效期内,跨域请求不会一直发option请求去增大服务器压力
方式二:使用编码方式定义配置类
@Configuration
public class CorsConfig {
private static final String MAX_AGE = "18000L";
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
// 使用SpringMvc自带的跨域检测工具类判断当前请求是否跨域
if (!CorsUtils.isCorsRequest(request)) {
return chain.filter(ctx);
}
HttpHeaders requestHeaders = request.getHeaders();
// 获取请求头
ServerHttpResponse response = ctx.getResponse();
// 获取响应对象
HttpMethod requestMethod =
requestHeaders.getAccessControlRequestMethod(); // 获取请求方式对象
HttpHeaders headers = response.getHeaders();
// 获取响应头
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,
requestHeaders.getOrigin()); // 把请求头中的请求源(协议+ip+端口)添加到响应头中(相当于yml中的allowedOrigins)
headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
requestHeaders.getAccessControlRequestHeaders());
if (requestMethod != null) {
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,
requestMethod.name()); // 允许被响应的方法(GET/POST等,相当于yml中的allowedMethods)
}
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
// 允许在请求中携带cookie(相当于yml中的allowCredentials)
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
// 允许在请求中携带的头信息(相当于yml中的allowedHeaders)
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
// 本次跨域检测的有效期(单位毫秒,相当于yml中的maxAge)
if (request.getMethod() == HttpMethod.OPTIONS) {
// 直接给option请求反回结果
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
return chain.filter(ctx);
// 不是option请求则放行
};
}
}
网关限流
<!--redis 限流-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
@Configuration
public class KeyResolverConfiguration {
@Bean
public KeyResolver ipKeyResolver(){
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
};
}
}
修改yml中的配置项,指定限制流量的配置以及redis的配置
routes:
- id: cloud-user-consumer
uri: http://127.0.0.1:7780
predicates: # 断言
- Path=/ts/** # 路径需携带ts
filters:
- StripPrefix=1 #路由去掉前缀信息 使用内置过滤器
- AddRequestHeader=token,123 #添加请求头
- name: RequestRateLimiter #请求数限流 名字不能随便写
args:
key-resolver: "#{@ipKeyResolver}" #用于限流的键的解析器的 Bean 对象的名字。它使用SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
redis-rate-limiter.burstCapacity: 1 #令牌桶总容量。