为什么引入API网关
使用 API 网关后的优点如下:
- 易于监控。可以在网关收集监控数据并将其推送到外部系统进行分析。
- 易于认证。可以在网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
- 减少了客户端与各个微服务之间的交互次数。
基本环境见 spring-cloud-gateway简介, 项目中 provider1的maven配置在此做下更正
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-cloud-learning</artifactId> <groupId>cn.idea360</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>idc-provider1</artifactId>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> </plugin> </plugins> </build> </project>
|
静态路由基本实现
静态路由比较简单,这里做简单实现。
更改路由模块application.yml配置,更改后的配置文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| server: port: 2000 spring: application: name: idc-gateway redis: host: localhost port: 6379 timeout: 6000ms jedis: pool: max-active: 1000 max-wait: -1ms max-idle: 10 min-idle: 5 cloud: consul: host: localhost port: 8500 gateway: discovery: locator: enabled: true routes: - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/** filters: - StripPrefix=1 - id: provider2 uri: lb://idc-provider2 predicates: - Path=/p2/** filters: - StripPrefix=1
|
官方关于 StripPrefix 得说明里有示例。
当 SpringBoot
服务配置了上下文 server.servlet.context-path
后, 如下配置则路径刚好正确
1 2 3 4 5 6 7 8 9 10 11 12
| spring: cloud: gateway: routes: - id: knowledge-base uri: http://idea360.cn/knowledge-base predicates: - Path=/knowledge-base/** - id: xxx-web uri: lb://xxx-web predicates: - Path=/xxx-web/**
|
xxx-web服务原始路径: 127.0.0.1:8080/xxx-web/version
网关路径: 127.0.0.1:2000/xxx-web/version
测试
执行以下请求测试路由1
1
| curl http://localhost:2000/p/provider1/1
|
结果 返回 2001
。说明成功路由到了 provider1 服务。
执行以下请求测试路由2
1
| curl http://localhost:2000/p2/provider2
|
返回 {"port":"2002"}
。说明成功路由到了 provider2 服务。
Predicate 断言条件
1 通过请求参数匹配
Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。
1 2 3 4 5 6 7 8
| ... - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/** - Query=username filters: - StripPrefix=1
|
这样配置,只要请求中包含 username 属性的参数即可匹配路由。否则404
测试
1
| curl http://localhost:2000/p/provider1/1?username=admin
|
经过测试发现只要请求汇总带有 username
参数即会匹配路由,不带 username
参数则不会匹配。
还可以将 Query 的值以键值对的方式进行配置,这样在请求过来时会对属性值和正则进行匹配,匹配上才会走路由。
1 2 3 4 5 6 7
| - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/** - Query=username, ad. filters: - StripPrefix=1
|
这样只要当请求中包含 username
属性并且参数值是以 admin 开头的长度为三位的字符串才会进行匹配和路由。
测试
1
| curl http://localhost:2000/p/provider1/1?username=adm
|
测试可以返回页面代码,将 username
的属性值改为 admin
再次访问就会报 404
,证明路由需要匹配正则表达式才会进行路由。
Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。
1 2 3 4 5 6 7
| - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/** - Header=token, \d+ filters: - StripPrefix=1
|
测试
1
| curl http://localhost:2000/p/provider1/1 -H "token:11"
|
则返回页面代码证明匹配成功。将参数-H "token:11"改为-H "token:spring"再次执行时返回404证明没有匹配。
3 通过 Cookie 匹配
Cookie Route Predicate 可以接收两个参数,一个是 Cookie name ,一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。
1 2 3 4 5 6 7
| - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/** - Cookie=sessionId, test filters: - StripPrefix=1
|
使用 curl 测试,命令行输入:
1
| curl http://localhost:2000/p/provider1/1 --cookie "sessionId=test"
|
则会返回页面代码,如果去掉–cookie “sessionId=test”,后台会报 404 错误。
4 通过 Host 匹配
Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。
1 2 3 4 5 6 7
| - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/** - Host=**.baidu.com filters: - StripPrefix=1
|
使用 curl 测试,命令行输入:
1 2
| curl http://localhost:2000/p/provider1/1 -H "Host: www.baidu.com" curl http://localhost:2000/p/provider1/1 -H "Host: md.baidu.com"
|
经测试以上两种 host 均可匹配到 host_route 路由,去掉 host 参数则会报 404 错误。
5 通过请求方式匹配
可以通过是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。
1 2 3 4 5 6 7
| - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/** - Method=GET filters: - StripPrefix=1
|
使用 curl 测试,命令行输入:
1
| curl http://localhost:2000/p/provider1/1
|
测试返回页面代码,证明匹配到路由,我们再以 POST
的方式请求测试。
1
| curl -X POST http://localhost:2000/p/provider1/1
|
返回 404 没有找到,证明没有匹配上路由
6 通过请求路径匹配
Path Route Predicate 接收一个匹配路径的参数来判断是否走路由。
1 2 3 4 5 6 7
| - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/{segment} - Method=POST filters: - StripPrefix=1
|
如果请求路径符合要求,则此路由将匹配,例如:/foo/1 或者 /foo/bar。
7 通过请求 ip 地址进行匹配
Predicate 也支持通过设置某个 ip 区间号段的请求才会路由,RemoteAddr Route Predicate 接受 cidr 符号(IPv4 或 IPv6 )字符串的列表(最小大小为1),例如 192.168.124.5/16 (其中 192.168.124.5 是 IP 地址,16 是子网掩码)。
1 2 3 4 5 6 7
| - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/** - RemoteAddr=192.168.124.5/16 filters: - StripPrefix=1
|
可以将此地址设置为本机的 ip 地址进行测试。
1
| curl http://192.168.124.5:2000/p/provider1/1
|
如果请求的远程地址是 192.168.124.5,则此路由将匹配。
8 组合使用
各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配。
一个请求满足多个路由的断言条件时,请求只会被首个成功匹配的路由转发
结语
本篇到此结束,欢迎大家关注公众号【当我遇上你】。