网关的作用:由于项目采用微服务方式开发后,一个项目包含多个服务,每个服务的ip和端口可能都不相同,并且同一个服务可能也是集群部署,在这样具有多个服务的情况下,我们不可能让前端调用不同接口时写不同的ip和端口,甚至还有同一个服务集群间调用哪一个的问题,因此我们需要gateway作为统一我们服务的入口, 所有的请求都将发送给网关,然后由网关帮我们负责转发到对应的服务,并且实现负载均衡(因此我们需要将gateway注册到nacos中),同时我们还可以在网关做一些操作,比如身份认证啊,流量控制啊,数据脱敏啊,ip黑名单啊之类的功能。
gateway依赖
<!--引入gateway网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
需要注意的是,由于gateway是基于webflux模型构建,因此会与webMVC依赖冲突,我们需要保证gateway项目中不存在mvc的依赖,否则将会启动失败
***************************
APPLICATION FAILED TO START
***************************
Description:
Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway.
Action:
Please set spring.main.web-application-type=reactive or remove spring-boot-starter-web dependency.
gateway的使用有点类似于nginx,需要我们在项目中将路由规则之类的写好才会生效
方式一:配置文件配置(推荐)
该配置就是将localhost:8084/test1/**路由至localhost:8080/,将localhost:8084/test2/**路由至localhost:8081/,并且gateway会自动帮你拼接url,拼接规则为uri+predicate+实际地址,如localhost:8080/test1/hello
#端口设置
server:
port: 8084
#服务名设置
spring:
application:
name: gateway-test
#nacos服务中心的地址
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes: #gateway路由配置,为一个数组形式
- id: nacos-test-8080 #路由的唯一标识,保持唯一即可
uri: http://localhost:8080/ #需要将请求路由到的目标地址
predicates: #断言,跟java中的断言接口差不多,总之就是满足条件就放行,不满足就直接拦截
- Path=/test1/** #意思为地址匹配,只要请求是以test1开头(如localhost:8084/test1/hello),则将该请求路由到上面的uri中
- id: nacos-test-8081
uri: http://localhost:8081/
predicates:
- Path=/test2/**
为了满足匹配规则路由,我们需要两个服务有不同的url前缀
如服务一,请求地址为/test1/hello
服务二,请求地址为/test2/hello
我们用浏览器向网关发送请求,发现网关已经为我们实现了路由转发
但是这里很明显有一个问题,我们在路由匹配规则时已经将ip和端口写死,这意味着我们将无法实现对同一个服务的负载均衡,因此这个时候我们就需要用到nacos(或其他注册中心),我们将三个服务均注册至nacos中
此时我们再去修改网关的配置文件,修改其中的uri,形式为lb://nacos上的服务名,lb代表loadBlance,学过ribbon的因该很熟悉,代表负载均衡,这样设置uri那么网关将会去注册中心拉去指定的服务并实现负载均衡
由于nacos-test是同一个服务,此处我们把两个服务的接口请求地址都统一为/test1/hello
重启服务,我们再去浏览器访问gateway,多次访问发现gateway已经帮我们实现了轮询方式的负载均衡
断言和过滤器简单介绍
断言先于过滤器执行,断言先判断请求是否满足条件,若满足则由过滤器进行处理,请求和响应都将经过过滤器
常用断言介绍
- After=2020-07-21T11:33:33.993+08:00[Asia/Shanghai] 在指定日期后生效
- Before=2020-07-21T11:33:33.993+08:00[Asia/Shanghai] 在指定日期之前生效
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] 指定日期之间生效
- Cookie=key,value 请求中必须包含指定cookie,value支持正则表达式如
- Cookie=key,[A-Za-z0-9]+
- Header=key, value 请求中必须包含指定请求头键值对,value同样支持正则表达式
`curl http://localhost:8989/user/findAll -H "X-Request-Id:11"
- Method=GET,POST 指定请求方式
以上after,bafore,between断言支持的时间格式为ZonedDateTime,我们可以用ZonedDateTime.now()获取当前时间
使用方式实例
常用过滤器介绍
- AddRequestHeader=key,value 在请求头中增加指定键值对
- AddRequestParameter=key, value 在请求中增加指定参数,可用request.getAttribute("")取值
- AddResponseHeader=key, value 在响应头中增加指定键值对
- PrefixPath=/path 在请求路径中增加指定前缀
- StripPrefix=n(n为整数) 在请求路径中去掉n个前缀
使用示例
再次请求/test1/hello,可以发现响应头中增加了配置的键值对key,value
StripPrefix=n(n为整数) 这个过滤器可以帮我们实现服务间不需要特定的前缀也能区分路由到指定的服务
我这里指定路径包含/test1/则将其转发到nacos-test服务,然后在过滤器中指定去除一个前缀
此时我的nacos-test服务并无/test1前缀
这时我按照/test1/的方式/test1/hello去请求gateway却能准确的被路由到nacos-test服务中,这是因为断言先执行,过滤器后执行,我们请求地址初始为/test1/**满足断言条件,然后被路由至对应的服务中,也就是nacos-test服务中,然后过滤器帮我们去除了第一个前缀,那么请求就变成了/hello,刚好等于的我们的hello接口地址,因而被成功访问
因此这个过滤器可以帮我们实现不同服务见没有不同的统一前缀也能路由至对应服务
最后gateway也给了我们自由定制过滤器的功能,但是由于web模型是flux,所以书写方式并不和传统的webMvc的filter,举个简单例子,有兴趣的朋友可以下去深入学习
@Configuration
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("进入自定义filter");
if(exchange.getRequest().getQueryParams().get("username")!=null){
log.info("用户身份信息合法,放行");
return chain.filter(exchange);
}
log.info("非法用户,拒绝");
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1;
}
}