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-starterwebflux 而不是 spring-boot-start-web
应用启动类添加注解
@EnableDiscoveryClient
编写
application.yml
server :port : 8070spring :application :name : alibaba-gateway-8070cloud :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());
}
}