上篇文章我们介绍了 API 网关的基本构建方式以及请求过滤,小伙伴们对 Zuul 的作用应该已经有了一个基本的认识,但是对于路由的配置我们只是做了一个简单的介绍,本文我们就来看看路由配置的其他一些细节。
本文是 Spring Cloud 系列的第二十篇文章,了解前十九篇文章内容有助于更好的理解本文:
1.使用 Spring Cloud 搭建服务注册中心
2.使用 Spring Cloud 搭建高可用服务注册中心
3.Spring Cloud 中服务的发现与消费
4.Eureka 中的核心概念
5.什么是客户端负载均衡
6.Spring RestTemplate 中几种常见的请求方式
7.RestTemplate 的逆袭之路,从发送请求到负载均衡
8.Spring Cloud 中负载均衡器概览
9.Spring Cloud 中的负载均衡策略
10.Spring Cloud 中的断路器 Hystrix
11.Spring Cloud 自定义 Hystrix 请求命令
12.Spring Cloud 中 Hystrix 的服务降级与异常处理
13.Spring Cloud 中 Hystrix 的请求缓存
14.Spring Cloud 中 Hystrix 的请求合并
15.Spring Cloud 中 Hystrix 仪表盘与 Turbine 集群监控
16.Spring Cloud 中声明式服务调用 Feign
17.Spring Cloud 中 Feign 的继承特性
18.Spring Cloud 中 Feign 配置详解
19.Spring Cloud 中的 API 网关服务 Zuul
首先我们来回忆一下上篇文章我们配置路由规则的那两行代码:
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=feign-consumer
我们说当我的访问地址符合/api-a/**规则的时候,会被自动定位到 feign-consumer 服务上去,不过两行代码有点麻烦,我们可以用下面一行代码来代替,如下:
zuul.routes.feign-consumer=/api-a/**
zuul.routes 后面跟着的是服务名,服务名后面跟着的是路径规则,这种配置方式显然更简单。
如果映射规则我们什么都不写,系统也给我们提供了一套默认的配置规则,默认的配置规则如下:
zuul.routes.feign-consumer.path=/feign-consumer/**
zuul.routes.feign-consumer.serviceId=feign-consumer
默认情况下,Eureka 上所有注册的服务都会被 Zuul 创建映射关系来进行路由,但是对于我这里的例子来说,我希望提供服务的是 feign-consumer,hello-service 作为服务提供者只对服务消费者提供服务,不对外提供服务,如果使用默认的路由规则,则 Zuul 也会自动为 hello-service 创建映射规则,这个时候我们可以采用如下方式来让 Zuul 跳过 hello-service 服务,不为其创建路由规则:
zuul.ignored-services=hello-service
有的小伙伴可能为有疑问,我们定义路由规则/api-a/**
的时候,为什么最后面是两个*,一个可不可以呢?当然可以,不过意义可就不一样了,Zuul 中的路由匹配规则使用了 Ant 风格定义,一共有三种不同的通配符:
通配符 | 含义 | 举例 | 解释 |
---|---|---|---|
? | 匹配任意单个字符 | /feign-consumer/? | 匹配/feign-consumer/a,/feign-consumer/b,/feign-consumer/c 等 |
* | 匹配任意数量的字符 | /feign-consumer/* | 匹配/feign-consumer/aaa,feign-consumer/bbb,/feign-consumer/ccc 等,无法匹配/feign-consumer/a/b/c |
** | 匹配任意数量的字符 | /feign-consumer/* | 匹配/feign-consumer/aaa,feign-consumer/bbb,/feign-consumer/ccc 等,也可以匹配/feign-consumer/a/b/c |
有的时候我们还会遇到这样一个问题,比如我有两个服务,一个叫做 feign-consumer,还有一个叫做 feign-consumer-hello,此时我的路由配置规则可能这样来写:
zuul.routes.feign-consumer.path=/feign-consumer/**
zuul.routes.feign-consumer.serviceId=feign-consumer
zuul.routes.feign-consumer-hello.path=/feign-consumer/hello/**
zuul.routes.feign-consumer-hello.serviceId=feign-consumer-hello
此时我访问 feign-consumer-hello 的路径会同时被这两条规则所匹配,Zuul 中的路径匹配方式是一种线性匹配方式,即按照路由匹配规则的存储顺序依次匹配,因此我们只需要确保 feign-consumer-hello 的匹配规则被先定义 feign-consumer 的匹配规则被后定义即可,但是在 properties 文件中我们不能保证这个先后顺序,此时我们需要用 YAML 来配置,这个时候我们可以删掉 resources 文件夹下的 application.properties,然后新建一个 application.yml,内容如下:
spring:
application:
name: api-gateway
server:
port: 2006
zuul:
routes:
feign-consumer-hello:
path: /feign-consumer/hello/**
serviceId: feign-consumer-hello
feign-consumer:
path: /feign-consumer/**
serviceId: feign-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka/
这个时候我们就可以确保先加载 feign-consumer-hello 的匹配规则,后加载 feign-consumer 的匹配规则。
上文我们说了一个 zuul.ignored-services=hello-service 属性可以忽略掉一个服务,不给某个服务设置映射规则,这个配置我们可以进一步细化,比如说我不想给/hello 接口路由,那我们可以按如下方式配置(后面我都用 yaml 配置):
zuul:
ignored-patterns: /**/hello/**
此时访问/hello 接口就会报 404 错误,同时我们也可以看到后台打印如下日志:
此外,我们也可以统一的为路由规则增加前缀,设置方式如下:
zuul:
prefix: /myapi
此时我们的访问路径就变成了http://localhost:2006/myapi/f…。
一般情况下 API 网关只是作为系统的统一入口,但是有的时候我们可能也需要在 API 网关上做一点业务逻辑操作,比如我现在在 api-gateway 项目中新建如下 Controller:
@RestController
public class HelloController {
@RequestMapping("/local")
public String hello() {
return "hello api gateway";
}
}
我希望用户在访问/local 时能够自动跳转到这个方法上来处理,那么此时我们需要用到 Zuul 的本地跳转,配置方式如下:
zuul:
prefix: /myapi
ignored-patterns: /**/hello/**
routes:
local:
path: /local/**
url: forward:/local
此时访问http://localhost:2006/myapi/l…结果如下:
我们在使用 Nginx 的时候,会涉及到一个请求头信息的配置,防止页面重定向后跳转到上游服务器上去,这个问题在 Zuul 中一样存在,假设我的 feign-consumer 中提供了一个接口/hello4,当访问/hello4 接口的时候,页面重定向到/hello,默认情况下,重定向的地址是具体的服务实例的地址,而不是 API 网关的跳转地址,这种做法会暴露真实的服务地址,所以需要在 Zuul 中配置,配置方式很简单,如下:
zuul:
add-host-header: true
表示 API 网关在进行请求路由转发前为请求设置 Host 头信息。
默认情况下,敏感的头信息无法经过 API 网关进行传递,我们可以通过如下配置使之可以传递:
zuul:
routes:
feign-consumer:
sensitiveHeaders:
在 Zuul 中,Ribbon 和 Hystrix 的配置还是和之前的配置方式一致,这里我就不赘述了,如果我们想关闭 Hystrix 重试机制,可以通过如下方式:
关闭全局重试机制:
zuul:
retryable: false
关闭某一个服务的重试机制:
zuul:
routes:
feign-consumer:
retryable: false
关于 Zuul 中路由的配置细节我们就说到这里,有问题欢迎留言讨论。