Spring Cloud Gateway网关

1.什么是网关

存在问题:
1. 如果添加鉴权功能,需要对每一个服务进行改造
2. 跨域问题需要对每一个服务进行改造
网关就是当前微服务项目的 " 统一入口 "

2.GeteWay能做什么 

 

 

3.核心概念 

Router( 路由 )
路由时构建网关的基本模块,它由 ID ,目标 URI ,一系列的断言和过滤器组成,如果断言为 true 则匹配
该路由
Predicate( 断言 )
断言说简单点,就是请求匹配条件。断言是定义匹配条件,如果请求符合条件,则该请求匹配断言所属
的路由
Filter( 过滤 )
指的是 Spring 框架中 GatewayFilter 的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修
改。

4. Gateway搭建

搭建 gateway 示例
创建 springboot 项目,引入 jar
<!--注意我这里没有引入父工程,而是springboot父启动依赖,也不要引入web依赖而是webflux-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!--网关jar-->
<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>
<!--客户端负载均衡loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
Spring cloud gateway 是基于 webflux 的,如果非要 web 支持的话需要导入 spring-boot-starter
webflux 而不是 spring-boot-start-web
应用启动类添加注解 @EnableDiscoveryClient
编写 application.yml
server :
  port : 8070
spring :
  application :
    name : alibaba-gateway-8070
  cloud :
    nacos :
      discovery :
        server-addr : 127.0.0.1 : 3333 # 注册服务
    gateway :
      discovery :
        locator :
          enabled : true # 表明 gateway 开启服务注册和发现的功能, ateway 为每一个服务创建了一
router ,这个 router 将以服务名开头的请求路径转发到对应的服务。
          lower-case-service-id : true # 请求路径的服务名改为小写

 

启动项目,输入路径 ip+gateway 端口号 + 要请求的服务名 + 地址
如: http://127.0.0.1:8070/alibaba-user-consumer-8090/user/findUserById?id=3
测试成功!!!!
注意:新版本无法使用,权限不足,需要配置自定义路由。
自定义路由
修改 yml 文件信息
注意 yml 格式 空格 关闭 gateway 开启服务注册和发现的功能 否则自定义不生效
routes 后面的路由可以配置多个,相当于配置个数组,一个 - 开头的配置就是其中的一个数组元
路由主要有四个配置:
路由 id id
路由目标( uri
路由断言( predicates ):判断路由的规则,
路由过滤器( filters ):对请求或响应做处理
再次启动项目 输入路径: ip+gateway 端口号 +ts+ 路径
http://127.0.0.1:8070/ts/user/findUserById?id=3
路由过滤器
客户端请求先找到路由,路由匹配时经过过滤器层层筛选,最终访问到微服务。
请求头过滤器配置示例(局部过滤器)
filters: # 过滤器配置
- AddRequestHeader=token, test # 添加请求头
给所有进入 user-consumer 的请求添加一个请求头。
请求头的 key token value test
默认过滤器配置示例(全局过滤器)
default - filters 的配置和 routes 平级。
只要配置在 default - filters 下面的过滤器,会对 routes 配置的所有路由都生效。
自定义全局路由过滤器
有时候 SpringCloudGateWay 提供的过滤器工厂不能满足自己的要求。
可能有时候需要在过滤时做一些其它的逻辑操作。
那么这时候可以选择使用 java 代码自定义全局过滤器。
创建 GateWayFilter 实现接口 GlobalFilter Ordered

 

 

@Component
public class GateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain
chain) {
// 1.获取请求参数
//1.这里的request并不是servlet中的request
//2.返回值是一个多键的map集合、也就是说这个map集合的键可以重复
MultiValueMap<String, String> params =
exchange.getRequest().getQueryParams();
// 2.获取userName参数
String id = params.getFirst("id");
System.out.println("id===="+id);
// 3.校验
if ("3".equals(id)) {
// 放行
return chain.filter(exchange);
}
// 4.拦截
// 4.1.禁止访问,设置状态码
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);//状态码
// 4.2.结束处理
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return 0;
}
}
当有多个过滤器时, Order 的值决定了过滤器的执行顺序。
Ordered 无法在 GateWayFilter 中获得 token ,建议使用注解 Order
数值越大优先级越低, 负的越多, 优先级越高。
设置 Order 的值有两种方式:
1. 实现 Ordered 接口,并且重写 getOrder 方法
2. 使用 @Order 注解
@Order(0)
@Component
public class GateWayFilter implements GlobalFilter {
}
网关的跨域问题
使用 CORS 方式。
CORS 是一个 W3C 标准,全称是 " 跨域资源共享 " Cross-origin resource sharing )。
它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。
方式一:配置 application.yml 文件:

 方式二:使用编码方式定义配置类:

@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请求则放行
};
}
}

5.网关限流

网关可以做很多的事情,比如,限流,当我们的系统 被频繁的请求的时候,就有可能 将系统压垮,所
以 为了解决这个问题,需要在每一个微服务中做限流操作,但是如果有了网关,那么就可以在网关系统
做限流,因为所有的请求都需要先通过网关系统才能路由到微服务中。
令牌桶算法是比较常见的限流算法之一,大概描述如下:
所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
根据限流大小,设置按照一定的速率往桶里添加令牌;
桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之
后,将令牌直接删除;
令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保
证足够的限流
网关限流代码实现
需求:每个 ip 地址 1 秒内只能发送 1 次请求,多出来的请求返回 429 错误。
spring cloud gateway 默认使用 redis RateLimter 限流算法来实现。所以我们要使用首先需要引
redis 的依赖
<!--redis 限流-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

 

定义 KeyResolver
在启动类添加如下代码, KeyResolver 用于计算某一个类型的限流的 KEY 也就是说,可以通过
KeyResolver 来指定限流的 Key
//定义一个KeyResolver
@Bean
public KeyResolver ipKeyResolver() {
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return
Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
};
}
修改 application.yml 中配置项,指定限制流量的配置以及 redis 的配置

 

解释:
burstCapacity :令牌桶总容量。
replenishRate :令牌桶每秒填充平均速率。
key - resolver :用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据 #
{@beanName} Spring 容器中获取 Bean 对象。
启动项目,快速刷新,请求失败就会报错 429
其他限流规则
参数限流: key - resolver: "#{@parameterKeyResolver}"
@Bean
public KeyResolver parameterKeyResolver()
{
return exchange ->
Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
URI 限流 : key - resolver: "#{@pathKeyResolver}"
/**
* 限流规则配置类 多个bean时其中一个bean添加@Primary注解
*/
@Configuration
public class KeyResolverConfiguration
{
@Bean
public KeyResolver pathKeyResolver()
{
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值