如果没有网关,客户端与每个微服务直接通讯,会造成以下的不便
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
org.springframework.cloud
spring-cloud-alibaba-dependencies
${com.alibaba.cloud.version}
pom
import
0.9.0.RELEASE
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-alibaba-nacos-discovery
server:
port: 8040
spring:
application:
name: gateway
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 118.31.11.178:8848
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 118.31.11.178:8848
gateway:
discovery:
locator:
# 让gateway通过服务发现组件找到其它的微服务
enabled: true
management:
endpoints:
web:
exposure:
# 暴露出所有actuator监控的端点
include: '*'
endpoint:
health:
show-details: always
由图可见,网关的两大核心,1.转发请求(路由,routes),2.过滤器
spring:
cloud:
gateway:
discovery:
locator:
# 开启 gateway 发现服务注册组件上其它微服务的自动路由
enabled: true
如果有多个路由,请求会被第一个符合条件的路由转发走
将 http://localhost:8040/baidu/search/error.html?chid=wx 路由转发到 https://www.baidu.com/search/error.html,不带chid=wx参数的不转发
spring:
cloud:
gateway:
routes:
- id: test_baidu_route
uri: https://www.baidu.com
predicates:
- Query=chid,wx
- Path=/baidu/**
filters:
- name: RewritePath
args:
regexp: /baidu/(?.*)
replacement: /${remaining}
这个配置表示的含义:
id
这个路由的唯一标识,随便定义
uri
转发的去处,就是要转发到哪里去
predicates,
这是断言,也叫谓词工厂,这里使用了两个谓词,被转发的url必须满足这两个条件
第一个是Query,Query=chid,wx表示,请求gateway的链接必须要有参数 chid=wx才会被转发
第一个是Path,Path=/baidu/**表示,请求gateway的链接必须是 http://localhost:8040/baidu/****************,gateway_url后面必须是/baidu/的才会被转发
内置的谓词工厂有11个,起到不同的作用,如果这11个内置的谓词不能满足你的需求,你也可以写一个谓词工厂。这里是官方文档:https://cloud.spring.io/spring-cloud-gateway/reference/html/#gateway-request-predicates-factories
filters
过滤器,这里只配置了一个过滤器RewritePath,如果不配置这个路径替换过滤器
http://localhost:8040/baidu/search/error.html?chid=wx被转发到 https://www.baidu.com/baidu/search/error.html,你会发现中间多了一个 /baidu/,这就不对了,所以这个过滤器就是把 /baidu/替换掉的逻辑,使最终的url是 https://www.baidu.com/search/error.html,一个可以正常访问的url
内置的过滤器工厂有二三十个,各种功能都有,同样你也可以自定义符合自己需求的过滤器。这里是官方文档:https://cloud.spring.io/spring-cloud-gateway/reference/html/#_rewritepath_gatewayfilter_factory
测试正常的转发
http://localhost:8040/baidu/search/error.html?chid=wx,没错,是baidu的返回
测试参数 chid=wx 不正确
http://localhost:8040/baidu/search/error.html?chid=wxx,说明predicate Query起作用了
测试不以 /baidu/ 开头
http://localhost:8040/search/error.html?chid=wx,说明 predicate Path 起作用了
官方文档链接:https://cloud.spring.io/spring-cloud-gateway/reference/html/#gateway-request-predicates-factories
spring:
cloud:
gateway:
routes:
- id: after_route
uri: lb://user-center
predicates:
# 当且仅当请求时的时间After配置的时间时,才会转发到用户微服务
# 目前配置不会进该路由配置,所以返回404
# 将时间改成 < now的时间,则访问localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- After=2030-01-20T17:42:47.789-07:00[America/Denver]
spring:
cloud:
gateway:
routes:
- id: before_route
uri: lb://user-center
predicates:
# 当且仅当请求时的时间Before配置的时间时,才会转发到用户微服务
# 目前配置不会进该路由配置,所以返回404
# 将时间改成 > now的时间,则访问localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Before=2018-01-20T17:42:47.789-07:00[America/Denver]
spring:
cloud:
gateway:
routes:
- id: between_route
uri: lb://user-center
predicates:
# 当且仅当请求时的时间Between配置的时间时,才会转发到用户微服务
# 因此,访问localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2027-01-21T17:42:47.789-07:00[America/Denver]
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: lb://user-center
predicates:
# 当且仅当带有名为somecookie,并且值符合正则ch.p的Cookie时,才会转发到用户微服务
# 如Cookie满足条件,则访问http://localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Cookie=somecookie, ch.p
spring:
cloud:
gateway:
routes:
- id: header_route
uri: lb://user-center
predicates:
# 当且仅当带有名为X-Request-Id,并且值符合正则\d+的Header时,才会转发到用户微服务
# 如Header满足条件,则访问http://localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Header=X-Request-Id, \d+
spring:
cloud:
gateway:
routes:
- id: host_route
uri: lb://user-center
predicates:
# 当且仅当名为Host的Header符合**.somehost.org或**.anotherhost.org时,才会转发用户微服务
# 如Host满足条件,则访问http://localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Host=**.somehost.org,**.anotherhost.org
spring:
cloud:
gateway:
routes:
- id: method_route
uri: lb://user-center
predicates:
# 当且仅当HTTP请求方法是GET时,才会转发用户微服务
# 如请求方法满足条件,访问http://localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Method=GET
spring:
cloud:
gateway:
routes:
- id: path_route
uri: lb://user-center
predicates:
# 当且仅当访问路径是/users/*或者/some-path/**,才会转发用户微服务
# segment是一个特殊的占位符,单层路径匹配
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Path=/users/{segment},/some-path/**
spring:
cloud:
gateway:
routes:
- id: query_route
uri: lb://user-center
predicates:
# 当且仅当请求带有baz的参数,才会转发到用户微服务
# eg. 访问http://localhost:8040/users/1?baz=xx -> user-center的/users/1
- Query=baz
示例2
spring:
cloud:
gateway:
routes:
- id: query_route
uri: lb://user-center
predicates:
# 当且仅当请求带有名为foo的参数,且参数值符合正则ba.,才会转发到用户微服务
# eg. 访问http://localhost:8040/users/1?baz=baz -> user-center的/users/1?baz=baz
- Query=foo, ba.
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: lb://user-center
predicates:
# 当且仅当请求IP是192.168.1.1/24网段,例如192.168.1.10,才会转发到用户微服务
# eg. 访问http://localhost:8040/users/1 -> user-center的/users/1
- RemoteAddr=192.168.1.1/24
组合使用
spring:
cloud:
gateway:
routes:
- id: host_foo_path_headers_to_httpbin
uri: http://ityouknow.com
predicates:
- Host=**.foo.org
- Path=/headers
- Method=GET
- Header=X-Request-Id, \d+
- Query=foo, ba.
- Query=baz
- Cookie=chocolate, ch.p
- After=2018-01-20T06:06:06+08:00[Asia/Shanghai]
相关代码
如果想知道具体配置的细则,看源码是最直接有效的方法
参照这些内置predicate的源码实现,照葫芦画瓢,代码并不多,写一个类就行
官方文档:https://cloud.spring.io/spring-cloud-gateway/reference/html/#_gatewayfilter_factories,参照文档配置使用就行
也可以看大目师兄的手记:https://www.imooc.com/article/290816
也是参照内置的filters去实现
上面提到的过滤器都是对单个路由起作用的gatewayFilter,全局过滤器是对所有路由都有用的过滤器globalFilter
经过我的测试,全局过滤器默认都是生效的,包括内置的全局过滤器和自定义的全局过滤器
官方文档:https://cloud.spring.io/spring-cloud-gateway/reference/html/#_global_filters
大目师兄手记:https://www.imooc.com/article/290821
我自定一个类 GlobalFilterConfiguration
package com.zengchen.gateway.configuration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Configuration
@Slf4j
public class GlobalFilterConfiguration {
/**
* 给每个请求增加个header requestHeader=111111
* @return
*/
@Bean
@Order(1)
public GlobalFilter addHeaderArPre() {
return (exchange, chain) -> {
log.info("do **pre** GlobalFilter addHeaderArPre");
ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate();
requestBuilder.header("requestHeader","111111");
ServerWebExchange newExchange = exchange.mutate().request(requestBuilder.build()).build();
return chain.filter(newExchange).then(Mono.fromRunnable(() -> {
log.info("do **post** GlobalFilter addHeaderArPre");
}));
};
}
/**
* 给每个请求返回的时候,增加header
* @return
*/
@Bean
@Order(2)
public GlobalFilter addHeaderAtPost() {
return (exchange, chain) -> {
log.info("do **pre** GlobalFilter addHeaderAtPost");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("do **post** GlobalFilter addHeaderAtPost");
exchange.getResponse().getHeaders().add("responseHeader","666666");
}));
};
}
}
这样就ok了,不用写任何配置,重启项目,直接就对所有的请求有效了
返回的responseHeader已经有了666666,至于请求的header有没有添加进去,可以在其它执行顺序排在它后面的过滤器里面打断点查看,我偷偷看过,有的
上面请求打的日志,addHeaderArPre排在addHeaderAtPost之前执行,因为addHeaderArPre的order是1,addHeaderAtPost的order是2,数值小的先执行。对于post,方法返回时候执行顺序又倒过来了,有点绕,执行顺序得看清楚了
对于gatewayFilter,写配置的时候,谁写在第一个,它的order就是1,后面的顺着排,2,3,4、、、、
创建项目的时候,我们已经添加了actuator监控组件
访问:http://localhost:8040/actuator/,最后一个就是gateway部分的入口,但是不能直接访问,我试一下404,下面介绍可以访问的端点
路径 | 描述 |
---|---|
globalfilters | GET 展示所有的全局过滤器 |
routefilters | GET 展示所有的 gatewayFilter |
routes | GET 展示路由列表 |
routes/{id} | GET 展示指定 id 的路由的信息 |
refresh | POST 清空路由缓存 |
routes/{id} | POST 新增一个路由,消息体如下 |
routes/{id} | DELETE 删除一个路由 |
新增路由消息体:
{
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/test"
}
}
],
"filters": [
{
"name": "AddRequestHeader",
"args": {
"_genkey_0": "X-Request-Foo",
"_genkey_1": "Bar"
}
},
{
"name": "PreLog",
"args": {
"_genkey_0": "a",
"_genkey_1": "b"
}
}
],
"uri": "https://www.itmuch.com",
"order": 0
}