统一网关Gateway
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
简介
为什么需要网关呢?
我们之前的项目完成了多个微服务的搭建,将它们都注册到了Nacos配置中心,并且在nacos的配置中心做了统一的配置管理,了解了bootstrap的配置文件等,并且我们还对各个微服务之间引入了feign,是他们的调用更加清晰明确。
但是,现在有个问题,我们的微服务是不是都暴露给任何人了呢?我们的请求数量要是太多了,是不是就会摧毁我们的微服务呢?
因此,网关就出现了
我们来看看网关的功能吧.
1.身份的验证和权限校验
这能限制一些人的访问,使得微服务并不是暴露给任何人
2.服务路由,负载均衡
使得当外人访问这个网关的时候,比如说需要访问orderservice,网关就会路由到这个服务,在多个orderservice的集群中,可以选择相对空闲的那个,实现负载均衡
3.请求限流
举个例子,如果现在有2000个请求涌进来,但是整个微服务只能承受500个,这时候先流的作用就是更好的保护整个微服务
下面我们看一张图,来了解整体的架构(draw.io画的图)
总结:网关就是保护膜
技术实现
在SpringCloud的网关实现中包括这两种:
1.gateway
2.zuul
这里我们用非阻塞式的SpringCloudGateway
搭建网关Gateway
1.创建一个gateway的模块,引入nacos服务注册的依赖和gateway的依赖
<!-- nacos服务发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2.之后写个启动类
3.注册gateway服务到nacos中,配置路由
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: user-service # 路由标识
uri: lb://userservice # 路由的目标地址
predicates: # 路由断言,判断请求是否符合规则
- Path=/user/** # 路径断言,判断是否符合/user开头,如果是,则符合
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
注意,这里的网关本质上也是个微服务,它负责转发请求。
这里的断言限定了:如果访问localhost:10010/user他就会帮忙转发到http://userservice/user上,也就是http://localhost:8081/user上
lb则代表loadbalance,它会自动帮你负载均衡
路由断言工厂Route Predicate Factory
我们在配置文件里面写的断言规则只是字符串,这些字符串会被Predicate Factoy读取并且处理,转变为路由判断的条件
例如:Path=/user/**是按照路径匹配的。它是由断言工厂的一个类来处理的
像这样的断言工厂,SpringCloudGateway还有十几个
这个网站上有特别详细的案例描述,可以根据自己的选择去限制转发
比如说我们试一下官网上的这样一个配置,意思是,只有17年前才能访问
然后我们发现了404,配置生效!
路由统一过滤器 GatewayFilter
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做相应的处理
简单来说,就是对请求和响应做些请求头添加啊,类似的操作。
那么具体是做什么呢?
直接上官网链接
tmd,30多个过滤器,都在这个文章里面,你难道要一个一个去学吗,除非你是傻子,这些,用到的时候再看就可以,和上面的断言工厂一样,用到的时候再看即可
我们从官网上看一个吧
比如第一个,那个filter就是加一个X-Request-Foo的请求头到下流的微服务上,key是X-Request-Foo,value是Bar
案例出发
给所有的userservice去添加一个请求头:Truth=wanghaotian is a handsome man
实现方式:
修改yml文件
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: user-service # 路由标识
uri: lb://userservice # 路由的目标地址
predicates: # 路由断言,判断请求是否符合规则
- Path=/user/** # 路径断言,判断是否符合/user开头,如果是,则符合
filters:
- AddRequestHeader=Truth, wanghaotian is a handsomeboy # 改动的地方
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
然后修改一下controller
用@RequestHeader接收请求头,然后打印,我们重启一下然后访问
打印成功
全局过滤器 global Filter
全局过滤器的作用和GatewayFilter的作用相同,都是处理一切进入网关的请求
但是,你有没有想过,gatewayfilter的东西,是不是已经写死了?它没办法自定义!他处理的逻辑很固定
而GlobalFilter的逻辑需要自己写代码去实现
定义方式是实现GlobalFilter
下面我们来通过一个案例讲解:
需求:定义全局过滤器,拦截请求,判断请求的参数是否满足以下条件:
1.参数中是否有authorization
2.authorization参数值是否为admin
同时满足就放行
我们定义一个AuthorizeFilter类实现GlobalFilter
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> params = request.getQueryParams();
//2.获取参数中的authorization参数
String auth = params.getFirst("authorization");
//3.判断是否等于admin
if("admin".equals(auth)){
//放行给下一个拦截器,相当于通过了拦截器
return chain.filter(exchange);
}
//否就拦截,且设置未认证
ServerHttpResponse response = exchange.getResponse();
//设置响应状态码是401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
注意,这里的order是拦截器的优先级,越小优先级越高,需要实现Ordered 接口,或者加上@Order(-1)的注解
写完了以后,我们先发一个没有authorization的请求,果然
出现了我们设置的响应码401
当我们访问http://localhost:10010/user/1?authorization=admin的时候,成功访问了!
过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由过滤器(Path:/user/**),DefaultFilter,GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器。
也许有人会有疑问,这三个过滤器,类型都不一样,还能放到一个集合当中?并且还排序?
我们不妨先点进去看看,
原来我们发现,路由过滤器和default过滤器是同一类,都是gatewayfilter
那GlobalFilter呢?
原来这里使用了适配器的设计模式,所有的GlobalFilter都可以被适配成gatewayfilter
因此,网关里面所有的类型都是gatewayfilter。
还有一个问题,我们在globalfilter里面制定了order值用来排序,可是路由过滤器和default过滤器都没有指定,那么他们又是怎么进行排序的呢?
原来路由过滤器和default过滤器的order值由spring指定,默认按照声明顺序由1递增
当过滤器的order值都一样的时候,会按照defaultFilter>路由过滤器>GlobalFilter的顺序去执行
网关的跨域问题处理
什么是跨域?
其实很简单,就是前端请求的网址和前端自己的网址的域名不一样,或者端口不一样
但是我们之前8080口的user服务为什么可以调用8081口的order服务呢?
其实我们又可以对跨域做一个定义的再完善:即浏览器禁止请求的发起者与服务端跨域ajax请求,请求会被浏览器拦截。
我们前端一直用的anxios,底层也是ajax噢
解决方案:CORS(浏览器问服务器给不给跨域)
网关处理同样采取CORS方案,并且只需要简单配置即可
globalcors:
add-to-simple-url-handler-mapping: true # 解决预检请求被拦截
cors-configurations:
'[/**]': # 对于一切请求拦截处理
allowedOrigins: # 允许访问的源
- "http://localhost:8090"
- "https://www.leyou.com"
allowedMethods:# 允许访问的类型
- "GET"
- "POST"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许一切请求头
allowCredentials: true # 允许携带cookie
maxAge: 3600 # 每3600s进行一次拦截